use crate::{
Transport,
commands::SecureChannel,
crypto::suite::SessionSuite,
session::SessionError,
types::{KeyNumber, NonMasterKeyNumber, ResponseCode, ResponseStatus},
};
fn crc32(data: &[u8]) -> [u8; 4] {
let mut crc: u32 = 0xFFFF_FFFF;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ 0xEDB8_8320
} else {
crc >> 1
};
}
}
crc.to_le_bytes()
}
pub(crate) async fn change_key<T: Transport, S: SessionSuite>(
transport: &mut T,
channel: &mut SecureChannel<'_, S>,
key_no: NonMasterKeyNumber,
new_key: &[u8; 16],
new_key_version: u8,
old_key: &[u8; 16],
) -> Result<(), SessionError<T::Error>> {
let mut plaintext = [0u8; 32];
for i in 0..16 {
plaintext[i] = new_key[i] ^ old_key[i];
}
plaintext[16] = new_key_version;
plaintext[17..21].copy_from_slice(&crc32(new_key));
plaintext[21] = 0x80;
transmit(transport, channel, KeyNumber::from(key_no), plaintext, true).await
}
pub(crate) async fn change_master_key<T: Transport, S: SessionSuite>(
transport: &mut T,
channel: &mut SecureChannel<'_, S>,
new_key: &[u8; 16],
new_key_version: u8,
) -> Result<(), SessionError<T::Error>> {
let mut plaintext = [0u8; 32];
plaintext[..16].copy_from_slice(new_key);
plaintext[16] = new_key_version;
plaintext[17] = 0x80;
transmit(transport, channel, KeyNumber::Key0, plaintext, false).await
}
async fn transmit<T: Transport, S: SessionSuite>(
transport: &mut T,
channel: &mut SecureChannel<'_, S>,
key_no: KeyNumber,
mut plaintext: [u8; 32],
expect_mact: bool,
) -> Result<(), SessionError<T::Error>> {
channel.encrypt_command(&mut plaintext)?;
let key_no_byte = key_no.as_byte();
let mac = channel.compute_cmd_mac(0xC4, &[key_no_byte], &plaintext);
let mut apdu = [0u8; 5 + 1 + 32 + 8 + 1];
apdu[..5].copy_from_slice(&[0x90, 0xC4, 0x00, 0x00, 0x29]);
apdu[5] = key_no_byte;
apdu[6..38].copy_from_slice(&plaintext);
apdu[38..46].copy_from_slice(&mac);
let resp = transport.transmit(&apdu).await?;
let code = ResponseCode::desfire(resp.sw1, resp.sw2);
if !matches!(code.status(), ResponseStatus::OperationOk) {
return Err(SessionError::ErrorResponse(code.status()));
}
let body = resp.data.as_ref();
if expect_mact {
channel.verify_response_mac_and_advance(resp.sw2, body)?;
} else {
if !body.is_empty() {
return Err(SessionError::UnexpectedLength {
got: body.len(),
expected: 0,
});
}
channel.advance_counter();
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::suite::AesSuite;
use crate::session::Authenticated;
use crate::testing::{Exchange, TestTransport, block_on, hex_array, hex_bytes};
fn authenticated_aes(
enc_key: [u8; 16],
mac_key: [u8; 16],
ti: [u8; 4],
cmd_counter: u16,
) -> Authenticated<AesSuite> {
let mut state = Authenticated::new(
AesSuite::from_keys(enc_key, mac_key),
ti,
crate::KeyNumber::Key0,
);
for _ in 0..cmd_counter {
state.advance_counter();
}
state
}
#[test]
fn change_key_case1_an12196_vector() {
let mac_key = hex_array("5529860B2FC5FB6154B7F28361D30BF9");
let enc_key = hex_array("4CF3CB41A22583A61E89B158D252FC53");
let ti = hex_array("7614281A");
let old_key = [0u8; 16];
let new_key = hex_array("F3847D627727ED3BC9C4CC050489B966");
let new_key_version: u8 = 0x01;
let expected_apdu = hex_bytes(
"90C4000029022CF362B7BF4311FF3BE1DAA295E8C68DE09050560D19B9E16C2393AE9CD1FAC75D0CE20BCD1D06E600",
);
let resp_body = hex_bytes("203BB55D1089D587");
let mut transport =
TestTransport::new([Exchange::new(&expected_apdu, &resp_body, 0x91, 0x00)]);
let mut state = authenticated_aes(enc_key, mac_key, ti, 2);
block_on(async {
let mut ch = SecureChannel::new(&mut state);
change_key(
&mut transport,
&mut ch,
NonMasterKeyNumber::Key2,
&new_key,
new_key_version,
&old_key,
)
.await
})
.expect("ChangeKey Case 1 must succeed");
assert_eq!(state.counter(), 3);
assert_eq!(transport.remaining(), 0);
}
#[test]
fn change_master_key_case2_an12196_vector() {
let mac_key = hex_array("5529860B2FC5FB6154B7F28361D30BF9");
let enc_key = hex_array("4CF3CB41A22583A61E89B158D252FC53");
let ti = hex_array("7614281A");
let new_key = hex_array("5004BF991F408672B1EF00F08F9E8647");
let new_key_version: u8 = 0x01;
let expected_apdu = hex_bytes(
"90C400002900C0EB4DEEFEDDF0B513A03A95A75491818580503190D4D05053FF75668A01D6FDA6610234BDED643200",
);
let mut transport = TestTransport::new([Exchange::new(&expected_apdu, &[], 0x91, 0x00)]);
let mut state = authenticated_aes(enc_key, mac_key, ti, 3);
block_on(async {
let mut ch = SecureChannel::new(&mut state);
change_master_key(&mut transport, &mut ch, &new_key, new_key_version).await
})
.expect("ChangeKey Case 2 must succeed");
assert_eq!(state.counter(), 4);
assert_eq!(transport.remaining(), 0);
}
#[test]
fn crc32_matches_an12196_case1_vector() {
let new_key = hex_array::<16>("F3847D627727ED3BC9C4CC050489B966");
assert_eq!(crc32(&new_key), hex_array("789DFADC"));
}
}