@@ -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