use std::collections::HashMap; use der::{Any, DerOrd, Encode, Reader, asn1::SetOfVec, oid::ObjectIdentifier}; use openssl::{ bn::{BigNum, BigNumContext}, ec::{EcGroup, EcKey, EcPoint, PointConversionForm}, nid::Nid, symm::{Cipher, Crypter, Mode}, }; use crate::{Card, Class, CommandChaining, OwnedCommandAPDU, SecureMessaging}; pub type SecurityInfos = SetOfVec; fn decrypt_unpadded( c: Cipher, key: &[u8], iv: Option<&[u8]>, data: &[u8], ) -> Result, openssl::error::ErrorStack> { let mut crypter = Crypter::new(c, Mode::Decrypt, key, iv)?; crypter.pad(false); let mut out = vec![0; data.len() + 64]; let count = crypter.update(data, &mut out)?; let rest = crypter.finalize(&mut out[count..])?; out.truncate(count + rest); Ok(out) } fn encrypt_unpadded( c: Cipher, key: &[u8], iv: Option<&[u8]>, data: &[u8], ) -> Result, openssl::error::ErrorStack> { let mut crypter = Crypter::new(c, Mode::Encrypt, key, iv)?; crypter.pad(false); let mut out = vec![0; data.len() + 64]; let count = crypter.update(data, &mut out)?; let rest = crypter.finalize(&mut out[count..])?; out.truncate(count + rest); Ok(out) } const BSI_DE: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.4.0.127.0.7"); const ID_PACE: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.4.0.127.0.7.2.2.4"); const ID_PACE_ECDH_GM: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.4.0.127.0.7.2.2.4.2"); const ID_PACE_ECDH_GM_AES_CBC_CMAC_256: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.4.0.127.0.7.2.2.4.2.4"); #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub enum SecurityInfoData { PACE { version: u64, parameter_id: Option, }, Other { required_data: Any, optional_data: Option, }, } impl DerOrd for SecurityInfoData { fn der_cmp(&self, other: &Self) -> der::Result { Ok(self.cmp(other)) } } #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub struct SecurityInfo { pub protocol: ObjectIdentifier, pub data: SecurityInfoData, } impl DerOrd for SecurityInfo { fn der_cmp(&self, other: &Self) -> der::Result { Ok(self.cmp(other)) } } pub struct EncryptedCardWrapper<'a, C: Card + 'a> { pub card: &'a mut C, pub counter: [u8; 16], pub creds: PACECredentials, } impl<'a, C: Card + Send + 'a> EncryptedCardWrapper<'a, C> { pub fn new(card: &'a mut C, creds: PACECredentials) -> Self { Self { card, creds, counter: [0; 16], } } fn tick_counter(&mut self) { for i in 0..16 { let j = 15 - i; if let Some(ok) = self.counter[j].checked_add(1) { self.counter[j] = ok; break; } else { self.counter[j] = 0; } } } } fn pad_vec(v: &mut Vec, to: usize) { v.push(0x80); while v.len() % to != 0 { v.push(0x00); } } impl<'a, C: Card + Send + 'a> Card for EncryptedCardWrapper<'a, C> { async fn transmit(&mut self, mut apdu: OwnedCommandAPDU) -> std::io::Result { if let Class::Standard { secure_messaging, .. } = &mut apdu.class { *secure_messaging = SecureMessaging::StandardHeaderAuthenticated; } self.tick_counter(); let mut header = vec![ apdu.class.encode().unwrap(), apdu.instruction, apdu.parameter[0], apdu.parameter[1], ]; pad_vec(&mut header, 16); let mut to_encrypt_data = apdu.command.clone(); pad_vec(&mut to_encrypt_data, 16); let iv = encrypt_unpadded( openssl::symm::Cipher::aes_256_cbc(), &self.creds.k_enc, Some(&[0; 16]), &self.counter, ) .unwrap(); let mut encrypted_data_do = encrypt_unpadded( Cipher::aes_256_cbc(), &self.creds.k_enc, Some(&iv), &to_encrypt_data, ) .unwrap(); encrypted_data_do.insert(0, 0x01); prepend_do(&mut encrypted_data_do, 0x87); let expected_length_do = if apdu.expected_length != Some(0) { let mut v = vec![apdu.expected_length.unwrap_or_default() as u8]; prepend_do(&mut v, 0x97); v } else { Vec::new() }; let mut mac_data = self.counter.to_vec(); mac_data.extend_from_slice(&header); mac_data.extend_from_slice(&encrypted_data_do); mac_data.extend_from_slice(&expected_length_do); pad_vec(&mut mac_data, 16); let cmac_key = openssl::pkey::PKey::cmac(&openssl::symm::Cipher::aes_256_cbc(), &self.creds.k_mac[..]) .unwrap(); let mut cmac_signer = openssl::sign::Signer::new_without_digest(&cmac_key).unwrap(); cmac_signer.update(&mac_data).unwrap(); let mut signature = cmac_signer.sign_to_vec().unwrap(); signature.truncate(8); let mut encoded_data = Vec::new(); encoded_data.extend_from_slice(&encrypted_data_do); encoded_data.extend_from_slice(&expected_length_do); append_do(&mut encoded_data, 0x8e, &signature); apdu.command = encoded_data; apdu.expected_length = None; let resp = self.card.transmit(apdu).await?; if resp.status == 0x6987 || resp.status == 0x6988 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "Secure messaging error.", )); } self.tick_counter(); if resp.data.len() < 8 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "Secure messaging error.", )); } let mac = &resp.data[resp.data.len() - 8..]; let mut data_to_mac = self.counter.to_vec(); data_to_mac.extend_from_slice(&resp.data[..resp.data.len() - 10]); pad_vec(&mut data_to_mac, 16); let cmac_key = openssl::pkey::PKey::cmac(&openssl::symm::Cipher::aes_256_cbc(), &self.creds.k_mac[..]) .unwrap(); let mut cmac_signer = openssl::sign::Signer::new_without_digest(&cmac_key).unwrap(); cmac_signer.update(&data_to_mac).unwrap(); let mut signature = cmac_signer.sign_to_vec().unwrap(); signature.truncate(8); if mac != signature { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "invalid APDU", )); } let mut rest = &resp.data[..]; let mut decrypted_data = Vec::new(); if rest[0] == 0x87 { let (length, skip) = if rest[1] < 0x80 { (rest[1] as usize, 2) } else { let count = rest[1] as usize - 0x80; let mut out = 0; for i in 0..count { out = (out << 8) | rest[2 + i] as usize; } (out, 2 + count) }; let encrypted_data = rest[skip + 1..skip + length].to_vec(); let iv = encrypt_unpadded( openssl::symm::Cipher::aes_256_cbc(), &self.creds.k_enc, Some(&[0; 16]), &self.counter, ) .unwrap(); decrypted_data = decrypt_unpadded( Cipher::aes_256_cbc(), &self.creds.k_enc, Some(&iv), &encrypted_data, ) .unwrap(); while decrypted_data.pop() != Some(0x80) {} rest = &rest[skip + length..]; } assert_eq!(rest[0], 0x99); assert_eq!(rest[1], 0x02); let new_sw1 = rest[2]; let new_sw2 = rest[3]; Ok(crate::ResultAPDU { data: decrypted_data, status: (new_sw1 as u16) << 8 | (new_sw2 as u16), }) } async fn transmit_raw(&mut self, apdu_buf: &[u8]) -> std::io::Result { self.card.transmit_raw(apdu_buf).await } } impl SecurityInfo { fn for_datas(oid: ObjectIdentifier, reqd: Any, opt: Option) -> der::Result { let data = if oid.parent().and_then(|f| f.parent()) == Some(ID_PACE) { SecurityInfoData::PACE { version: reqd.decode_as()?, parameter_id: if let Some(opt) = opt { Some(opt.decode_as()?) } else { None }, } } else { SecurityInfoData::Other { required_data: reqd, optional_data: opt, } }; Ok(Self { protocol: oid, data, }) } } impl<'a> der::Decode<'a> for SecurityInfo { fn decode>(decoder: &mut R) -> der::Result { decoder.sequence(|r| { let oid = r.decode::()?; let reqd = r.decode::()?; let opt = r.decode::>()?; SecurityInfo::for_datas(oid, reqd, opt) }) } } pub enum PasswordType { MRZ = 0x01, CAN = 0x02, PIN = 0x03, PUK = 0x04, } #[derive(Debug)] pub enum PACEStatus { Okay, Error(u16), TriesLeft(u8), PasswordSuspended, PasswordBlocked, } fn make_set_authentication_template_apdu( cryptographic_mechanism: ObjectIdentifier, password: PasswordType, ) -> OwnedCommandAPDU { let mut buf = Vec::new(); append_do(&mut buf, 0x80, cryptographic_mechanism.as_bytes()); append_do(&mut buf, 0x83, &[password as u8]); OwnedCommandAPDU { class: Class::Standard { command_chaining: crate::CommandChaining::LastOrOnly, secure_messaging: SecureMessaging::None, channel: 0, }, instruction: 0x22, parameter: [0xC1, 0xA4], command: buf, expected_length: Some(0), } } pub async fn set_authentication_template( card: &mut impl Card, cryptographic_mechanism: ObjectIdentifier, password: PasswordType, ) -> std::io::Result { let d = card .transmit(make_set_authentication_template_apdu( cryptographic_mechanism, password, )) .await?; Ok(match d.status { 0x9000 => PACEStatus::Okay, v if v & 0xFFF0 == 0x63C0 => PACEStatus::TriesLeft((v as u8) & 0xF), 0x63C1 => PACEStatus::PasswordSuspended, 0x63C0 => PACEStatus::PasswordBlocked, v => PACEStatus::Error(v), }) } pub async fn step_general_authenticate( card: &mut impl Card, chained: bool, make_data: impl FnOnce(&mut Vec), ) -> std::io::Result>> { let mut buf = Vec::new(); make_data(&mut buf); prepend_do(&mut buf, 0x7c); let bbuf = buf.clone(); let res = card .transmit(OwnedCommandAPDU { class: Class::Standard { command_chaining: if chained { CommandChaining::NotLast } else { CommandChaining::LastOrOnly }, secure_messaging: SecureMessaging::None, channel: 0, }, instruction: 0x86, parameter: [0x00, 0x00], command: buf, expected_length: None, }) .await?; if !res.data.starts_with(&[0x7c]) || res.status != 0x9000 { return Ok(HashMap::new()); } let mut b = &res.data[2..]; let mut out = HashMap::new(); while !b.is_empty() { let id = b[0]; let len = b[1] as usize; out.insert(id, b[2..2 + len].to_vec()); b = &b[2 + len..]; } Ok(out) } #[derive(Clone, Debug)] pub struct PACECredentials { pub k_mac: [u8; 32], pub k_enc: [u8; 32], pub card_ephemeral_key: Vec, } pub async fn authenticate_pin( card: &mut impl Card, pin: &[u8], cryptographic_mechanism: ObjectIdentifier, ) -> std::io::Result { // Step one: Get the encrypted nonce let mut data = step_general_authenticate(card, true, |_| {}).await?; let encrypted_nonce = data.remove(&0x80).unwrap(); let mut pin_padded = pin.to_vec(); pin_padded.extend_from_slice(&[0x00, 0x00, 0x00, 0x03]); let hashed_pin = openssl::sha::sha256(&pin_padded); let cipher = openssl::symm::Cipher::aes_256_cbc(); let decrypted_nonce = decrypt_unpadded(cipher, &hashed_pin, Some(&[0; 16]), &encrypted_nonce).unwrap(); let mut bn_ctx = BigNumContext::new().unwrap(); let main_group = EcGroup::from_curve_name(Nid::BRAINPOOL_P320R1).unwrap(); let host_ephemeral_key = EcKey::generate(&main_group).unwrap(); // Step two: provide mapping data to the card. // In generic mapping, this is an EC point. let host_ephemeral_key_bytes = host_ephemeral_key .public_key() .to_bytes(&main_group, PointConversionForm::UNCOMPRESSED, &mut bn_ctx) .unwrap(); let data = step_general_authenticate(card, true, |f| { append_do(f, 0x81, &host_ephemeral_key_bytes) }) .await?; let icc_public_key_point = EcPoint::from_bytes(&main_group, data.get(&0x82).unwrap(), &mut bn_ctx).unwrap(); let mut shared_secret = EcPoint::new(&main_group).unwrap(); shared_secret .mul( &main_group, &icc_public_key_point, host_ephemeral_key.private_key(), &bn_ctx, ) .unwrap(); let mut tmp = EcPoint::new(&main_group).unwrap(); let mut mapped_generator = EcPoint::new(&main_group).unwrap(); tmp.mul_generator( &main_group, &BigNum::from_slice(&decrypted_nonce[..]).unwrap(), &bn_ctx, ) .unwrap(); mapped_generator .add(&main_group, &tmp, &shared_secret, &mut bn_ctx) .unwrap(); let mut mapped_group = EcGroup::from_curve_name(Nid::BRAINPOOL_P320R1).unwrap(); let mut order = BigNum::new().unwrap(); mapped_group.order(&mut order, &mut bn_ctx).unwrap(); let mut cofactor = BigNum::new().unwrap(); mapped_group.cofactor(&mut cofactor, &mut bn_ctx).unwrap(); mapped_group .set_generator(mapped_generator, order, cofactor) .unwrap(); let host_ephemeral_mapped_key = EcKey::generate(&mapped_group).unwrap(); let host_ephemeral_mapped_key_bytes = host_ephemeral_mapped_key .public_key() .to_bytes( &mapped_group, PointConversionForm::UNCOMPRESSED, &mut bn_ctx, ) .unwrap(); let data = step_general_authenticate(card, true, |f| { append_do(f, 0x83, &host_ephemeral_mapped_key_bytes) }) .await?; let icc_ephemeral_mapped_key = EcPoint::from_bytes(&mapped_group, data.get(&0x84).unwrap(), &mut bn_ctx).unwrap(); let mut mapped_shared_secret = EcPoint::new(&mapped_group).unwrap(); mapped_shared_secret .mul( &mapped_group, &icc_ephemeral_mapped_key, host_ephemeral_mapped_key.private_key(), &mut bn_ctx, ) .unwrap(); let mut mapped_shared_secret_x = BigNum::new().unwrap(); let mut mapped_shared_secret_y = BigNum::new().unwrap(); mapped_shared_secret .affine_coordinates( &mapped_group, &mut mapped_shared_secret_x, &mut mapped_shared_secret_y, &mut bn_ctx, ) .unwrap(); let mut shared_secret_bytes = mapped_shared_secret_x.to_vec(); shared_secret_bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); let k_enc = openssl::sha::sha256(&shared_secret_bytes); shared_secret_bytes.pop(); shared_secret_bytes.push(0x02); let k_mac = openssl::sha::sha256(&shared_secret_bytes); let mut to_mac = Vec::new(); cryptographic_mechanism.encode_to_vec(&mut to_mac).unwrap(); append_do( &mut to_mac, 0x86, &icc_ephemeral_mapped_key .to_bytes( &mapped_group, PointConversionForm::UNCOMPRESSED, &mut bn_ctx, ) .unwrap(), ); prepend_do(&mut to_mac, 0x7F49); let cmac_key = openssl::pkey::PKey::cmac(&openssl::symm::Cipher::aes_256_cbc(), &k_mac[..]).unwrap(); let mut cmac_signer = openssl::sign::Signer::new_without_digest(&cmac_key).unwrap(); cmac_signer.update(&to_mac).unwrap(); let mut signature = cmac_signer.sign_to_vec().unwrap(); signature.truncate(8); let _ = step_general_authenticate(card, false, |f| append_do(f, 0x85, &signature)).await?; // TODO: verify card let mut icc_ephemeral_mapped_key_x = BigNum::new().unwrap(); let mut icc_ephemeral_mapped_key_y = BigNum::new().unwrap(); icc_ephemeral_mapped_key .affine_coordinates( &mapped_group, &mut icc_ephemeral_mapped_key_x, &mut icc_ephemeral_mapped_key_y, &mut bn_ctx, ) .unwrap(); Ok(PACECredentials { k_mac, k_enc, card_ephemeral_key: icc_ephemeral_mapped_key_x.to_vec(), }) } pub fn prepend_do(v: &mut Vec, val: u16) { let l = v.len() as u8; v.insert(0, l); if val < 0x100 { v.insert(0, val as u8); } else { v.insert(0, (val >> 8) as u8); v.insert(1, val as u8); } } pub fn append_do(v: &mut Vec, val: u16, d: &[u8]) { let l = d.len() as u8; if val < 0x100 { v.push(val as u8); } else { v.push((val >> 8) as u8); v.push(val as u8); } v.push(l); v.extend_from_slice(d); }