diff --git a/CHANGELOG.md b/CHANGELOG.md index 0491b1c..05af296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ SPDX-License-Identifier: CC0-1.0 # Changelog +## [v0.3.1][] (2023-06-02) + +- Add setter to the options builder for the UUID ([#32][]) + +[#32]: https://github.com/Nitrokey/piv-authenticator/pull/32 + ## [v0.3.0][] (2023-05-31) - Fix reset not checking that the key is locked ([#29][]) diff --git a/Cargo.toml b/Cargo.toml index 2991afa..e2d1461 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "piv-authenticator" -version = "0.3.0" +version = "0.3.1" authors = ["Nicolas Stalder ", "Nitrokey GmbH"] edition = "2021" license = "LGPL-3.0-only" diff --git a/src/lib.rs b/src/lib.rs index 4ec58ba..28e1507 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,12 @@ pub struct Options { impl Default for Options { fn default() -> Self { + Self::new() + } +} + +impl Options { + pub const fn new() -> Self { Self { storage: Location::External, label: NITROKEY_APPLICATION_LABEL, @@ -64,18 +70,19 @@ impl Default for Options { uuid: None, } } -} -impl Options { - pub fn storage(self, storage: Location) -> Self { + pub const fn storage(self, storage: Location) -> Self { Self { storage, ..self } } - pub fn url(self, url: &'static [u8]) -> Self { + pub const fn url(self, url: &'static [u8]) -> Self { Self { url, ..self } } - pub fn label(self, label: &'static [u8]) -> Self { + pub const fn label(self, label: &'static [u8]) -> Self { Self { label, ..self } } + pub const fn uuid(self, uuid: Option<[u8; 16]>) -> Self { + Self { uuid, ..self } + } } /// PIV authenticator Trussed app. diff --git a/tests/card/mod.rs b/tests/card/mod.rs index e206ad2..b72f6d2 100644 --- a/tests/card/mod.rs +++ b/tests/card/mod.rs @@ -10,7 +10,10 @@ use std::sync::Mutex; static VSC_MUTEX: Mutex<()> = Mutex::new(()); -pub fn with_vsc R, R>(f: F) -> R { +pub const WITH_UUID: Options = Options::new().uuid(Some([0; 16])); +pub const WITHOUT_UUID: Options = Options::new(); + +pub fn with_vsc R, R>(options: Options, f: F) -> R { let _lock = VSC_MUTEX.lock().unwrap(); let mut vpicc = vpicc::connect().expect("failed to connect to vpcd"); @@ -18,7 +21,7 @@ pub fn with_vsc R, R>(f: F) -> R { let (tx, rx) = mpsc::channel(); let handle = spawn(move |stopped| { with_ram_client("opcard", |client| { - let card = Authenticator::new(client, Options::default()); + let card = Authenticator::new(client, options); let mut vpicc_card = VpiccCard::new(card); let mut result = Ok(()); while !stopped.get() && result.is_ok() { diff --git a/tests/command_response.ron b/tests/command_response.ron index 1c61dec..993d79d 100644 --- a/tests/command_response.ron +++ b/tests/command_response.ron @@ -140,4 +140,50 @@ Reset(), ] ), + IoTest( + name: "UUID", + uuid_config: None, + cmd_resp: [ + GetData( + input: "5C 03 5FC102", + output: Len(61), + ), + AuthenticateManagement( + key: ( + algorithm: Tdes, + key: "0102030405060708 0102030405060708 0102030405060708" + ) + ), + PutData( + input: "5C 03 5FC102 53 3b 3019d4e739d821086c1084210d8360d8210842108421804210c3f3341000112233445566778899aabbccddeeff350839393939313233313e00fe00", + ), + GetData( + input: "5C 03 5FC102", + output: Data("53 3b 3019d4e739d821086c1084210d8360d8210842108421804210c3f3341000112233445566778899aabbccddeeff350839393939313233313e00fe00"), + ), + ] + ), + IoTest( + name: "With UUID", + uuid_config: WithUuid("00112233445566778899AABBCCDDEEFF"), + cmd_resp: [ + GetData( + input: "5C 03 5FC102", + output: Data("53 3b 3019d4e739d821086c1084210d8360d8210842108421804210c3f3341000112233445566778899aabbccddeeff350839393939313233313e00fe00"), + ), + AuthenticateManagement( + key: ( + algorithm: Tdes, + key: "0102030405060708 0102030405060708 0102030405060708" + ) + ), + PutData( + input: "5C 03 5FC102 53 3b 3019d4e739d821086c1084210d8360d8210842108421804210c3f33410B0BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB30839393939313233313e00fe00", + ), + GetData( + input: "5C 03 5FC102", + output: Data("53 3b 3019d4e739d821086c1084210d8360d8210842108421804210c3f33410B0BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB30839393939313233313e00fe00"), + ), + ] + ) ] diff --git a/tests/command_response.rs b/tests/command_response.rs index f3ff73c..16b5f30 100644 --- a/tests/command_response.rs +++ b/tests/command_response.rs @@ -191,6 +191,8 @@ impl TryFrom for Status { struct IoTest { name: String, cmd_resp: Vec, + #[serde(default)] + uuid_config: UuidConfig, } #[derive(Debug, Clone, Deserialize)] @@ -394,7 +396,11 @@ impl IoCmd { .map(Into::into) .unwrap_or_default(); - println!("Output: {:?}\nStatus: {status:?}", hex::encode(&rep)); + println!( + "Output({}): {:?}\nStatus: {status:?}", + rep.len(), + hex::encode(&rep) + ); if !output.validate(&rep) { panic!("Bad output. Expected {output:02x?}"); @@ -532,6 +538,19 @@ impl IoCmd { } } +#[derive(Deserialize, Debug, PartialEq, Clone)] +enum UuidConfig { + None, + WithUuid(String), + WithBoth(String), +} + +impl Default for UuidConfig { + fn default() -> Self { + Self::WithBoth("00".repeat(16)) + } +} + #[test_log::test] fn command_response() { let data = std::fs::read_to_string("tests/command_response.ron").unwrap(); @@ -539,10 +558,26 @@ fn command_response() { for t in tests { println!("\n\n===========================================================",); println!("Running {}", t.name); - setup::piv(|card| { - for io in t.cmd_resp { - io.run(card); + if matches!(t.uuid_config, UuidConfig::None | UuidConfig::WithBoth(_)) { + println!("Running {} without uuid", t.name); + setup::piv(setup::WITHOUT_UUID, |card| { + for io in &t.cmd_resp { + io.run(card); + } + }); + } + match t.uuid_config { + UuidConfig::WithUuid(uuid) | UuidConfig::WithBoth(uuid) => { + println!("Running {} with uuid {uuid:?}", t.name); + let uuid = (&*parse_hex(&uuid)).try_into().unwrap(); + + setup::piv(piv_authenticator::Options::new().uuid(Some(uuid)), |card| { + for io in &t.cmd_resp { + io.run(card); + } + }); } - }); + _ => {} + } } } diff --git a/tests/generate_asymmetric_keypair.rs b/tests/generate_asymmetric_keypair.rs deleted file mode 100644 index 03b244c..0000000 --- a/tests/generate_asymmetric_keypair.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2022 Nicolas Stalder AND Nitrokey GmbH -// SPDX-License-Identifier: LGPL-3.0-only - -mod setup; - -// example: 00 47 00 9A 0B -// AC 09 -// # P256 -// 80 01 11 -// # 0xAA = Yubico extension (of course...), PinPolicy, 0x2 = -// AA 01 02 -// # 0xAB = Yubico extension (of course...), TouchPolicy, 0x2 = -// AB 01 02 - -#[test_log::test] -fn gen_keypair() { - let _cmd = cmd!("00 47 00 9A 0B AC 09 80 01 11 AA 01 02 AB 01 02"); - - // without PIN, no key generation - setup::piv(|_piv| { - // not currently implemented - // - // let mut response = iso7816::Data::<16>::default(); - // assert_eq!(Err(SecurityStatusNotSatisfied), piv.respond(&cmd, &mut response)); - }); -} diff --git a/tests/get_data.rs b/tests/get_data.rs deleted file mode 100644 index fac73fc..0000000 --- a/tests/get_data.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2022 Nicolas Stalder AND Nitrokey GmbH -// SPDX-License-Identifier: LGPL-3.0-only - -mod setup; - -// use delog::hex_str; -// use iso7816::Status::*; - -#[test_log::test] -fn get_data() { - // let cmd = cmd!("00 47 00 9A 0B AC 09 80 01 11 AA 01 02 AB 01 02"); - // let cmd = cmd!("00 47 00 9A 0B AC 09 80 01 11 AA 01 02 AB 01 02"); - - // let cmd = cmd!("00 f8 00 00"); - // // without PIN, no key generation - setup::piv(|_piv| { - - // ykGetSerial - // println!("{}", hex_str!(&piv.respond(&cmd!("00 f8 00 00")).unwrap())); - // panic!(); - // let mut response = iso7816::Data::<16>::default(); - // piv.respond(&cmd!("00 f8 00 00"), &mut response).unwrap(); - // // assert_eq!([].as_ref(), piv.respond(&cmd!("00 f8 00 00")).unwrap()); - // // ykGetVersion - // piv.respond(&cmd!("00 fd 00 00"), &mut response).unwrap(); - }); -} diff --git a/tests/opensc.rs b/tests/opensc.rs index b065a00..1105c9d 100644 --- a/tests/opensc.rs +++ b/tests/opensc.rs @@ -7,25 +7,27 @@ mod card; use std::process::Command; -use card::with_vsc; +use card::{with_vsc, WITHOUT_UUID, WITH_UUID}; use expectrl::{spawn, Eof, WaitStatus}; #[test_log::test] fn list() { - with_vsc(|| { + let test = || { let mut p = spawn("piv-tool -n").unwrap(); p.expect("Using reader with a card: Virtual PCD 00 00") .unwrap(); p.expect("Personal Identity Verification Card").unwrap(); p.expect(Eof).unwrap(); assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } #[test_log::test] fn admin_mutual() { - with_vsc(|| { + let test = || { let mut command = Command::new("piv-tool"); command .env("PIV_EXT_AUTH_KEY", "tests/default_admin_key") @@ -36,14 +38,16 @@ fn admin_mutual() { // p.expect("Personal Identity Verification Card").unwrap(); p.expect(Eof).unwrap(); assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } /// Fails because of https://github.com/OpenSC/OpenSC/issues/2658 #[test_log::test] #[ignore] fn admin_card() { - with_vsc(|| { + let test = || { let mut command = Command::new("piv-tool"); command .env("PIV_EXT_AUTH_KEY", "tests/default_admin_key") @@ -54,12 +58,14 @@ fn admin_card() { p.expect("Personal Identity Verification Card").unwrap(); p.expect(Eof).unwrap(); assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } #[test_log::test] fn generate_key() { - // with_vsc(|| { + // let test = || { // let mut command = Command::new("piv-tool"); // command // .env("PIV_EXT_AUTH_KEY", "tests/default_admin_key") @@ -71,7 +77,10 @@ fn generate_key() { // // Non zero exit code? // assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 1)); // }); - // with_vsc(|| { + // with_vsc(WITH_UUID, test); + // with_vsc(WITHOUT_UUID, test); + + // let test = || { // let mut command = Command::new("piv-tool"); // command // .env("PIV_EXT_AUTH_KEY", "tests/default_admin_key") @@ -82,5 +91,7 @@ fn generate_key() { // p.expect(Eof).unwrap(); // // Non zero exit code? // assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 1)); - // }); + // }; + // with_vsc(WITH_UUID, test); + // with_vsc(WITHOUT_UUID, test); } diff --git a/tests/pivy.rs b/tests/pivy.rs index e4d3c3f..f81a9e5 100644 --- a/tests/pivy.rs +++ b/tests/pivy.rs @@ -5,7 +5,7 @@ mod card; -use card::with_vsc; +use card::{with_vsc, WITHOUT_UUID, WITH_UUID}; use expectrl::{spawn, Eof, Regex, WaitStatus}; @@ -14,7 +14,7 @@ use std::process::{Command, Stdio}; #[test_log::test] fn list() { - with_vsc(|| { + let test = || { let mut p = spawn("pivy-tool list").unwrap(); p.expect(Regex("card: [0-9A-Z]*")).unwrap(); p.expect("device: Virtual PCD 00 00").unwrap(); @@ -24,12 +24,14 @@ fn list() { .unwrap(); p.expect(Eof).unwrap(); assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } #[test_log::test] fn generate() { - with_vsc(|| { + let test = || { let mut p = spawn("pivy-tool -A 3des -K 010203040506070801020304050607080102030405060708 generate 9A -a eccp256 -P 123456").unwrap(); p.expect(Regex( "ecdsa-sha2-nistp256 (?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)? PIV_slot_9A@[A-F0-9]{20}", @@ -37,8 +39,11 @@ fn generate() { .unwrap(); p.expect(Eof).unwrap(); assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 0)); - }); - with_vsc(|| { + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); + + let test = || { let mut p = spawn("pivy-tool -A 3des -K 010203040506070801020304050607080102030405060708 generate 9A -a rsa2048 -P 123456").unwrap(); p.expect(Regex( "ssh-rsa (?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)? PIV_slot_9A@[A-F0-9]{20}", @@ -46,12 +51,14 @@ fn generate() { .unwrap(); p.expect(Eof).unwrap(); assert_eq!(p.wait().unwrap(), WaitStatus::Exited(p.pid(), 0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } #[test_log::test] fn ecdh() { - with_vsc(|| { + let test = || { let mut p = spawn("pivy-tool -A 3des -K 010203040506070801020304050607080102030405060708 generate 9A -a eccp256 -P 123456").unwrap(); p.expect(Regex( "ecdsa-sha2-nistp256 (?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)? PIV_slot_9A@[A-F0-9]{20}", @@ -74,7 +81,9 @@ fn ecdh() { drop(stdin); assert_eq!(p.wait().unwrap().code(), Some(0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } const LARGE_CERT: &str = "-----BEGIN CERTIFICATE----- @@ -122,7 +131,7 @@ N4vF6RP8Ck9wj1OYq/w82MkgxOPleUju4Q== #[test_log::test] fn large_cert() { - with_vsc(|| { + let test = || { let mut p = Command::new("pivy-tool") .args(["write-cert", "9A"]) .stdin(Stdio::piped()) @@ -145,5 +154,7 @@ fn large_cert() { stdout.read_to_string(&mut buf).unwrap(); assert_eq!(&buf, LARGE_CERT); assert_eq!(p.wait().unwrap().code(), Some(0)); - }); + }; + with_vsc(WITH_UUID, test); + with_vsc(WITHOUT_UUID, test); } diff --git a/tests/put_data.rs b/tests/put_data.rs deleted file mode 100644 index e498aed..0000000 --- a/tests/put_data.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2022 Nicolas Stalder AND Nitrokey GmbH -// SPDX-License-Identifier: LGPL-3.0-only - -mod setup; - -// use apdu_dispatch::dispatch::Interface::Contact; -// use apdu_dispatch::app::App as _; -// use hex_literal::hex; -// use iso7816::Command; - -// # PutData -// 00 DB 3F FF 23 -// # data object: 5FC109 -// 5C 03 5F C1 09 -// # data: -// 53 1C -// # actual data -// 88 1A 89 18 AA 81 D5 48 A5 EC 26 01 60 BA 06 F6 EC 3B B6 05 00 2E B6 3D 4B 28 7F 86 - -#[test_log::test] -fn put_data() { - setup::piv(|_piv| { - - // let mut response = iso7816::Data::<16>::default(); - // piv.respond(&cmd!( - // "00 DB 3F FF 23 5C 03 5F C1 09 53 1C 88 1A 89 18 AA 81 D5 48 A5 EC 26 01 60 BA 06 F6 EC 3B B6 05 00 2E B6 3D 4B 28 7F 86" - // ), &mut response).unwrap(); - }); -} diff --git a/tests/setup/mod.rs b/tests/setup/mod.rs index 3321e63..060d5e9 100644 --- a/tests/setup/mod.rs +++ b/tests/setup/mod.rs @@ -19,9 +19,11 @@ use trussed::virt::Ram; pub type Piv = piv_authenticator::Authenticator>; -pub fn piv(test: impl FnOnce(&mut Piv) -> R) -> R { +pub const WITHOUT_UUID: Options = Options::new(); + +pub fn piv(options: Options, test: impl FnOnce(&mut Piv) -> R) -> R { with_ram_client("test", |client| { - let mut piv_app = Authenticator::new(client, Options::default()); + let mut piv_app = Authenticator::new(client, options); test(&mut piv_app) }) }