Skip to content

Commit

Permalink
Extend serialization tests
Browse files Browse the repository at this point in the history
This patch adds de-/serialization tests for the modules ctap1,
ctap2::client_pin and ctap2::large_blobs.

See also: #54
  • Loading branch information
robin-nitrokey committed Jun 24, 2024
1 parent 3a6cc6c commit 0f39e0c
Show file tree
Hide file tree
Showing 4 changed files with 629 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ cosey = "0.3"
delog = "0.1"
heapless = { version = "0.7", default-features = false, features = ["serde"] }
heapless-bytes = "0.3"
iso7816 = "0.1"
iso7816 = "0.1.2"
serde = { version = "1", default-features = false, features = ["derive"] }
serde-indexed = "0.1.1"
serde_bytes = { version = "0.11.14", default-features = false }
serde_repr = "0.1"

[dev-dependencies]
hex-literal = "0.4.1"
serde_test = "1.0.176"

[features]
Expand Down
140 changes: 140 additions & 0 deletions src/ctap1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,143 @@ impl<A: Authenticator> crate::Rpc<Error, Request<'_>, Response> for A {
self.call_ctap1(request)
}
}

#[cfg(test)]
mod tests {
use super::*;
use heapless::Vec;
use hex_literal::hex;
use iso7816::command::{
class::Class, instruction::Instruction, Command, CommandBuilder, ExpectedLen,
};

// examples taken from:
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#examples

fn command(ins: u8, p1: u8, p2: u8, data: &[u8]) -> Command<1024> {
let builder = CommandBuilder::new(
Class::from_byte(0).unwrap(),
Instruction::from(ins),
p1,
p2,
data,
ExpectedLen::Max,
);
let mut apdu = Vec::<_, 1024>::new();
builder.serialize_into(&mut apdu).unwrap();
Command::try_from(&apdu).unwrap()
}

#[test]
fn test_register_request() {
let mut input = [0; 64];
// challenge
input[..32].copy_from_slice(&hex!(
"4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb"
));
// application
input[32..].copy_from_slice(&hex!(
"f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1c4"
));

let command = command(1, 0, 0, &input);
let request = Request::try_from(&command).unwrap();
let Request::Register(request) = request else {
panic!("expected register request, got: {:?}", request);
};
assert_eq!(request.challenge, &input[..32]);
assert_eq!(request.app_id, &input[32..]);
}

#[test]
fn test_register_response() {
let public_key = hex!("b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9");
let public_key = cosey::EcdhEsHkdf256PublicKey {
x: Bytes::from_slice(&public_key[..32]).unwrap(),
y: Bytes::from_slice(&public_key[32..]).unwrap(),
};
let key_handle = hex!("2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25");
let key_handle = Bytes::from_slice(&key_handle).unwrap();
let signature = hex!("304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871");
let signature = Bytes::from_slice(&signature).unwrap();
let attestation_certificate = hex!("3082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df");
let attestation_certificate = Bytes::from_slice(&attestation_certificate).unwrap();
let response = register::Response::new(
0x05,
&public_key,
key_handle,
signature,
attestation_certificate,
);
let mut output = Vec::<_, 1024>::new();
Response::Register(response).serialize(&mut output).unwrap();
assert_eq!(
output.as_slice(),
&hex!("0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871"),
);
}

#[test]
fn test_authenticate_request() {
let challenge = &hex!("ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57");
let application = &hex!("4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca");
let key_handle = &hex!("2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25");
let mut input = Vec::<_, 1024>::new();
input.extend_from_slice(challenge).unwrap();
input.extend_from_slice(application).unwrap();
input.push(u8::try_from(key_handle.len()).unwrap()).unwrap();
input.extend_from_slice(key_handle).unwrap();

let control_bytes = [
(0x07, ControlByte::CheckOnly),
(0x03, ControlByte::EnforceUserPresenceAndSign),
(0x08, ControlByte::DontEnforceUserPresenceAndSign),
];

for (byte, variant) in control_bytes {
let command = command(2, byte, 0, &input);
let request = Request::try_from(&command).unwrap();
let Request::Authenticate(request) = request else {
panic!("expected authenticate request, got: {:?}", request);
};
assert_eq!(request.control_byte, variant);
assert_eq!(request.challenge, challenge);
assert_eq!(request.app_id, application);
assert_eq!(request.key_handle, key_handle);
}
}

#[test]
fn test_authenticate_response() {
let signature = &hex!("304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f");
let signature = Bytes::from_slice(signature).unwrap();
let response = authenticate::Response {
user_presence: 1,
count: 1,
signature,
};
let mut output = Vec::<_, 1024>::new();
Response::Authenticate(response)
.serialize(&mut output)
.unwrap();
assert_eq!(
output.as_slice(),
&hex!("0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f"),
);
}

#[test]
fn test_version_request() {
let command = command(3, 0, 0, &[]);
let request = Request::try_from(&command).unwrap();
assert_eq!(request, Request::Version);
}

#[test]
fn test_version_response() {
let response = Response::Version(*b"U2F_V2");
let mut output = Vec::<_, 1024>::new();
response.serialize(&mut output).unwrap();
assert_eq!(output.as_slice(), b"U2F_V2");
}
}
Loading

0 comments on commit 0f39e0c

Please sign in to comment.