use crate::command::{build, push, BuildError, Capdu};
const KEY_TYPE_AES: u8 = 0x88;
pub struct KeyBlock<'a> {
pub key_type: u8, pub encrypted_key: &'a [u8],
pub kcv: [u8; 3],
pub clear_key_len: u8,
}
pub fn put_key(p1: u8, new_kvn: u8, blocks: &[KeyBlock<'_>; 3]) -> Result<Capdu, BuildError> {
let mut data = Capdu::new();
push(&mut data, &[new_kvn])?;
for block in blocks {
let enc_len = u8::try_from(block.encrypted_key.len()).map_err(|_| BuildError::Overflow)?;
if block.key_type == KEY_TYPE_AES {
let block_len = enc_len.checked_add(1).ok_or(BuildError::Overflow)?;
push(&mut data, &[block.key_type, block_len, block.clear_key_len])?;
} else {
push(&mut data, &[block.key_type, enc_len])?;
}
push(&mut data, block.encrypted_key)?;
push(&mut data, &[0x03])?;
push(&mut data, &block.kcv)?;
}
build(0x84, 0xD8, p1, 0x81, &data, true)
}
#[cfg(test)]
mod tests {
use super::*;
use scll_test_util::HexSlice;
fn aes_block(enc: &[u8], kcv: [u8; 3]) -> KeyBlock<'_> {
KeyBlock {
key_type: 0x88,
encrypted_key: enc,
kcv,
clear_key_len: u8::try_from(enc.len()).unwrap(),
}
}
fn aes_block_clear(enc: &[u8], clear_key_len: u8, kcv: [u8; 3]) -> KeyBlock<'_> {
KeyBlock {
key_type: 0x88,
encrypted_key: enc,
kcv,
clear_key_len,
}
}
#[test]
fn replace_three_aes_blocks_golden() {
let enc = [0xDE, 0xAD];
let blocks = [
aes_block(&enc, [0x01, 0x02, 0x03]),
aes_block(&enc, [0x01, 0x02, 0x03]),
aes_block(&enc, [0x01, 0x02, 0x03]),
];
let apdu = put_key(0x30, 0x30, &blocks).unwrap();
let block = [0x88, 0x03, 0x02, 0xDE, 0xAD, 0x03, 0x01, 0x02, 0x03];
let mut expected = heapless::Vec::<u8, 64>::new();
expected
.extend_from_slice(&[0x84, 0xD8, 0x30, 0x81, 0x1C, 0x30])
.unwrap();
for _ in 0..3 {
expected.extend_from_slice(&block).unwrap();
}
expected.extend_from_slice(&[0x00]).unwrap();
assert_eq!(HexSlice(&apdu), HexSlice(&expected));
}
#[test]
fn add_uses_p1_00_and_kvn_inside_data() {
let enc = [0x11; 16];
let blocks = [
aes_block(&enc, [0xAA, 0xBB, 0xCC]),
aes_block(&enc, [0xAA, 0xBB, 0xCC]),
aes_block(&enc, [0xAA, 0xBB, 0xCC]),
];
let apdu = put_key(0x00, 0x31, &blocks).unwrap();
assert_eq!(&apdu[0..4], &[0x84, 0xD8, 0x00, 0x81]); assert_eq!(apdu[5], 0x31); assert_eq!(apdu[6], 0x88); assert_eq!(apdu[7], 0x11); assert_eq!(apdu[8], 0x10); assert_eq!(apdu[25], 0x03); assert_eq!(&apdu[26..29], &[0xAA, 0xBB, 0xCC]);
assert_eq!(*apdu.last().unwrap(), 0x00); }
#[test]
fn aes192_and_aes256_clear_length_byte() {
let enc = [0x11u8; 32];
let b192 = [
aes_block_clear(&enc, 24, [1, 2, 3]),
aes_block_clear(&enc, 24, [1, 2, 3]),
aes_block_clear(&enc, 24, [1, 2, 3]),
];
let a192 = put_key(0x00, 0x31, &b192).unwrap();
assert_eq!(a192[6], 0x88); assert_eq!(a192[7], 0x21); assert_eq!(a192[8], 0x18);
let b256 = [
aes_block_clear(&enc, 32, [1, 2, 3]),
aes_block_clear(&enc, 32, [1, 2, 3]),
aes_block_clear(&enc, 32, [1, 2, 3]),
];
let a256 = put_key(0x00, 0x31, &b256).unwrap();
assert_eq!(a256[7], 0x21); assert_eq!(a256[8], 0x20); }
#[test]
fn oversized_key_value_overflows() {
let enc = [0x00u8; 255];
let blocks = [
aes_block(&enc, [0; 3]),
aes_block(&enc, [0; 3]),
aes_block(&enc, [0; 3]),
];
assert_eq!(put_key(0x30, 0x30, &blocks), Err(BuildError::Overflow));
}
}