use super::*;
use ciborium::value::Value;
use ring::digest;
fn create_basic_auth_data() -> Vec<u8> {
let mut auth_data = Vec::new();
let rp_id_hash = digest::digest(&digest::SHA256, "example.com".as_bytes());
auth_data.extend_from_slice(rp_id_hash.as_ref());
auth_data.push(0x01 | 0x04 | 0x40);
auth_data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
auth_data
}
fn create_auth_data_with_aaguid(aaguid: &[u8; 16]) -> Vec<u8> {
let mut auth_data = create_basic_auth_data();
auth_data.extend_from_slice(aaguid);
auth_data.extend_from_slice(&[0x00, 0x10]); auth_data.extend_from_slice(&[0x02; 16]);
let public_key_entries = vec![
(Value::Integer(1i64.into()), Value::Integer(2i64.into())), (Value::Integer(3i64.into()), Value::Integer((-7i64).into())), (Value::Integer((-1i64).into()), Value::Integer(1i64.into())), (Value::Integer((-2i64).into()), Value::Bytes(vec![0x02; 32])), (Value::Integer((-3i64).into()), Value::Bytes(vec![0x03; 32])), ];
let public_key = Value::Map(public_key_entries);
let mut public_key_bytes = Vec::new();
ciborium::ser::into_writer(&public_key, &mut public_key_bytes).unwrap();
auth_data.extend_from_slice(&public_key_bytes);
auth_data
}
fn create_auth_data_no_attested_cred() -> Vec<u8> {
let mut auth_data = Vec::new();
let rp_id_hash = digest::digest(&digest::SHA256, "example.com".as_bytes());
auth_data.extend_from_slice(rp_id_hash.as_ref());
auth_data.push(0x01 | 0x04);
auth_data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
auth_data.extend_from_slice(&[0x01; 16]);
auth_data
}
fn create_client_data_hash() -> Vec<u8> {
let client_data = r#"{"type":"webauthn.create","challenge":"dGVzdGNoYWxsZW5nZQ","origin":"https://example.com"}"#;
let hash = digest::digest(&digest::SHA256, client_data.as_bytes());
hash.as_ref().to_vec()
}
fn create_att_stmt(alg: i64, sig: &[u8]) -> Vec<(CborValue, CborValue)> {
vec![
(Value::Text("alg".to_string()), Value::Integer(alg.into())),
(Value::Text("sig".to_string()), Value::Bytes(sig.to_vec())),
]
}
fn create_att_stmt_with_x5c(
alg: i64,
sig: &[u8],
cert_bytes: Vec<u8>,
) -> Vec<(CborValue, CborValue)> {
let mut att_stmt = create_att_stmt(alg, sig);
let certs = vec![Value::Bytes(cert_bytes)];
att_stmt.push((Value::Text("x5c".to_string()), Value::Array(certs)));
att_stmt
}
fn create_att_stmt_with_ecdaa(
alg: i64,
sig: &[u8],
key_id: Vec<u8>,
) -> Vec<(CborValue, CborValue)> {
let mut att_stmt = create_att_stmt(alg, sig);
att_stmt.push((Value::Text("ecdaaKeyId".to_string()), Value::Bytes(key_id)));
att_stmt
}
fn create_dummy_cert() -> Vec<u8> {
vec![0x30, 0x82, 0x01, 0x01]
}
fn create_empty_cert_chain() -> Vec<u8> {
vec![]
}
#[test]
fn test_verify_packed_attestation_unsupported_alg() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt(-8, &sig);
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Unsupported or unrecognized algorithm"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_ecdaa_not_supported() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let key_id = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt_with_ecdaa(ES256_ALG, &sig, key_id);
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("ECDAA attestation not supported"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_both_x5c_and_ecdaa() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let mut att_stmt = create_att_stmt_with_x5c(ES256_ALG, &sig, create_dummy_cert());
att_stmt.push((
Value::Text("ecdaaKeyId".to_string()),
Value::Bytes(vec![0x01, 0x02, 0x03, 0x04]),
));
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("both x5c and ecdaaKeyId present"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_invalid_cert() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt_with_x5c(ES256_ALG, &sig, create_dummy_cert());
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Failed to parse attestation certificate"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_empty_cert_chain() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt_with_x5c(ES256_ALG, &sig, create_empty_cert_chain());
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Failed to parse attestation certificate"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_malformed_x5c() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let mut att_stmt = create_att_stmt(ES256_ALG, &sig);
let malformed_certs = vec![Value::Text("not_bytes".to_string())];
att_stmt.push((
Value::Text("x5c".to_string()),
Value::Array(malformed_certs),
));
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(
msg.contains("No attested credential data")
|| msg.contains("Invalid public key CBOR")
|| msg.contains("Self attestation signature verification failed")
);
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_self_attestation_no_cred_data() {
let auth_data = create_auth_data_no_attested_cred();
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt(ES256_ALG, &sig);
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("No attested credential data in self attestation"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_self_attestation_invalid_sig() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04]; let att_stmt = create_att_stmt(ES256_ALG, &sig);
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Self attestation signature verification failed"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_self_attestation_missing_attested_cred_flag() {
let auth_data = create_auth_data_no_attested_cred();
let signed_data = vec![0x01, 0x02, 0x03];
let signature = vec![0x04, 0x05, 0x06];
let result = verify_self_attestation(&auth_data, &signed_data, &signature);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("No attested credential data in self attestation"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_self_attestation_truncated_auth_data() {
let mut auth_data = create_basic_auth_data();
auth_data[32] |= 0x40; auth_data.extend_from_slice(&[0x01; 16]);
let signed_data = vec![0x01, 0x02, 0x03];
let signature = vec![0x04, 0x05, 0x06];
let result = verify_self_attestation(&auth_data, &signed_data, &signature);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Auth data too short for credential ID length"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_self_attestation_invalid_cbor() {
let mut auth_data = create_basic_auth_data();
auth_data[32] |= 0x40; auth_data.extend_from_slice(&[0x01; 16]); auth_data.extend_from_slice(&[0x00, 0x10]); auth_data.extend_from_slice(&[0x02; 16]); auth_data.extend_from_slice(&[0xFF, 0xFF, 0xFF]);
let signed_data = vec![0x01, 0x02, 0x03];
let signature = vec![0x04, 0x05, 0x06];
let result = verify_self_attestation(&auth_data, &signed_data, &signature);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Invalid public key CBOR"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_certificate_chain_empty() {
let x5c: Vec<Vec<u8>> = vec![];
let result = verify_certificate_chain(&x5c);
assert!(result.is_ok());
}
#[test]
fn test_verify_certificate_chain_invalid_cert() {
let x5c = vec![create_dummy_cert()];
let result = verify_certificate_chain(&x5c);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Failed to parse certificate in chain"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_certificate_chain_multiple_invalid_certs() {
let x5c = vec![create_dummy_cert(), create_dummy_cert()];
let result = verify_certificate_chain(&x5c);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Failed to parse certificate in chain"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_cert_with_dummy_data() {
let _auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
}
#[test]
fn test_verify_packed_attestation_missing_alg() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let att_stmt = vec![(
Value::Text("sig".to_string()),
Value::Bytes(vec![0x01, 0x02, 0x03, 0x04]),
)];
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
}
#[test]
fn test_verify_packed_attestation_missing_sig() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let att_stmt = vec![(
Value::Text("alg".to_string()),
Value::Integer(ES256_ALG.into()),
)];
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
}
#[test]
fn test_verify_packed_attestation_empty_att_stmt() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let att_stmt: Vec<(CborValue, CborValue)> = vec![];
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
}
#[test]
fn test_verify_packed_attestation_x5c_empty_array() {
let auth_data = create_auth_data_with_aaguid(&[0x01; 16]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let mut att_stmt = create_att_stmt(ES256_ALG, &sig);
let empty_certs: Vec<Value> = vec![];
att_stmt.push((Value::Text("x5c".to_string()), Value::Array(empty_certs)));
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Self attestation signature verification failed"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_large_credential_id() {
let mut auth_data = create_basic_auth_data();
auth_data.extend_from_slice(&[0x01; 16]);
auth_data.extend_from_slice(&[0xFF, 0xFF]);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt(ES256_ALG, &sig);
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Auth data too short for credential ID"));
} else {
panic!("Expected PasskeyError::Verification");
}
}
#[test]
fn test_verify_packed_attestation_zero_credential_id_length() {
let mut auth_data = create_basic_auth_data();
auth_data.extend_from_slice(&[0x01; 16]);
auth_data.extend_from_slice(&[0x00, 0x00]);
let public_key_entries = vec![
(Value::Integer(1i64.into()), Value::Integer(2i64.into())),
(Value::Integer(3i64.into()), Value::Integer((-7i64).into())),
(Value::Integer((-1i64).into()), Value::Integer(1i64.into())),
(Value::Integer((-2i64).into()), Value::Bytes(vec![0x02; 32])),
(Value::Integer((-3i64).into()), Value::Bytes(vec![0x03; 32])),
];
let public_key = Value::Map(public_key_entries);
let mut public_key_bytes = Vec::new();
ciborium::ser::into_writer(&public_key, &mut public_key_bytes).unwrap();
auth_data.extend_from_slice(&public_key_bytes);
let client_data_hash = create_client_data_hash();
let sig = vec![0x01, 0x02, 0x03, 0x04];
let att_stmt = create_att_stmt(ES256_ALG, &sig);
let result = verify_packed_attestation(&auth_data, &client_data_hash, &att_stmt);
assert!(result.is_err());
if let Err(PasskeyError::Verification(msg)) = result {
assert!(msg.contains("Self attestation signature verification failed"));
} else {
panic!("Expected PasskeyError::Verification");
}
}