1 unstable release
0.0.1 | Apr 17, 2024 |
---|
#10 in #smartcard
167 downloads per month
1MB
5K
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
- Apache License, Version 2.0 (link:LICENSE-APACHE[LICENSE-APACHE] or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (link:LICENSE-MIT[LICENSE-MIT] or http://opensource.org/licenses/MIT)
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