Add test case for credential ID stability

This patch adds a test case that ensures that the calculated credential
ID for a credential that was created using the old (unstripped) format
is the same as the one generated originally.  Otherwise, the platform or
the RP could reject assertions because of a changed credential ID.
This commit is contained in:
Robin Krahl
2024-12-02 12:01:06 +01:00
parent c145a451ef
commit 86403fa9f2
2 changed files with 112 additions and 0 deletions
+1
View File
@@ -68,6 +68,7 @@ littlefs2 = "0.5.0"
log = "0.4.21"
p256 = { version = "0.13.2", features = ["ecdh"] }
rand = "0.8.4"
rand_chacha = "0.3"
sha2 = "0.10"
serde_test = "1.0.176"
trussed = { version = "0.1", features = ["virt"] }
+111
View File
@@ -601,9 +601,17 @@ impl From<&FullCredential> for StrippedCredential {
#[cfg(test)]
mod test {
use super::*;
use hex_literal::hex;
use littlefs2_core::path;
use rand::SeedableRng as _;
use rand_chacha::ChaCha8Rng;
use trussed::{
client::{Chacha8Poly1305, Sha256},
key::{Kind, Secrecy},
store::keystore::{ClientKeystore, Keystore as _},
types::Location,
virt::{self, Ram},
Platform as _,
};
fn credential_data() -> CredentialData {
@@ -630,6 +638,30 @@ mod test {
}
}
fn old_credential_data() -> CredentialData {
CredentialData {
rp: LocalPublicKeyCredentialRpEntity {
id: String::from("John Doe"),
name: None,
},
user: LocalPublicKeyCredentialUserEntity {
id: Bytes::from_slice(&[1, 2, 3]).unwrap(),
icon: None,
name: None,
display_name: None,
},
creation_time: 123,
use_counter: false,
algorithm: -7,
key: Key::WrappedKey(Bytes::from_slice(&[1, 2, 3]).unwrap()),
hmac_secret: Some(false),
cred_protect: None,
use_short_id: None,
large_blob_key: None,
third_party_payment: None,
}
}
fn random_byte_array<const N: usize>() -> ByteArray<N> {
use rand::{rngs::OsRng, RngCore};
let mut bytes = [0; N];
@@ -733,6 +765,85 @@ mod test {
assert_eq!(credential_data, deserialized);
}
#[test]
fn old_credential_id() {
// generated with v0.1.1-nitrokey.4 (NK3 firmware version v1.4.0)
const OLD_ID: &[u8] = &hex!("A300583A71AEF80C4DA56033D66EB3266E9ACB8D84923D13F89BCBCE9FF30D8CD77ED968A436CA3D39C49999EC0F69A289CB2A65A08ABF251DEB21BB4B56014C00000000000000000000000002504DF499ABDAE80F5615C870985B74A799");
const SERIALIZED_DATA: &[u8] = &hex!(
"A700A1626964684A6F686E20446F6501A16269644301020302187B03F404260582014301020306F4"
);
const SERIALIZED_CREDENTIAL: &[u8] = &hex!("A3000201A700A1626964684A6F686E20446F6501A16269644301020302187B03F404260582014301020306F4024C000000000000000000000000");
virt::with_platform(Ram::default(), |mut platform| {
let kek = [0; 44];
let client_id = path!("fido");
let kek = {
let rng = ChaCha8Rng::from_rng(platform.rng()).unwrap();
let mut keystore = ClientKeystore::new(client_id.into(), rng, platform.store());
keystore
.store_key(
Location::Internal,
Secrecy::Secret,
Kind::Symmetric32Nonce(12),
&kek,
)
.unwrap()
};
platform.run_client(client_id.as_str(), |mut client| {
let data = old_credential_data();
let rp_id_hash = syscall!(client.hash_sha256(data.rp.id.as_ref())).hash;
let credential_id = CredentialId(Bytes::from_slice(OLD_ID).unwrap());
let encrypted_serialized =
EncryptedSerializedCredential::try_from(credential_id).unwrap();
let serialized = syscall!(client.decrypt_chacha8poly1305(
kek,
&encrypted_serialized.0.ciphertext,
&rp_id_hash,
&encrypted_serialized.0.nonce,
&encrypted_serialized.0.tag,
))
.plaintext
.unwrap();
let full = FullCredential::deserialize(&serialized).unwrap();
assert_eq!(
full,
FullCredential {
ctap: CtapVersion::Fido21Pre,
data,
nonce: [0; 12].into(),
}
);
let stripped_credential = full.strip();
let serialized_data: Bytes<1024> =
trussed::cbor_serialize_bytes(&stripped_credential.data).unwrap();
assert_eq!(
delog::hexstr!(&serialized_data).to_string(),
delog::hexstr!(SERIALIZED_DATA).to_string()
);
let serialized_credential: Bytes<1024> =
trussed::cbor_serialize_bytes(&stripped_credential).unwrap();
assert_eq!(
delog::hexstr!(&serialized_credential).to_string(),
delog::hexstr!(SERIALIZED_CREDENTIAL).to_string()
);
let credential = Credential::Full(full);
let id = credential
.id(&mut client, kek, rp_id_hash.as_ref().try_into().unwrap())
.unwrap()
.0;
assert_eq!(
delog::hexstr!(&id).to_string(),
delog::hexstr!(OLD_ID).to_string()
);
});
});
}
#[test]
fn credential_ids() {
trussed::virt::with_ram_client("fido", |mut client| {