use crate::{
Transport,
commands::SecureChannel,
crypto::originality::SIGNATURE_LEN,
crypto::suite::SessionSuite,
session::SessionError,
types::{ResponseCode, ResponseStatus},
};
pub(crate) async fn read_sig<T: Transport>(
transport: &mut T,
) -> Result<[u8; SIGNATURE_LEN], SessionError<T::Error>> {
let resp = transport
.transmit(&[0x90, 0x3C, 0x00, 0x00, 0x01, 0x00, 0x00])
.await?;
let code = ResponseCode::desfire(resp.sw1, resp.sw2);
if !matches!(
code.status(),
ResponseStatus::Unknown(0x9190) | ResponseStatus::OperationOk
) {
return Err(SessionError::ErrorResponse(code.status()));
}
let data = resp.data.as_ref();
data.try_into().map_err(|_| SessionError::UnexpectedLength {
got: data.len(),
expected: SIGNATURE_LEN,
})
}
const READ_SIG_CT_LEN: usize = 64;
pub(crate) async fn read_sig_mac<T: Transport, S: SessionSuite>(
transport: &mut T,
channel: &mut SecureChannel<'_, S>,
) -> Result<[u8; SIGNATURE_LEN], SessionError<T::Error>> {
let cmd_mac = channel.compute_cmd_mac(0x3C, &[0x00], &[]);
let mut apdu = [0u8; 5 + 1 + 8 + 1];
apdu[..5].copy_from_slice(&[0x90, 0x3C, 0x00, 0x00, 0x09]);
apdu[5] = 0x00; apdu[6..14].copy_from_slice(&cmd_mac);
let resp = transport.transmit(&apdu).await?;
let code = ResponseCode::desfire(resp.sw1, resp.sw2);
if !matches!(
code.status(),
ResponseStatus::OperationOk | ResponseStatus::Unknown(0x9190)
) {
return Err(SessionError::ErrorResponse(code.status()));
}
channel.decrypt_full_fixed::<READ_SIG_CT_LEN, SIGNATURE_LEN, T::Error>(
resp.sw2,
resp.data.as_ref(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::suite::{AesSuite, Direction};
use crate::session::Authenticated;
use crate::testing::{
Exchange, TestTransport, aes_key0_suite_085bc941, block_on, hex_array, hex_bytes,
lrp_key0_suite_bbe12900,
};
use alloc::vec::Vec;
fn encrypt_sig(suite_keys: (&[u8; 16], &[u8; 16]), ti: [u8; 4], sig: &[u8]) -> [u8; 64] {
let (enc_key, mac_key) = suite_keys;
let mut buf = [0u8; 64];
buf[..sig.len()].copy_from_slice(sig);
buf[sig.len()] = 0x80;
let mut suite = AesSuite::from_keys(*enc_key, *mac_key);
suite.encrypt(Direction::Response, &ti, 1, &mut buf);
buf
}
#[test]
fn read_sig_mac_roundtrip() {
let mac_key = hex_array("4C6626F5E72EA694202139295C7A7FC7");
let enc_key = hex_array("1309C877509E5A215007FF0ED19CA564");
let ti = [0x9D, 0x00, 0xC4, 0xDF];
let sig: Vec<u8> = (0..56u8).collect();
let suite = AesSuite::from_keys(enc_key, mac_key);
let cmd_mac = {
let mut input = Vec::new();
input.push(0x3C);
input.extend_from_slice(&0u16.to_le_bytes());
input.extend_from_slice(&ti);
input.push(0x00);
suite.mac(&input)
};
let ciphertext = encrypt_sig((&enc_key, &mac_key), ti, &sig);
let resp_mac = {
let mut input = Vec::new();
input.push(0x00);
input.extend_from_slice(&1u16.to_le_bytes());
input.extend_from_slice(&ti);
input.extend_from_slice(&ciphertext);
suite.mac(&input)
};
let mut expected_apdu = Vec::from([0x90, 0x3C, 0x00, 0x00, 0x09, 0x00]);
expected_apdu.extend_from_slice(&cmd_mac);
expected_apdu.push(0x00);
let mut resp_body = Vec::from(ciphertext);
resp_body.extend_from_slice(&resp_mac);
let mut transport =
TestTransport::new([Exchange::new(&expected_apdu, &resp_body, 0x91, 0x00)]);
let mut state = Authenticated::new(
AesSuite::from_keys(enc_key, mac_key),
ti,
crate::KeyNumber::Key0,
);
let out = block_on(async {
let mut ch = SecureChannel::new(&mut state);
read_sig_mac(&mut transport, &mut ch).await
})
.expect("authenticated Read_Sig must succeed");
assert_eq!(out.as_slice(), sig.as_slice());
assert_eq!(state.counter(), 1);
assert_eq!(transport.remaining(), 0);
}
#[test]
fn read_sig_mac_rejects_bad_trailer() {
let mac_key = hex_array("4C6626F5E72EA694202139295C7A7FC7");
let enc_key = hex_array("1309C877509E5A215007FF0ED19CA564");
let ti = [0x9D, 0x00, 0xC4, 0xDF];
let sig: Vec<u8> = (0..56u8).collect();
let suite = AesSuite::from_keys(enc_key, mac_key);
let cmd_mac = {
let mut input = Vec::new();
input.push(0x3C);
input.extend_from_slice(&0u16.to_le_bytes());
input.extend_from_slice(&ti);
input.push(0x00);
suite.mac(&input)
};
let ciphertext = encrypt_sig((&enc_key, &mac_key), ti, &sig);
let mut bad_mac = {
let mut input = Vec::new();
input.push(0x00);
input.extend_from_slice(&1u16.to_le_bytes());
input.extend_from_slice(&ti);
input.extend_from_slice(&ciphertext);
suite.mac(&input)
};
bad_mac[0] ^= 0x01;
let mut expected_apdu = Vec::from([0x90, 0x3C, 0x00, 0x00, 0x09, 0x00]);
expected_apdu.extend_from_slice(&cmd_mac);
expected_apdu.push(0x00);
let mut resp_body = Vec::from(ciphertext);
resp_body.extend_from_slice(&bad_mac);
let mut transport =
TestTransport::new([Exchange::new(&expected_apdu, &resp_body, 0x91, 0x00)]);
let mut state = Authenticated::new(
AesSuite::from_keys(enc_key, mac_key),
ti,
crate::KeyNumber::Key0,
);
let result = block_on(async {
let mut ch = SecureChannel::new(&mut state);
read_sig_mac(&mut transport, &mut ch).await
});
match result {
Err(SessionError::ResponseMacMismatch) => (),
other => panic!("expected ResponseMacMismatch, got {other:?}"),
}
assert_eq!(state.counter(), 0);
}
#[test]
fn read_sig_plain_hw_aes() {
let sig = hex_bytes(
"03F0A17889E3063D2D01CD8750734601BC031C26812705A1BD3B75361604B19B762DC285DCE303A5B6DE5F2814F0449BA64AB445A7AEC4CF",
);
let mut transport = TestTransport::new([Exchange::new(
&hex_bytes("903C0000010000"),
&sig,
0x91,
0x90,
)]);
let got = block_on(read_sig(&mut transport)).expect("hw plain Read_Sig must succeed");
assert_eq!(got.len(), 56);
assert_eq!(got.as_slice(), sig.as_slice());
assert_eq!(transport.remaining(), 0);
}
#[test]
fn read_sig_plain_hw_lrp() {
let sig = hex_bytes(
"5F019173DA747943318455D4DD9413858C8D335D4B488DC52A606386115C5C796CE79E95A499C430B6DD5D1CD41BF23F258C678070DBD42C",
);
let mut transport = TestTransport::new([Exchange::new(
&hex_bytes("903C0000010000"),
&sig,
0x91,
0x90,
)]);
let got = block_on(read_sig(&mut transport)).expect("hw plain Read_Sig must succeed");
assert_eq!(got.len(), 56);
assert_eq!(got.as_slice(), sig.as_slice());
assert_eq!(transport.remaining(), 0);
}
#[test]
fn read_sig_mac_hw_aes() {
let (suite, ti) = aes_key0_suite_085bc941();
let mut state = Authenticated::new(suite, ti, crate::KeyNumber::Key0);
state.advance_counter();
let mut transport = TestTransport::new([Exchange::new(
&hex_bytes("903C0000090032E6A647B984427F00"),
&hex_bytes(
"C2907EA2100DA5336DDDF17EE7AD70A240915DCD38E6319A663445D69E14825AF42F6AC725487F163ECC696B504F90390DF67BC5D8C0DBFCE2158FFB5A2A427AE1E8BDA4D293F528",
),
0x91,
0x90,
)]);
let sig = block_on(async {
let mut ch = SecureChannel::new(&mut state);
read_sig_mac(&mut transport, &mut ch).await
})
.expect("hw AES Read_Sig must succeed");
assert_eq!(sig.len(), 56);
assert_eq!(state.counter(), 2);
assert_eq!(transport.remaining(), 0);
}
#[test]
fn read_sig_mac_hw_lrp() {
let (suite, ti) = lrp_key0_suite_bbe12900();
let mut state = Authenticated::new(suite, ti, crate::KeyNumber::Key0);
state.advance_counter();
let mut transport = TestTransport::new([Exchange::new(
&hex_bytes("903C000009000C525883EFF7777400"),
&hex_bytes(
"E6DD25572D69A8D89C705AAC541BAD3D6DC5E50FCC8BE583E6487A07AB283F0A8CFD0A5097ACE24DB86C80C6D41A93C3FE1F1144D8E5D1873E1D50F10362E3F63766345847D34250",
),
0x91,
0x90,
)]);
let sig = block_on(async {
let mut ch = SecureChannel::new(&mut state);
read_sig_mac(&mut transport, &mut ch).await
})
.expect("hw LRP Read_Sig must succeed");
assert_eq!(sig.len(), 56);
assert_eq!(state.counter(), 2);
assert_eq!(transport.remaining(), 0);
}
}