Crate emrtd

source ·
Expand description

A library that can read an eMRTD.

A library that can read an eMRTD (Electronic Machine Readable Travel Document).

The emrtd crate provides a simple API that can be used to communicate with eMRTDs and read the data that resides within them. With the help of openssl, it can perform Passive Authentication.

NOTE: Please note that this crate is provided ‘as is’ and is not considered production-ready. Use at your own risk.

Currently Active Authentication (AA), Chip Authentication (CA), PACE or EAC are not supported.

Enable the passive_auth feature for Passive Authentication (PA), but note that it depends on openssl crate.

§Quick Start

use emrtd::{bytes2hex, get_jpeg_from_ef_dg2, other_mrz, EmrtdComms, EmrtdError};
use tracing::{error, info};

#[cfg(feature = "passive_auth")]
use emrtd::{parse_master_list, passive_authentication, validate_dg};

fn main() -> Result<(), EmrtdError> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::TRACE)
        .init();

    let doc_no = "DOCUMENT NUMBER";
    let birthdate = "BIRTH DATE IN YYMMDD";
    let expirydate = "EXPIRY DATE IN YYMMDD";

    // Establish a PC/SC context.
    let ctx = match pcsc::Context::establish(pcsc::Scope::User) {
        Ok(ctx) => ctx,
        Err(err) => {
            error!("Failed to establish context: {err}");
            return Ok(());
        }
    };

    // List available readers.
    let mut readers_buf = [0; 2048];
    let mut readers = match ctx.list_readers(&mut readers_buf) {
        Ok(readers) => readers,
        Err(err) => {
            error!("Failed to list readers: {err}");
            return Ok(());
        }
    };

    // Use the first reader.
    let reader = match readers.next() {
        Some(reader) => reader,
        None => {
            error!("No readers are connected.");
            return Ok(());
        }
    };
    info!("Using reader: {reader:?}");

    // Connect to the card.
    let card = match ctx.connect(reader, pcsc::ShareMode::Shared, pcsc::Protocols::ANY) {
        Ok(card) => card,
        Err(pcsc::Error::NoSmartcard) => {
            error!("A smartcard is not present in the reader.");
            return Ok(());
        }
        Err(err) => {
            error!("Failed to connect to card: {err}");
            return Ok(());
        }
    };

    let mut sm_object = EmrtdComms::new(card);

    // Get the card's ATR.
    info!("ATR from attribute: {}", bytes2hex(&sm_object.get_atr()?));

    // Read EF.CardAccess
    sm_object.select_ef(b"\x01\x1C", "EF.CardAccess", false)?;
    let ef_cardacess = sm_object.read_data_from_ef(false)?;
    info!("Data from the EF.CardAccess: {}", bytes2hex(&ef_cardacess));

    // Read EF.DIR
    // let ef_dir = sm_object.read_data_from_ef(b"\x2F\x00", "EF.DIR");
    // info!("Data from the EF.DIR: {}", bytes2hex(&ef_dir));

    // Select eMRTD application
    sm_object.select_emrtd_application()?;

    let secret = match other_mrz(&doc_no, &birthdate, &expirydate) {
        Ok(secret) => secret,
        Err(EmrtdError) => {
            error!("Invalid MRZ string.");
            return Ok(());
        }
    };

    sm_object.establish_bac_session_keys(secret.as_bytes())?;

    // Read EF.COM
    sm_object.select_ef(b"\x01\x1E", "EF.COM", true)?;
    let ef_com = sm_object.read_data_from_ef(true)?;
    info!("Data from the EF.COM: {}", bytes2hex(&ef_com));

    // Read EF.SOD
    sm_object.select_ef(b"\x01\x1D", "EF.SOD", true)?;
    let ef_sod = sm_object.read_data_from_ef(true)?;
    info!("Data from the EF.SOD: {}", bytes2hex(&ef_sod));

    #[cfg(feature = "passive_auth")]
    {
        let master_list = include_bytes!("../data/DE_ML_2024-04-10-10-54-13.ml");
        let csca_cert_store = parse_master_list(master_list)?;
        let result = passive_authentication(&ef_sod, &csca_cert_store).unwrap();
        info!("{:?} {:?} {:?}", result.0.type_(), result.1, result.2);
    }

    // Read EF.DG1
    sm_object.select_ef(b"\x01\x01", "EF.DG1", true)?;
    let ef_dg1 = sm_object.read_data_from_ef(true)?;
    info!("Data from the EF.DG1: {}", bytes2hex(&ef_dg1));
    #[cfg(feature = "passive_auth")]
    validate_dg(&ef_dg1, 1, result.0, &result.1)?;

    // Read EF.DG2
    sm_object.select_ef(b"\x01\x02", "EF.DG2", true)?;
    let ef_dg2 = sm_object.read_data_from_ef(true)?;
    info!("Data from the EF.DG2: {}", bytes2hex(&ef_dg2));
    #[cfg(feature = "passive_auth")]
    validate_dg(&ef_dg2, 2, result.0, &result.1)?;

    let jpeg = get_jpeg_from_ef_dg2(&ef_dg2)?;
    std::fs::write("face.jpg", jpeg).expect("Error writing file");

    return Ok(());
}

Structs§

  • An Application Protocol Data Unit (APDU) used in smart card communication.

Enums§

Functions§

  • Helper function that converts a byte slice into a hex string.
  • Extracts the JPEG image from the EF.DG2.
  • Encodes the length field in ASN.1 format.
  • Manually calculates the MRZ (Machine Readable Zone) string for BAC (Basic Access Control).