@@ -763,6 +763,14 @@ impl<E: Env> CtapState<E> {
763763 Ok ( ( ) )
764764 }
765765
766+ fn has_valid_up ( & mut self , has_pin_uv_auth_param : bool , has_uv_option : bool ) -> bool {
767+ if has_pin_uv_auth_param {
768+ self . client_pin . get_user_present_flag ( )
769+ } else {
770+ has_uv_option
771+ }
772+ }
773+
766774 fn process_make_credential (
767775 & mut self ,
768776 env : & mut E ,
@@ -812,10 +820,7 @@ impl<E: Env> CtapState<E> {
812820 false
813821 } ;
814822
815- // MakeCredential always requires user presence.
816- // User verification depends on the PIN auth inputs, which are checked here.
817- // The ED flag is added later, if applicable.
818- let has_uv = pin_uv_auth_param. is_some ( ) ;
823+ let has_pin_uv_auth_param = pin_uv_auth_param. is_some ( ) ;
819824 let mut flags = match pin_uv_auth_param {
820825 Some ( pin_uv_auth_param) => {
821826 // This case is not mentioned in CTAP2.1, so we keep 2.0 logic.
@@ -860,8 +865,11 @@ impl<E: Env> CtapState<E> {
860865 }
861866 }
862867 } ;
868+ // MakeCredential always requires user presence.
869+ // The ED flag is added later, if applicable.
863870 flags |= UP_FLAG | AT_FLAG ;
864871
872+ let has_uv = has_pin_uv_auth_param || options. uv ;
865873 let rp_id_hash = Sha :: < E > :: digest ( rp_id. as_bytes ( ) ) ;
866874 if let Some ( exclude_list) = exclude_list {
867875 for cred_desc in exclude_list {
@@ -885,7 +893,9 @@ impl<E: Env> CtapState<E> {
885893 }
886894 }
887895
888- check_user_presence ( env, channel) ?;
896+ if !self . has_valid_up ( has_pin_uv_auth_param, options. uv ) {
897+ check_user_presence ( env, channel) ?;
898+ }
889899 self . client_pin . clear_token_flags ( ) ;
890900
891901 let default_cred_protect = env. customization ( ) . default_cred_protect ( ) ;
@@ -1191,9 +1201,7 @@ impl<E: Env> CtapState<E> {
11911201 return Err ( Ctap2StatusCode :: CTAP2_ERR_UNSUPPORTED_OPTION ) ;
11921202 }
11931203
1194- // The user verification bit depends on the existance of PIN auth, since we do
1195- // not support internal UV. User presence is requested as an option.
1196- let has_uv = pin_uv_auth_param. is_some ( ) ;
1204+ let has_pin_uv_auth_param = pin_uv_auth_param. is_some ( ) ;
11971205 let mut flags = match pin_uv_auth_param {
11981206 Some ( pin_uv_auth_param) => {
11991207 // This case is not mentioned in CTAP2.1, so we keep 2.0 logic.
@@ -1240,6 +1248,7 @@ impl<E: Env> CtapState<E> {
12401248 flags |= ED_FLAG ;
12411249 }
12421250
1251+ let has_uv = has_pin_uv_auth_param || options. uv ;
12431252 let rp_id_hash = Sha :: < E > :: digest ( rp_id. as_bytes ( ) ) ;
12441253 let ( credential, next_credential_keys) = if let Some ( allow_list) = allow_list {
12451254 (
@@ -1280,7 +1289,7 @@ impl<E: Env> CtapState<E> {
12801289 let credential = credential. ok_or ( Ctap2StatusCode :: CTAP2_ERR_NO_CREDENTIALS ) ?;
12811290
12821291 // This check comes before CTAP2_ERR_NO_CREDENTIALS in CTAP 2.0.
1283- if options. up {
1292+ if options. up && ! self . has_valid_up ( has_pin_uv_auth_param , options . uv ) {
12841293 check_user_presence ( env, channel) ?;
12851294 self . client_pin . clear_token_flags ( ) ;
12861295 }
@@ -2393,6 +2402,39 @@ mod test {
23932402 ) ;
23942403 }
23952404
2405+ #[ test]
2406+ #[ cfg( feature = "fingerprint" ) ]
2407+ fn test_process_make_credential_cached_up ( ) {
2408+ let mut env = TestEnv :: default ( ) ;
2409+ let key_agreement_key = EcdhSk :: < TestEnv > :: random ( env. rng ( ) ) ;
2410+ let pin_uv_auth_token = [ 0x88 ; 32 ] ;
2411+ let pin_uv_auth_protocol = PinUvAuthProtocol :: V2 ;
2412+ let client_pin = ClientPin :: < TestEnv > :: new_test (
2413+ & mut env,
2414+ key_agreement_key,
2415+ pin_uv_auth_token,
2416+ pin_uv_auth_protocol,
2417+ ) ;
2418+ let mut ctap_state = CtapState :: < TestEnv > :: new ( & mut env) ;
2419+ env. create_fingerprint ( ) . unwrap ( ) ;
2420+ ctap_state. client_pin = client_pin;
2421+
2422+ env. persist ( ) . set_pin ( & [ 0u8 ; 16 ] , 4 ) . unwrap ( ) ;
2423+ env. user_presence ( ) . set ( || Err ( UserPresenceError :: Canceled ) ) ;
2424+ let client_data_hash = [ 0xCD ] ;
2425+ let pin_uv_auth_param = authenticate_pin_uv_auth_token (
2426+ & pin_uv_auth_token,
2427+ & client_data_hash,
2428+ pin_uv_auth_protocol,
2429+ ) ;
2430+ let mut make_credential_params = create_minimal_make_credential_parameters ( ) ;
2431+ make_credential_params. pin_uv_auth_param = Some ( pin_uv_auth_param) ;
2432+ make_credential_params. pin_uv_auth_protocol = Some ( pin_uv_auth_protocol) ;
2433+ assert ! ( ctap_state
2434+ . process_make_credential( & mut env, make_credential_params, DUMMY_CHANNEL )
2435+ . is_ok( ) ) ;
2436+ }
2437+
23962438 fn check_assertion_response_with_user (
23972439 response : CtapResult < ResponseData > ,
23982440 expected_user : Option < PublicKeyCredentialUserEntity > ,
@@ -2501,6 +2543,107 @@ mod test {
25012543 check_assertion_response ( get_assertion_response, vec ! [ 0x1D ] , signature_counter, None ) ;
25022544 }
25032545
2546+ #[ test]
2547+ fn test_process_get_assertion_cancelled ( ) {
2548+ let mut env = TestEnv :: default ( ) ;
2549+ let mut ctap_state = CtapState :: < TestEnv > :: new ( & mut env) ;
2550+
2551+ let make_credential_params = create_minimal_make_credential_parameters ( ) ;
2552+ assert ! ( ctap_state
2553+ . process_make_credential( & mut env, make_credential_params, DUMMY_CHANNEL )
2554+ . is_ok( ) ) ;
2555+
2556+ env. user_presence ( ) . set ( || Err ( UserPresenceError :: Canceled ) ) ;
2557+ let get_assertion_params = AuthenticatorGetAssertionParameters {
2558+ rp_id : String :: from ( "example.com" ) ,
2559+ client_data_hash : vec ! [ 0xCD ] ,
2560+ allow_list : None ,
2561+ extensions : GetAssertionExtensions :: default ( ) ,
2562+ options : GetAssertionOptions {
2563+ up : true ,
2564+ uv : false ,
2565+ } ,
2566+ pin_uv_auth_param : None ,
2567+ pin_uv_auth_protocol : None ,
2568+ } ;
2569+ let get_assertion_response =
2570+ ctap_state. process_get_assertion ( & mut env, get_assertion_params, DUMMY_CHANNEL ) ;
2571+ assert_eq ! (
2572+ get_assertion_response,
2573+ Err ( Ctap2StatusCode :: CTAP2_ERR_KEEPALIVE_CANCEL )
2574+ ) ;
2575+ }
2576+
2577+ #[ test]
2578+ #[ cfg( feature = "fingerprint" ) ]
2579+ fn test_process_get_assertion_up_through_fingerprint ( ) {
2580+ let mut env = TestEnv :: default ( ) ;
2581+ let mut ctap_state = CtapState :: < TestEnv > :: new ( & mut env) ;
2582+
2583+ let make_credential_params = create_minimal_make_credential_parameters ( ) ;
2584+ assert ! ( ctap_state
2585+ . process_make_credential( & mut env, make_credential_params, DUMMY_CHANNEL )
2586+ . is_ok( ) ) ;
2587+
2588+ env. user_presence ( ) . set ( || Err ( UserPresenceError :: Canceled ) ) ;
2589+ env. create_fingerprint ( ) . unwrap ( ) ;
2590+ let get_assertion_params = AuthenticatorGetAssertionParameters {
2591+ rp_id : String :: from ( "example.com" ) ,
2592+ client_data_hash : vec ! [ 0xCD ] ,
2593+ allow_list : None ,
2594+ extensions : GetAssertionExtensions :: default ( ) ,
2595+ options : GetAssertionOptions { up : true , uv : true } ,
2596+ pin_uv_auth_param : None ,
2597+ pin_uv_auth_protocol : None ,
2598+ } ;
2599+ let get_assertion_response =
2600+ ctap_state. process_get_assertion ( & mut env, get_assertion_params, DUMMY_CHANNEL ) ;
2601+ let expected_user = PublicKeyCredentialUserEntity {
2602+ user_id : vec ! [ 0x1D ] ,
2603+ user_name : None ,
2604+ user_display_name : None ,
2605+ user_icon : None ,
2606+ } ;
2607+ let signature_counter = env. persist ( ) . global_signature_counter ( ) . unwrap ( ) ;
2608+ check_assertion_response_with_user (
2609+ get_assertion_response,
2610+ Some ( expected_user) ,
2611+ 0x05 ,
2612+ signature_counter,
2613+ None ,
2614+ & [ ] ,
2615+ ) ;
2616+ }
2617+
2618+ #[ test]
2619+ #[ cfg( not( feature = "fingerprint" ) ) ]
2620+ fn test_process_get_assertion_no_uv_from_fingerprint ( ) {
2621+ let mut env = TestEnv :: default ( ) ;
2622+ let mut ctap_state = CtapState :: < TestEnv > :: new ( & mut env) ;
2623+
2624+ let make_credential_params = create_minimal_make_credential_parameters ( ) ;
2625+ assert ! ( ctap_state
2626+ . process_make_credential( & mut env, make_credential_params, DUMMY_CHANNEL )
2627+ . is_ok( ) ) ;
2628+
2629+ env. user_presence ( ) . set ( || Err ( UserPresenceError :: Canceled ) ) ;
2630+ let get_assertion_params = AuthenticatorGetAssertionParameters {
2631+ rp_id : String :: from ( "example.com" ) ,
2632+ client_data_hash : vec ! [ 0xCD ] ,
2633+ allow_list : None ,
2634+ extensions : GetAssertionExtensions :: default ( ) ,
2635+ options : GetAssertionOptions { up : true , uv : true } ,
2636+ pin_uv_auth_param : None ,
2637+ pin_uv_auth_protocol : None ,
2638+ } ;
2639+ let get_assertion_response =
2640+ ctap_state. process_get_assertion ( & mut env, get_assertion_params, DUMMY_CHANNEL ) ;
2641+ assert_eq ! (
2642+ get_assertion_response,
2643+ Err ( Ctap2StatusCode :: CTAP2_ERR_INVALID_OPTION )
2644+ ) ;
2645+ }
2646+
25042647 fn get_assertion_hmac_secret_params (
25052648 key_agreement_key : EcdhSk < TestEnv > ,
25062649 key_agreement_response : ResponseData ,
0 commit comments