Skip to content

Commit

Permalink
Add tests and fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
sosthene-nitrokey committed Apr 27, 2023
1 parent 5949dbb commit cd495e4
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 22 deletions.
5 changes: 2 additions & 3 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ pub const DEFAULT_USER_PIN: &[u8] = b"123456";
/// Default value for PW3
pub const DEFAULT_ADMIN_PIN: &[u8] = b"12345678";

/// Maximum length for generic DOs, limited by the length in trussed `read_file` command.
pub const MAX_GENERIC_LENGTH: usize = MAX_MESSAGE_LENGTH;
pub const MAX_GENERIC_LENGTH: usize = 4096;
/// Big endian encoding of [MAX_GENERIC_LENGTH](MAX_GENERIC_LENGTH)
pub const MAX_GENERIC_LENGTH_BE: [u8; 2] = (MAX_GENERIC_LENGTH as u16).to_be_bytes();

Expand Down Expand Up @@ -1455,7 +1454,7 @@ impl ArbitraryDO {
})?;
stop_at_first = !first_data.data.is_full();
read = first_data.data.len();
expected_len = first_data.data.len();
expected_len = first_data.len;
reply.expand(&first_data.data)?;
}

Expand Down
15 changes: 9 additions & 6 deletions tests/card/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,20 @@ impl<T: opcard::Client + Send + Sync + 'static> Card<T> {

impl<T: opcard::Client + Send + Sync + 'static> CardBackend for Card<T> {
fn transaction(&mut self) -> Result<Box<dyn CardTransaction + Send + Sync + Sync>, Error> {
// TODO: use reference instead of cloning
Ok(Box::new(Transaction {
let mut transaction = Transaction {
card: self.0.clone(),
caps: None,
buffer: heapless::Vec::new(),
}))
};
CardTransaction::initialize(&mut transaction).unwrap();
Ok(Box::new(transaction))
}
}

#[derive(Debug)]
pub struct Transaction<T: opcard::Client + Send + Sync + 'static> {
card: Arc<Mutex<opcard::Card<T>>>,
caps: Option<CardCaps>,
buffer: heapless::Vec<u8, RESPONSE_LEN>,
}

Expand Down Expand Up @@ -99,12 +102,12 @@ impl<T: opcard::Client + Send + Sync + 'static> CardTransaction for Transaction<
Ok(response)
}

fn init_card_caps(&mut self, _caps: CardCaps) {
// TODO: implement
fn init_card_caps(&mut self, caps: CardCaps) {
self.caps = Some(caps);
}

fn card_caps(&self) -> Option<&CardCaps> {
None
self.caps.as_ref()
}

fn feature_pinpad_verify(&self) -> bool {
Expand Down
23 changes: 23 additions & 0 deletions tests/command-response.ron
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,27 @@
),
]
),
IoTest(
name: "Large certificate",
cmd_resp: [
Verify(pin: Pw3),
GetData(tag: CardHolderCertificate, expected_value: "", occurence: First),
GetData(tag: CardHolderCertificate, expected_value: "", occurence: Second),
GetData(tag: CardHolderCertificate, expected_value: "", occurence: Third),

PutLargeData(tag: CardHolderCertificate, start: 1, len: 2058, occurence: First),
GetLargeData(tag: CardHolderCertificate, start: 1, len: 2058, occurence: First),
GetData(tag: CardHolderCertificate, expected_value: "", occurence: Second),
GetData(tag: CardHolderCertificate, expected_value: "", occurence: Third),

PutLargeData(tag: CardHolderCertificate, start: 2, len: 2058, occurence: Second),
GetLargeData(tag: CardHolderCertificate, start: 2, len: 2058, occurence: Second),
GetLargeData(tag: CardHolderCertificate, start: 1, len: 2058, occurence: First),
GetData(tag: CardHolderCertificate, expected_value: "", occurence: Third),

PutLargeData(tag: CardHolderCertificate, start: 3, len: 2058, occurence: Third),
GetLargeData(tag: CardHolderCertificate, start: 3, len: 2058, occurence: Third),
GetLargeData(tag: CardHolderCertificate, start: 1, len: 2058, occurence: First),
]
),
]
161 changes: 157 additions & 4 deletions tests/command-response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ use hex_literal::hex;
use ron::{extensions::Extensions, Options};
use serde::Deserialize;

const LARGE_DATA: [u8; 10 * 1024] = {
let mut res = [0; 10 * 1024];
let mut i = 0;
while i < 10 * 1024 {
res[i] = i as u8;
i += 1;
}
res
};

#[derive(Deserialize, Debug, PartialEq, Clone, Copy, Default)]
#[repr(u8)]
enum Occurence {
#[default]
First = 0,
Second = 1,
Third = 2,
}
// iso7816::Status doesn't support serde
#[derive(Deserialize, Debug, PartialEq, Clone, Copy, Default)]
enum Status {
Expand Down Expand Up @@ -375,6 +393,45 @@ enum IoCmd {
PutData {
tag: DataObject,
value: String,
#[serde(default)]
occurence: Option<Occurence>,
/// None means default value
#[serde(default)]
expected_status: Status,
},
PutLargeData {
tag: DataObject,
start: u8,
len: usize,
#[serde(default)]
occurence: Option<Occurence>,
/// None means default value
#[serde(default)]
expected_status: Status,
},
GetData {
tag: DataObject,
expected_value: String,
#[serde(default)]
occurence: Option<Occurence>,
/// None means default value
#[serde(default)]
expected_status: Status,
},
GetLargeData {
tag: DataObject,
start: u8,
len: usize,
#[serde(default)]
occurence: Option<Occurence>,
/// None means default value
#[serde(default)]
expected_status: Status,
},
SelectData {
tag: DataObject,
#[serde(default)]
occurence: Occurence,
/// None means default value
#[serde(default)]
expected_status: Status,
Expand Down Expand Up @@ -466,8 +523,52 @@ impl IoCmd {
Self::PutData {
tag,
value,
occurence,
expected_status,
} => Self::run_put_data(*tag, occurence, &parse_hex(value), *expected_status, card),
Self::PutLargeData {
tag,
start,
len,
occurence,
expected_status,
} => Self::run_put_data(
*tag,
occurence,
&LARGE_DATA[*start as usize..][..*len],
*expected_status,
card,
),
Self::GetData {
tag,
expected_value,
occurence,
expected_status,
} => Self::run_put_data(*tag, &parse_hex(value), *expected_status, card),
} => Self::run_get_data(
*tag,
occurence,
&parse_hex(expected_value),
*expected_status,
card,
),
Self::GetLargeData {
tag,
start,
len,
occurence,
expected_status,
} => Self::run_get_data(
*tag,
occurence,
&LARGE_DATA[*start as usize..][..*len],
*expected_status,
card,
),
Self::SelectData {
tag,
occurence,
expected_status,
} => Self::run_select_data(*tag, *occurence, *expected_status, card),
Self::UnblockPin {
reset_code,
new_value,
Expand All @@ -483,8 +584,8 @@ impl IoCmd {
card: &mut opcard::Card<T>,
) {
println!("Command: {input:x?}");
let mut rep: heapless::Vec<u8, 1024> = heapless::Vec::new();
let cmd: iso7816::Command<1024> = iso7816::Command::try_from(input).unwrap_or_else(|err| {
let mut rep: heapless::Vec<u8, 7096> = heapless::Vec::new();
let cmd: iso7816::Command<7096> = iso7816::Command::try_from(input).unwrap_or_else(|err| {
panic!("Bad command: {err:?}, for command: {}", hex::encode(input))
});
let status: Status = card
Expand Down Expand Up @@ -538,16 +639,62 @@ impl IoCmd {

fn run_put_data<T: opcard::Client>(
data_object: DataObject,
occurence: &Option<Occurence>,
data: &[u8],
expected_status: Status,
card: &mut opcard::Card<T>,
) {
if let Some(occ) = occurence {
Self::run_select_data(data_object, *occ, Status::Success, card);
}

let [p1, p2] = (data_object as u16).to_be_bytes();

let input = build_command(0x00, 0xDA, p1, p2, data, 0);
Self::run_bytes(&input, &OutputMatcher::Len(0), expected_status, card)
}

fn run_get_data<T: opcard::Client>(
data_object: DataObject,
occurence: &Option<Occurence>,
expected_data: &[u8],
expected_status: Status,
card: &mut opcard::Card<T>,
) {
if let Some(occ) = occurence {
Self::run_select_data(data_object, *occ, Status::Success, card);
}

let [p1, p2] = (data_object as u16).to_be_bytes();

let input = build_command(0x00, 0xCA, p1, p2, &[], 0);
Self::run_bytes(
&input,
&OutputMatcher::Bytes(Cow::Owned(expected_data.to_owned())),
expected_status,
card,
)
}

fn run_select_data<T: opcard::Client>(
data_object: DataObject,
occurence: Occurence,
expected_status: Status,
card: &mut opcard::Card<T>,
) {
let [obj1, obj2] = (data_object as u16).to_be_bytes();

let mut data = Vec::new();
if obj1 == 0 {
data.extend_from_slice(&[0x60, 0x03, 0x5C, 0x01, obj2]);
} else {
data.extend_from_slice(&[0x60, 0x04, 0x5C, 0x02, obj1, obj2]);
}

let input = build_command(0x00, 0xA5, occurence as u8, 4, &data, 0);
Self::run_bytes(&input, &OutputMatcher::Len(0), expected_status, card)
}

fn run_import<T: opcard::Client>(
key: &str,
key_type: Option<KeyType>,
Expand All @@ -561,7 +708,13 @@ impl IoCmd {
template = vec![0x92];
template.extend_from_slice(&serialize_len(key.len()))
} else if key_kind.is_aes() {
return Self::run_put_data(DataObject::PSOEncDecKey, &key, expected_status, card);
return Self::run_put_data(
DataObject::PSOEncDecKey,
&None,
&key,
expected_status,
card,
);
} else {
todo!()
}
Expand Down
20 changes: 11 additions & 9 deletions tests/dos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,23 @@ fn get_data() {

for i in 0..3 {
tx.select_data(i, &[0x7f, 0x21], false).unwrap();
tx.set_cardholder_certificate(format!("{i}").into())
.unwrap();
tx.set_cardholder_certificate(vec![i; 4096]).unwrap();
}

tx.select_data(0, &[0x7f, 0x21], false).unwrap();
assert_eq!(tx.cardholder_certificate().unwrap(), "0".as_bytes());
assert_eq!(tx.next_cardholder_certificate().unwrap(), "1".as_bytes());
assert_eq!(tx.next_cardholder_certificate().unwrap(), "2".as_bytes());
assert_eq!(tx.cardholder_certificate().unwrap(), [0; 4096].as_slice());
assert_eq!(
tx.next_cardholder_certificate().unwrap(),
[1; 4096].as_slice()
);
assert_eq!(
tx.next_cardholder_certificate().unwrap(),
[2; 4096].as_slice()
);

for i in 0..3 {
tx.select_data(i, &[0x7f, 0x21], false).unwrap();
assert_eq!(
tx.cardholder_certificate().unwrap(),
format!("{i}").as_bytes()
);
assert_eq!(tx.cardholder_certificate().unwrap(), [i; 4096].as_slice());
}
});
}
Expand Down

0 comments on commit cd495e4

Please sign in to comment.