#smartcard #electronic-id #epassport #eid

emrtd

A library that can read an eMRTD and do security checks

1 unstable release

0.0.1 Apr 17, 2024

#10 in #smartcard

Download history 167/week @ 2024-04-15

167 downloads per month

MIT/Apache

1MB
5K SLoC

OCaml 3K SLoC // 0.5% comments Rust 2K SLoC // 0.2% comments AsciiDoc 4 SLoC

Rust eMRTD

Introduction

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.

[WARNING]

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

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


lib.rs:

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.

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

Quick Start

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

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 = other_mrz(&doc_no, &birthdate, &expirydate)?;

    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));

    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));
    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));
    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(());
}

Dependencies

~11–16MB
~341K SLoC