Skip to content

Commit 60f7a2b

Browse files
test(credmgmt): GetNext requests carry only the subcommand
1 parent b4e15a8 commit 60f7a2b

1 file changed

Lines changed: 81 additions & 1 deletion

File tree

libwebauthn/src/management/credential_management.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,11 @@ mod test {
333333
use crate::proto::ctap2::cbor::{self, CborRequest, CborResponse};
334334
use crate::proto::ctap2::{
335335
Ctap2CommandCode, Ctap2CredentialManagementRequest, Ctap2GetInfoResponse,
336-
Ctap2PublicKeyCredentialRpEntity,
336+
Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity,
337+
Ctap2PublicKeyCredentialType, Ctap2PublicKeyCredentialUserEntity,
337338
};
338339
use crate::transport::mock::channel::MockChannel;
340+
use std::collections::BTreeMap;
339341

340342
const TIMEOUT: Duration = Duration::from_secs(1);
341343

@@ -379,4 +381,82 @@ mod test {
379381
assert_eq!(rp_data.rp_id_hash.len(), 32);
380382
assert_eq!(rp_data.rp_id_hash, hash.to_vec());
381383
}
384+
385+
// GetNextRP returns only rp (0x03) and rpIDHash (0x04).
386+
#[derive(SerializeIndexed)]
387+
struct EnumerateRpsNextResponse {
388+
#[serde(index = 0x03)]
389+
rp: Ctap2PublicKeyCredentialRpEntity,
390+
#[serde(index = 0x04)]
391+
rp_id_hash: ByteBuf,
392+
}
393+
394+
// GetNextCredential returns user (0x06), credentialID (0x07), publicKey (0x08), credProtect (0x0A).
395+
#[derive(SerializeIndexed)]
396+
struct EnumerateCredsNextResponse {
397+
#[serde(index = 0x06)]
398+
user: Ctap2PublicKeyCredentialUserEntity,
399+
#[serde(index = 0x07)]
400+
credential_id: Ctap2PublicKeyCredentialDescriptor,
401+
#[serde(index = 0x08)]
402+
public_key: BTreeMap<i8, i8>,
403+
#[serde(index = 0x0A)]
404+
cred_protect: u64,
405+
}
406+
407+
#[tokio::test]
408+
async fn enumerate_rps_next_rp_sends_only_the_subcommand() {
409+
let req = Ctap2CredentialManagementRequest::new_enumerate_rps_next_rp();
410+
let cbor_req: CborRequest = (&req).try_into().unwrap();
411+
assert_eq!(
412+
cbor_req.command,
413+
Ctap2CommandCode::AuthenticatorCredentialManagement
414+
);
415+
// CTAP 2.1 §6.8.3: {subCommand: enumerateRPsGetNextRP}, no pinUvAuth keys.
416+
assert_eq!(cbor_req.encoded_data, vec![0xA1, 0x01, 0x03]);
417+
418+
let hash = [0xCD_u8; 32];
419+
let fixture = EnumerateRpsNextResponse {
420+
rp: Ctap2PublicKeyCredentialRpEntity::new("example.org", "Example"),
421+
rp_id_hash: ByteBuf::from(hash.to_vec()),
422+
};
423+
let resp = CborResponse::new_success_from_slice(&cbor::to_vec(&fixture).unwrap());
424+
// Queue only the GetNext exchange: any interleaved command panics the mock.
425+
let mut channel = MockChannel::new();
426+
channel.push_command_pair(cbor_req, resp);
427+
428+
let rp_data = channel.enumerate_rps_next_rp(TIMEOUT).await.unwrap();
429+
assert_eq!(rp_data.rp_id_hash, hash.to_vec());
430+
}
431+
432+
#[tokio::test]
433+
async fn enumerate_credentials_next_sends_only_the_subcommand() {
434+
let req = Ctap2CredentialManagementRequest::new_enumerate_credentials_next();
435+
let cbor_req: CborRequest = (&req).try_into().unwrap();
436+
assert_eq!(
437+
cbor_req.command,
438+
Ctap2CommandCode::AuthenticatorCredentialManagement
439+
);
440+
// CTAP 2.1 §6.8.4: {subCommand: enumerateCredentialsGetNextCredential}, no pinUvAuth keys.
441+
assert_eq!(cbor_req.encoded_data, vec![0xA1, 0x01, 0x05]);
442+
443+
let fixture = EnumerateCredsNextResponse {
444+
user: Ctap2PublicKeyCredentialUserEntity::new(&[0x0B; 16], "bob", "bob"),
445+
credential_id: Ctap2PublicKeyCredentialDescriptor {
446+
id: ByteBuf::from(vec![0x1D; 32]),
447+
r#type: Ctap2PublicKeyCredentialType::PublicKey,
448+
transports: None,
449+
},
450+
public_key: BTreeMap::from([(1, 2), (3, -7)]),
451+
cred_protect: 1,
452+
};
453+
let resp = CborResponse::new_success_from_slice(&cbor::to_vec(&fixture).unwrap());
454+
let mut channel = MockChannel::new();
455+
channel.push_command_pair(cbor_req, resp);
456+
457+
let cred = channel.enumerate_credentials_next(TIMEOUT).await.unwrap();
458+
assert_eq!(cred.user.id, ByteBuf::from(vec![0x0B; 16]));
459+
assert_eq!(cred.cred_protect, 1);
460+
assert!(cred.large_blob_key.is_none());
461+
}
382462
}

0 commit comments

Comments
 (0)