Skip to content

Commit 90639f4

Browse files
authored
Updates UP and UV requirements (#749)
* Updates UP and UV requirements Fixes how devices with fingerprint recognize the contributions of internal UV to grant UP and UV. * Also adds UP caching to clientPin
1 parent 235a5c2 commit 90639f4

File tree

3 files changed

+198
-18
lines changed

3 files changed

+198
-18
lines changed

libraries/opensk/src/ctap/client_pin.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ impl<E: Env> ClientPin<E> {
339339
self.pin_protocol_v1.reset_pin_uv_auth_token(env);
340340
self.pin_protocol_v2.reset_pin_uv_auth_token(env);
341341
self.pin_uv_auth_token_state
342-
.begin_using_pin_uv_auth_token(env);
342+
.begin_using_pin_uv_auth_token(env, false);
343343
self.pin_uv_auth_token_state.set_default_permissions();
344344
let pin_uv_auth_token = shared_secret.encrypt(
345345
env,
@@ -402,7 +402,7 @@ impl<E: Env> ClientPin<E> {
402402
self.pin_protocol_v1.reset_pin_uv_auth_token(env);
403403
self.pin_protocol_v2.reset_pin_uv_auth_token(env);
404404
self.pin_uv_auth_token_state
405-
.begin_using_pin_uv_auth_token(env);
405+
.begin_using_pin_uv_auth_token(env, true);
406406
let pin_uv_auth_token = shared_secret.encrypt(
407407
env,
408408
self.get_pin_protocol(pin_uv_auth_protocol)
@@ -609,6 +609,7 @@ impl<E: Env> ClientPin<E> {
609609

610610
/// Consumes flags and permissions related to the pinUvAuthToken.
611611
pub fn clear_token_flags(&mut self) {
612+
self.pin_uv_auth_token_state.clear_user_present_flag();
612613
self.pin_uv_auth_token_state.clear_user_verified_flag();
613614
self.pin_uv_auth_token_state
614615
.clear_pin_uv_auth_token_permissions_except_lbw();
@@ -620,6 +621,11 @@ impl<E: Env> ClientPin<E> {
620621
.pin_uv_auth_token_usage_timer_observer(env);
621622
}
622623

624+
/// Returns if user presence is cached for use of the pinUvAuthToken.
625+
pub fn get_user_present_flag(&mut self) -> bool {
626+
self.pin_uv_auth_token_state.get_user_present_flag_value()
627+
}
628+
623629
/// Checks if user verification is cached for use of the pinUvAuthToken.
624630
pub fn check_user_verified_flag(&mut self) -> CtapResult<()> {
625631
if self.pin_uv_auth_token_state.get_user_verified_flag_value() {
@@ -686,7 +692,7 @@ impl<E: Env> ClientPin<E> {
686692
};
687693
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
688694
pin_uv_auth_token_state.set_permissions(0xFF);
689-
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(env);
695+
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(env, true);
690696
Self {
691697
pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token),
692698
pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token),
@@ -1727,7 +1733,7 @@ mod test {
17271733
assert!(!client_pin.has_token(&mut env));
17281734
client_pin
17291735
.pin_uv_auth_token_state
1730-
.begin_using_pin_uv_auth_token(&mut env);
1736+
.begin_using_pin_uv_auth_token(&mut env, false);
17311737
assert!(client_pin.has_token(&mut env));
17321738

17331739
let pin_uv_auth_token_v1 = client_pin

libraries/opensk/src/ctap/mod.rs

Lines changed: 152 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

libraries/opensk/src/ctap/token_state.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub struct PinUvAuthTokenState<E: Env> {
3737
permissions_set: u8,
3838
permissions_rp_id: Option<String>,
3939
usage_timer: <E::Clock as Clock>::Timer,
40+
user_present: bool,
4041
user_verified: bool,
4142
in_use: bool,
4243
}
@@ -48,6 +49,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
4849
permissions_set: 0,
4950
permissions_rp_id: None,
5051
usage_timer: <E::Clock as Clock>::Timer::default(),
52+
user_present: false,
5153
user_verified: false,
5254
in_use: false,
5355
}
@@ -110,7 +112,8 @@ impl<E: Env> PinUvAuthTokenState<E> {
110112
}
111113

112114
/// Starts the timer for pinUvAuthToken usage.
113-
pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E) {
115+
pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E, user_is_present: bool) {
116+
self.user_present = user_is_present;
114117
self.user_verified = true;
115118
self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT_MS);
116119
self.in_use = true;
@@ -126,11 +129,21 @@ impl<E: Env> PinUvAuthTokenState<E> {
126129
}
127130
}
128131

132+
/// Returns whether the user is present.
133+
pub fn get_user_present_flag_value(&self) -> bool {
134+
self.in_use && self.user_present
135+
}
136+
129137
/// Returns whether the user is verified.
130138
pub fn get_user_verified_flag_value(&self) -> bool {
131139
self.in_use && self.user_verified
132140
}
133141

142+
/// Consumes the user presence.
143+
pub fn clear_user_present_flag(&mut self) {
144+
self.user_present = false;
145+
}
146+
134147
/// Consumes the user verification.
135148
pub fn clear_user_verified_flag(&mut self) {
136149
self.user_verified = false;
@@ -146,6 +159,7 @@ impl<E: Env> PinUvAuthTokenState<E> {
146159
self.permissions_rp_id = None;
147160
self.permissions_set = 0;
148161
self.usage_timer = <E::Clock as Clock>::Timer::default();
162+
self.user_present = false;
149163
self.user_verified = false;
150164
self.in_use = false;
151165
}
@@ -161,7 +175,7 @@ mod test {
161175
fn test_observer() {
162176
let mut env = TestEnv::default();
163177
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
164-
token_state.begin_using_pin_uv_auth_token(&mut env);
178+
token_state.begin_using_pin_uv_auth_token(&mut env, false);
165179
assert!(token_state.is_in_use());
166180
env.clock().advance(100);
167181
token_state.pin_uv_auth_token_usage_timer_observer(&mut env);
@@ -175,7 +189,7 @@ mod test {
175189
fn test_stop() {
176190
let mut env = TestEnv::default();
177191
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
178-
token_state.begin_using_pin_uv_auth_token(&mut env);
192+
token_state.begin_using_pin_uv_auth_token(&mut env, false);
179193
assert!(token_state.is_in_use());
180194
token_state.stop_using_pin_uv_auth_token();
181195
assert!(!token_state.is_in_use());
@@ -257,16 +271,33 @@ mod test {
257271
);
258272
}
259273

274+
#[test]
275+
fn test_user_present_flag() {
276+
let mut env = TestEnv::default();
277+
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
278+
assert!(!token_state.get_user_present_flag_value());
279+
token_state.begin_using_pin_uv_auth_token(&mut env, false);
280+
assert!(!token_state.get_user_present_flag_value());
281+
token_state.begin_using_pin_uv_auth_token(&mut env, true);
282+
assert!(token_state.get_user_present_flag_value());
283+
token_state.clear_user_present_flag();
284+
assert!(!token_state.get_user_present_flag_value());
285+
token_state.begin_using_pin_uv_auth_token(&mut env, true);
286+
assert!(token_state.get_user_present_flag_value());
287+
token_state.stop_using_pin_uv_auth_token();
288+
assert!(!token_state.get_user_present_flag_value());
289+
}
290+
260291
#[test]
261292
fn test_user_verified_flag() {
262293
let mut env = TestEnv::default();
263294
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
264295
assert!(!token_state.get_user_verified_flag_value());
265-
token_state.begin_using_pin_uv_auth_token(&mut env);
296+
token_state.begin_using_pin_uv_auth_token(&mut env, false);
266297
assert!(token_state.get_user_verified_flag_value());
267298
token_state.clear_user_verified_flag();
268299
assert!(!token_state.get_user_verified_flag_value());
269-
token_state.begin_using_pin_uv_auth_token(&mut env);
300+
token_state.begin_using_pin_uv_auth_token(&mut env, false);
270301
assert!(token_state.get_user_verified_flag_value());
271302
token_state.stop_using_pin_uv_auth_token();
272303
assert!(!token_state.get_user_verified_flag_value());

0 commit comments

Comments
 (0)