#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use aes::{
Aes128,
cipher::{Array, BlockCipherEncrypt, KeyInit},
};
use crate::crypto::ct_eq_8;
use crate::crypto::lrp::{Lrp, generate_plaintexts, generate_updated_keys};
use crate::crypto::suite::{cmac_aes, cmac_lrp, truncate_mac};
#[cfg(feature = "alloc")]
pub(super) type LrpSessionState = Box<Lrp>;
#[cfg(not(feature = "alloc"))]
pub(super) type LrpSessionState = Lrp;
pub(super) enum SdmKeys {
Aes {
enc_key: [u8; 16],
mac_key: [u8; 16],
},
Lrp {
enc: LrpSessionState,
mac: LrpSessionState,
},
}
impl SdmKeys {
pub(super) fn verify_mac(&self, data: &[u8], expected: &[u8; 8]) -> bool {
let computed = match self {
Self::Aes { mac_key, .. } => truncate_mac(&cmac_aes(mac_key, data)),
Self::Lrp { mac, .. } => truncate_mac(&cmac_lrp(Lrp::clone(mac), data)),
};
ct_eq_8(&computed, expected)
}
}
pub(super) fn derive_sdm_keys_aes(
sdm_file_read_key: &[u8; 16],
uid: Option<&[u8; 7]>,
sdm_read_ctr: Option<&[u8; 3]>,
) -> SdmKeys {
let build_sv = |label: [u8; 2]| -> [u8; 16] {
let mut sv = [0u8; 16];
sv[0..2].copy_from_slice(&label);
sv[2..6].copy_from_slice(&[0x00, 0x01, 0x00, 0x80]);
let mut off = 6;
if let Some(u) = uid {
sv[off..off + 7].copy_from_slice(u);
off += 7;
}
if let Some(c) = sdm_read_ctr {
sv[off..off + 3].copy_from_slice(c);
}
sv
};
SdmKeys::Aes {
enc_key: cmac_aes(sdm_file_read_key, &build_sv([0xC3, 0x3C])),
mac_key: cmac_aes(sdm_file_read_key, &build_sv([0x3C, 0xC3])),
}
}
pub(super) fn derive_sdm_keys_lrp(
sdm_file_read_key: &[u8; 16],
uid: Option<&[u8; 7]>,
sdm_read_ctr: Option<&[u8; 3]>,
) -> SdmKeys {
let mut sv = [0u8; 16];
sv[0..4].copy_from_slice(&[0x00, 0x01, 0x00, 0x80]);
let mut off = 4;
if let Some(u) = uid {
sv[off..off + 7].copy_from_slice(u);
off += 7;
}
if let Some(c) = sdm_read_ctr {
sv[off..off + 3].copy_from_slice(c);
}
sv[14..16].copy_from_slice(&[0x1E, 0xE1]);
let kx_lrp = Lrp::from_base_key(*sdm_file_read_key);
let master: [u8; 16] = cmac_lrp(kx_lrp, &sv);
let plaintexts = generate_plaintexts(master);
let [uk_mac, uk_enc] = generate_updated_keys::<2>(master);
SdmKeys::Lrp {
mac: make_lrp_session_state(Lrp::from_parts(plaintexts, uk_mac)),
enc: make_lrp_session_state(Lrp::from_parts(plaintexts, uk_enc)),
}
}
#[cfg(feature = "alloc")]
fn make_lrp_session_state(lrp: Lrp) -> LrpSessionState {
Box::new(lrp)
}
#[cfg(not(feature = "alloc"))]
fn make_lrp_session_state(lrp: Lrp) -> LrpSessionState {
lrp
}
pub(super) fn aes_ecb_encrypt_block(key: &[u8; 16], input: &[u8; 16]) -> [u8; 16] {
let cipher = Aes128::new(&Array::from(*key));
let mut out = Array::default();
cipher.encrypt_block_b2b(&Array::from(*input), &mut out);
out.into()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::suite::{aes_cbc_decrypt, aes_cbc_encrypt};
use crate::testing::hex_array;
#[test]
fn session_keys_an12196_t1() {
let key = hex_array::<16>("5ACE7E50AB65D5D51FD5BF5A16B8205B");
let uid = hex_array::<7>("04C767F2066180");
let ctr = hex_array::<3>("010000");
let keys = derive_sdm_keys_aes(&key, Some(&uid), Some(&ctr));
match keys {
SdmKeys::Aes { enc_key, mac_key } => {
assert_eq!(enc_key, hex_array("66DA61797E23DECA5D8ECA13BBADF7A9"));
assert_eq!(mac_key, hex_array("3A3E8110E05311F7A3FCF0D969BF2B48"));
}
_ => unreachable!(),
}
}
#[test]
fn enc_file_data_an12196_t3() {
let uid = hex_array::<7>("04958CAA5C5E80");
let ctr = hex_array::<3>("010000");
let keys = derive_sdm_keys_aes(&[0u8; 16], Some(&uid), Some(&ctr));
let enc_key = match &keys {
SdmKeys::Aes { enc_key, .. } => *enc_key,
_ => unreachable!(),
};
assert_eq!(enc_key, hex_array("8097D73344D53F963B09E23E03B62336"));
let mut iv_in = [0u8; 16];
iv_in[..3].copy_from_slice(&ctr);
let iv = aes_ecb_encrypt_block(&enc_key, &iv_in);
assert_eq!(iv, hex_array("7B3F3CFC39D3B7FF5868636E38AF7C3A"));
let mut ct = hex_array::<16>("94592FDE69FA06E8E3B6CA686A22842B");
aes_cbc_decrypt(&enc_key, &iv, &mut ct).unwrap();
assert_eq!(&ct, b"xxxxxxxxxxxxxxxx");
}
#[test]
fn mac_empty_an12196_t4() {
let uid = hex_array::<7>("04DE5F1EACC040");
let ctr = hex_array::<3>("3D0000");
let keys = derive_sdm_keys_aes(&[0u8; 16], Some(&uid), Some(&ctr));
let mac_key = match &keys {
SdmKeys::Aes { mac_key, .. } => *mac_key,
_ => unreachable!(),
};
assert_eq!(mac_key, hex_array("3FB5F6E3A807A03D5E3570ACE393776F"));
assert!(keys.verify_mac(b"", &hex_array("94EED9EE65337086")));
}
#[test]
fn mac_nonempty_an12196_t5() {
let uid = hex_array::<7>("04958CAA5C5E80");
let ctr = hex_array::<3>("080000");
let keys = derive_sdm_keys_aes(&[0u8; 16], Some(&uid), Some(&ctr));
let mac_key = match &keys {
SdmKeys::Aes { mac_key, .. } => *mac_key,
_ => unreachable!(),
};
assert_eq!(mac_key, hex_array("3ED0920E5E6A0320D823D5987FEAFBB1"));
assert!(keys.verify_mac(
b"CEE9A53E3E463EF1F459635736738962&cmac=",
&hex_array("ECC1E7F6C6C73BF6"),
));
}
#[test]
fn lrp_session_key_master_derivation() {
let key = [0u8; 16];
let uid = hex_array::<7>("042E1D222A6380");
let ctr = hex_array::<3>("6A0000");
use crate::crypto::lrp::Lrp;
use crate::crypto::suite::cmac_lrp;
let mut sv = [0u8; 16];
sv[0..4].copy_from_slice(&[0x00, 0x01, 0x00, 0x80]);
sv[4..11].copy_from_slice(&uid);
sv[11..14].copy_from_slice(&ctr);
sv[14..16].copy_from_slice(&[0x1E, 0xE1]);
let master = cmac_lrp(Lrp::from_base_key(key), &sv);
assert_eq!(master, hex_array("99C2FD9C885C2CA3C9089C20057310C0"));
let keys = derive_sdm_keys_lrp(&key, Some(&uid), Some(&ctr));
assert!(matches!(keys, SdmKeys::Lrp { .. }));
}
#[allow(unused_imports)]
use aes_cbc_encrypt as _;
}