@@ -155,7 +155,29 @@ pub struct Ctap2GetInfoResponse {
155155 pub max_pin_length : Option < u32 > ,
156156}
157157
158+ /// CTAP 2.1/2.2 6.4: platforms assume 1024 bytes when maxMsgSize is absent.
159+ pub const DEFAULT_MAX_MSG_SIZE : usize = 1024 ;
160+
158161impl Ctap2GetInfoResponse {
162+ /// maxMsgSize (0x05), defaulting to 1024 bytes when the device omits it or advertises 0.
163+ pub fn max_msg_size ( & self ) -> usize {
164+ // A device may report 0, which cannot bound any real message; treat it as unset.
165+ self . max_msg_size
166+ . filter ( |& size| size > 0 )
167+ . map ( |size| size as usize )
168+ . unwrap_or ( DEFAULT_MAX_MSG_SIZE )
169+ }
170+
171+ /// maxCredentialCountInList (0x07), or None when the device sets no limit.
172+ pub fn max_credential_count_in_list ( & self ) -> Option < usize > {
173+ self . max_credential_count . map ( |count| count as usize )
174+ }
175+
176+ /// maxCredentialIdLength (0x08), or None when the device sets no limit.
177+ pub fn max_credential_id_length ( & self ) -> Option < usize > {
178+ self . max_credential_id_length . map ( |len| len as usize )
179+ }
180+
159181 /// Only checks if the option exists, i.e. is not None
160182 /// but does not check if the option is enabled (true)
161183 /// or disabled (false)
@@ -607,6 +629,49 @@ mod test {
607629 ) ;
608630 }
609631
632+ #[ test]
633+ fn max_msg_size_defaults_to_1024_when_absent ( ) {
634+ let info = Ctap2GetInfoResponse :: default ( ) ;
635+ assert_eq ! ( info. max_msg_size( ) , super :: DEFAULT_MAX_MSG_SIZE ) ;
636+ assert_eq ! ( info. max_msg_size( ) , 1024 ) ;
637+ }
638+
639+ #[ test]
640+ fn max_msg_size_uses_advertised_value ( ) {
641+ let info = Ctap2GetInfoResponse {
642+ max_msg_size : Some ( 2048 ) ,
643+ ..Default :: default ( )
644+ } ;
645+ assert_eq ! ( info. max_msg_size( ) , 2048 ) ;
646+ }
647+
648+ #[ test]
649+ fn max_msg_size_treats_zero_as_default ( ) {
650+ let info = Ctap2GetInfoResponse {
651+ max_msg_size : Some ( 0 ) ,
652+ ..Default :: default ( )
653+ } ;
654+ assert_eq ! ( info. max_msg_size( ) , super :: DEFAULT_MAX_MSG_SIZE ) ;
655+ }
656+
657+ #[ test]
658+ fn credential_count_and_id_length_are_unbounded_when_absent ( ) {
659+ let info = Ctap2GetInfoResponse :: default ( ) ;
660+ assert_eq ! ( info. max_credential_count_in_list( ) , None ) ;
661+ assert_eq ! ( info. max_credential_id_length( ) , None ) ;
662+ }
663+
664+ #[ test]
665+ fn credential_count_and_id_length_use_advertised_values ( ) {
666+ let info = Ctap2GetInfoResponse {
667+ max_credential_count : Some ( 8 ) ,
668+ max_credential_id_length : Some ( 64 ) ,
669+ ..Default :: default ( )
670+ } ;
671+ assert_eq ! ( info. max_credential_count_in_list( ) , Some ( 8 ) ) ;
672+ assert_eq ! ( info. max_credential_id_length( ) , Some ( 64 ) ) ;
673+ }
674+
610675 #[ test]
611676 fn device_pin_uv_auth_token_without_client_pin_does_not_panic ( ) {
612677 let info = create_info ( & [ ( "pinUvAuthToken" , true ) ] ) ;
0 commit comments