Add a setter for the UUID in the builder

This commit is contained in:
Sosthène Guédon
2023-06-02 14:30:55 +02:00
parent 883d936034
commit 870ee4396a
10 changed files with 150 additions and 117 deletions
+12 -5
View File
@@ -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.
+5 -2
View File
@@ -10,7 +10,10 @@ use std::sync::Mutex;
static VSC_MUTEX: Mutex<()> = Mutex::new(());
pub fn with_vsc<F: FnOnce() -> 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<F: FnOnce() -> 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<F: FnOnce() -> 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() {
+46
View File
@@ -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"),
),
]
)
]
+40 -5
View File
@@ -191,6 +191,8 @@ impl TryFrom<u16> for Status {
struct IoTest {
name: String,
cmd_resp: Vec<IoCmd>,
#[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);
}
});
}
});
_ => {}
}
}
}
-26
View File
@@ -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));
});
}
-27
View File
@@ -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();
});
}
+21 -10
View File
@@ -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);
}
+22 -11
View File
@@ -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);
}
-29
View File
@@ -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();
});
}
+4 -2
View File
@@ -19,9 +19,11 @@ use trussed::virt::Ram;
pub type Piv = piv_authenticator::Authenticator<VirtClient<Ram>>;
pub fn piv<R>(test: impl FnOnce(&mut Piv) -> R) -> R {
pub const WITHOUT_UUID: Options = Options::new();
pub fn piv<R>(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)
})
}