/src/OpenSK/libraries/opensk/src/ctap/config_command.rs
Line | Count | Source |
1 | | // Copyright 2020-2023 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | use super::client_pin::{ClientPin, PinPermission}; |
16 | | use super::command::AuthenticatorConfigParameters; |
17 | | use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLengthParams}; |
18 | | use super::response::ResponseData; |
19 | | use super::status_code::Ctap2StatusCode; |
20 | | use crate::api::customization::Customization; |
21 | | use crate::api::persist::Persist; |
22 | | use crate::ctap::status_code::CtapResult; |
23 | | use crate::ctap::storage; |
24 | | use crate::env::Env; |
25 | | use alloc::vec; |
26 | | |
27 | | /// Processes the subcommand enableEnterpriseAttestation for AuthenticatorConfig. |
28 | 2.91k | fn process_enable_enterprise_attestation(env: &mut impl Env) -> CtapResult<ResponseData> { |
29 | 2.91k | if env.customization().enterprise_attestation_mode().is_some() { |
30 | 690 | storage::enable_enterprise_attestation(env)?; |
31 | 690 | Ok(ResponseData::AuthenticatorConfig) |
32 | | } else { |
33 | 2.22k | Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) |
34 | | } |
35 | 2.91k | } opensk::ctap::config_command::process_enable_enterprise_attestation::<opensk::env::test::TestEnv> Line | Count | Source | 28 | 2.91k | fn process_enable_enterprise_attestation(env: &mut impl Env) -> CtapResult<ResponseData> { | 29 | 2.91k | if env.customization().enterprise_attestation_mode().is_some() { | 30 | 690 | storage::enable_enterprise_attestation(env)?; | 31 | 690 | Ok(ResponseData::AuthenticatorConfig) | 32 | | } else { | 33 | 2.22k | Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) | 34 | | } | 35 | 2.91k | } |
Unexecuted instantiation: opensk::ctap::config_command::process_enable_enterprise_attestation::<_> |
36 | | |
37 | | /// Processes the subcommand toggleAlwaysUv for AuthenticatorConfig. |
38 | 15 | fn process_toggle_always_uv(env: &mut impl Env) -> CtapResult<ResponseData> { |
39 | 15 | storage::toggle_always_uv(env)?; |
40 | 15 | Ok(ResponseData::AuthenticatorConfig) |
41 | 15 | } opensk::ctap::config_command::process_toggle_always_uv::<opensk::env::test::TestEnv> Line | Count | Source | 38 | 15 | fn process_toggle_always_uv(env: &mut impl Env) -> CtapResult<ResponseData> { | 39 | 15 | storage::toggle_always_uv(env)?; | 40 | 15 | Ok(ResponseData::AuthenticatorConfig) | 41 | 15 | } |
Unexecuted instantiation: opensk::ctap::config_command::process_toggle_always_uv::<_> |
42 | | |
43 | | /// Processes the subcommand setMinPINLength for AuthenticatorConfig. |
44 | 246 | fn process_set_min_pin_length( |
45 | 246 | env: &mut impl Env, |
46 | 246 | params: SetMinPinLengthParams, |
47 | 246 | ) -> CtapResult<ResponseData> { |
48 | | let SetMinPinLengthParams { |
49 | 246 | new_min_pin_length, |
50 | 246 | min_pin_length_rp_ids, |
51 | 246 | force_change_pin, |
52 | 246 | } = params; |
53 | 246 | let store_min_pin_length = storage::min_pin_length(env)?; |
54 | 246 | let new_min_pin_length = new_min_pin_length.unwrap_or(store_min_pin_length); |
55 | 246 | if new_min_pin_length < store_min_pin_length { |
56 | 4 | return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION); |
57 | 242 | } |
58 | 242 | let mut force_change_pin = force_change_pin.unwrap_or(false); |
59 | 242 | if force_change_pin && env.persist().pin_hash()?.is_none() { |
60 | 9 | return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); |
61 | 233 | } |
62 | 233 | if let Some(old_length) = env.persist().pin_code_point_length()? { |
63 | 0 | force_change_pin |= new_min_pin_length > old_length; |
64 | 233 | } |
65 | 233 | if force_change_pin { |
66 | 0 | env.persist().force_pin_change()?; |
67 | 233 | } |
68 | 233 | storage::set_min_pin_length(env, new_min_pin_length)?; |
69 | 233 | if let Some(min_pin_length_rp_ids) = min_pin_length_rp_ids { |
70 | 206 | storage::set_min_pin_length_rp_ids(env, min_pin_length_rp_ids)?; |
71 | 27 | } |
72 | 195 | Ok(ResponseData::AuthenticatorConfig) |
73 | 246 | } opensk::ctap::config_command::process_set_min_pin_length::<opensk::env::test::TestEnv> Line | Count | Source | 44 | 246 | fn process_set_min_pin_length( | 45 | 246 | env: &mut impl Env, | 46 | 246 | params: SetMinPinLengthParams, | 47 | 246 | ) -> CtapResult<ResponseData> { | 48 | | let SetMinPinLengthParams { | 49 | 246 | new_min_pin_length, | 50 | 246 | min_pin_length_rp_ids, | 51 | 246 | force_change_pin, | 52 | 246 | } = params; | 53 | 246 | let store_min_pin_length = storage::min_pin_length(env)?; | 54 | 246 | let new_min_pin_length = new_min_pin_length.unwrap_or(store_min_pin_length); | 55 | 246 | if new_min_pin_length < store_min_pin_length { | 56 | 4 | return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION); | 57 | 242 | } | 58 | 242 | let mut force_change_pin = force_change_pin.unwrap_or(false); | 59 | 242 | if force_change_pin && env.persist().pin_hash()?.is_none() { | 60 | 9 | return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); | 61 | 233 | } | 62 | 233 | if let Some(old_length) = env.persist().pin_code_point_length()? { | 63 | 0 | force_change_pin |= new_min_pin_length > old_length; | 64 | 233 | } | 65 | 233 | if force_change_pin { | 66 | 0 | env.persist().force_pin_change()?; | 67 | 233 | } | 68 | 233 | storage::set_min_pin_length(env, new_min_pin_length)?; | 69 | 233 | if let Some(min_pin_length_rp_ids) = min_pin_length_rp_ids { | 70 | 206 | storage::set_min_pin_length_rp_ids(env, min_pin_length_rp_ids)?; | 71 | 27 | } | 72 | 195 | Ok(ResponseData::AuthenticatorConfig) | 73 | 246 | } |
Unexecuted instantiation: opensk::ctap::config_command::process_set_min_pin_length::<_> |
74 | | |
75 | | /// Processes the AuthenticatorConfig command. |
76 | 3.17k | pub fn process_config<E: Env>( |
77 | 3.17k | env: &mut E, |
78 | 3.17k | client_pin: &mut ClientPin<E>, |
79 | 3.17k | params: AuthenticatorConfigParameters, |
80 | 3.17k | ) -> CtapResult<ResponseData> { |
81 | | let AuthenticatorConfigParameters { |
82 | 3.17k | sub_command, |
83 | 3.17k | sub_command_params, |
84 | 3.17k | pin_uv_auth_protocol, |
85 | 3.17k | pin_uv_auth_param, |
86 | 3.17k | } = params; |
87 | | |
88 | 3.17k | let enforce_uv = |
89 | 3.17k | !matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) && storage::has_always_uv(env)?; |
90 | 3.17k | if env.persist().pin_hash()?.is_some() || enforce_uv { |
91 | 0 | let pin_uv_auth_param = |
92 | 0 | pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?; |
93 | 0 | let pin_uv_auth_protocol = |
94 | 0 | pin_uv_auth_protocol.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; |
95 | | // Constants are taken from the specification, section 6.11, step 4.2. |
96 | 0 | let mut config_data = vec![0xFF; 32]; |
97 | 0 | config_data.extend(&[0x0D, sub_command as u8]); |
98 | 0 | if let Some(sub_command_params) = sub_command_params.clone() { |
99 | 0 | super::cbor_write(sub_command_params.into(), &mut config_data)?; |
100 | 0 | } |
101 | 0 | client_pin.verify_pin_uv_auth_token( |
102 | 0 | &config_data, |
103 | 0 | &pin_uv_auth_param, |
104 | 0 | pin_uv_auth_protocol, |
105 | 0 | )?; |
106 | 0 | client_pin.has_permission(PinPermission::AuthenticatorConfiguration)?; |
107 | 3.17k | } |
108 | | |
109 | 3.17k | match sub_command { |
110 | 2.91k | ConfigSubCommand::EnableEnterpriseAttestation => process_enable_enterprise_attestation(env), |
111 | 15 | ConfigSubCommand::ToggleAlwaysUv => process_toggle_always_uv(env), |
112 | | ConfigSubCommand::SetMinPinLength => { |
113 | 246 | if let Some(ConfigSubCommandParams::SetMinPinLength(params)) = sub_command_params { |
114 | 246 | process_set_min_pin_length(env, params) |
115 | | } else { |
116 | 0 | Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER) |
117 | | } |
118 | | } |
119 | 1 | _ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER), |
120 | | } |
121 | 3.17k | } opensk::ctap::config_command::process_config::<opensk::env::test::TestEnv> Line | Count | Source | 76 | 3.17k | pub fn process_config<E: Env>( | 77 | 3.17k | env: &mut E, | 78 | 3.17k | client_pin: &mut ClientPin<E>, | 79 | 3.17k | params: AuthenticatorConfigParameters, | 80 | 3.17k | ) -> CtapResult<ResponseData> { | 81 | | let AuthenticatorConfigParameters { | 82 | 3.17k | sub_command, | 83 | 3.17k | sub_command_params, | 84 | 3.17k | pin_uv_auth_protocol, | 85 | 3.17k | pin_uv_auth_param, | 86 | 3.17k | } = params; | 87 | | | 88 | 3.17k | let enforce_uv = | 89 | 3.17k | !matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) && storage::has_always_uv(env)?; | 90 | 3.17k | if env.persist().pin_hash()?.is_some() || enforce_uv { | 91 | 0 | let pin_uv_auth_param = | 92 | 0 | pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?; | 93 | 0 | let pin_uv_auth_protocol = | 94 | 0 | pin_uv_auth_protocol.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; | 95 | | // Constants are taken from the specification, section 6.11, step 4.2. | 96 | 0 | let mut config_data = vec![0xFF; 32]; | 97 | 0 | config_data.extend(&[0x0D, sub_command as u8]); | 98 | 0 | if let Some(sub_command_params) = sub_command_params.clone() { | 99 | 0 | super::cbor_write(sub_command_params.into(), &mut config_data)?; | 100 | 0 | } | 101 | 0 | client_pin.verify_pin_uv_auth_token( | 102 | 0 | &config_data, | 103 | 0 | &pin_uv_auth_param, | 104 | 0 | pin_uv_auth_protocol, | 105 | 0 | )?; | 106 | 0 | client_pin.has_permission(PinPermission::AuthenticatorConfiguration)?; | 107 | 3.17k | } | 108 | | | 109 | 3.17k | match sub_command { | 110 | 2.91k | ConfigSubCommand::EnableEnterpriseAttestation => process_enable_enterprise_attestation(env), | 111 | 15 | ConfigSubCommand::ToggleAlwaysUv => process_toggle_always_uv(env), | 112 | | ConfigSubCommand::SetMinPinLength => { | 113 | 246 | if let Some(ConfigSubCommandParams::SetMinPinLength(params)) = sub_command_params { | 114 | 246 | process_set_min_pin_length(env, params) | 115 | | } else { | 116 | 0 | Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER) | 117 | | } | 118 | | } | 119 | 1 | _ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER), | 120 | | } | 121 | 3.17k | } |
Unexecuted instantiation: opensk::ctap::config_command::process_config::<_> |
122 | | |
123 | | #[cfg(test)] |
124 | | mod test { |
125 | | use super::*; |
126 | | use crate::api::crypto::ecdh::SecretKey as _; |
127 | | use crate::api::customization::Customization; |
128 | | use crate::ctap::data_formats::PinUvAuthProtocol; |
129 | | use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token; |
130 | | use crate::env::EcdhSk; |
131 | | use crate::env::test::TestEnv; |
132 | | |
133 | | #[test] |
134 | | fn test_process_enable_enterprise_attestation() { |
135 | | let mut env = TestEnv::default(); |
136 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
137 | | let pin_uv_auth_token = [0x55; 32]; |
138 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
139 | | &mut env, |
140 | | key_agreement_key, |
141 | | pin_uv_auth_token, |
142 | | PinUvAuthProtocol::V1, |
143 | | ); |
144 | | |
145 | | let config_params = AuthenticatorConfigParameters { |
146 | | sub_command: ConfigSubCommand::EnableEnterpriseAttestation, |
147 | | sub_command_params: None, |
148 | | pin_uv_auth_param: None, |
149 | | pin_uv_auth_protocol: None, |
150 | | }; |
151 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
152 | | |
153 | | if env.customization().enterprise_attestation_mode().is_some() { |
154 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
155 | | assert_eq!(storage::enterprise_attestation(&mut env), Ok(true)); |
156 | | } else { |
157 | | assert_eq!( |
158 | | config_response, |
159 | | Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) |
160 | | ); |
161 | | } |
162 | | } |
163 | | |
164 | | #[test] |
165 | | fn test_process_toggle_always_uv() { |
166 | | let mut env = TestEnv::default(); |
167 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
168 | | let pin_uv_auth_token = [0x55; 32]; |
169 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
170 | | &mut env, |
171 | | key_agreement_key, |
172 | | pin_uv_auth_token, |
173 | | PinUvAuthProtocol::V1, |
174 | | ); |
175 | | |
176 | | let config_params = AuthenticatorConfigParameters { |
177 | | sub_command: ConfigSubCommand::ToggleAlwaysUv, |
178 | | sub_command_params: None, |
179 | | pin_uv_auth_param: None, |
180 | | pin_uv_auth_protocol: None, |
181 | | }; |
182 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
183 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
184 | | assert!(storage::has_always_uv(&mut env).unwrap()); |
185 | | |
186 | | let config_params = AuthenticatorConfigParameters { |
187 | | sub_command: ConfigSubCommand::ToggleAlwaysUv, |
188 | | sub_command_params: None, |
189 | | pin_uv_auth_param: None, |
190 | | pin_uv_auth_protocol: None, |
191 | | }; |
192 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
193 | | if env.customization().enforce_always_uv() { |
194 | | assert_eq!( |
195 | | config_response, |
196 | | Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED) |
197 | | ); |
198 | | } else { |
199 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
200 | | assert!(!storage::has_always_uv(&mut env).unwrap()); |
201 | | } |
202 | | } |
203 | | |
204 | | fn test_helper_process_toggle_always_uv_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { |
205 | | let mut env = TestEnv::default(); |
206 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
207 | | let pin_uv_auth_token = [0x55; 32]; |
208 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
209 | | &mut env, |
210 | | key_agreement_key, |
211 | | pin_uv_auth_token, |
212 | | pin_uv_auth_protocol, |
213 | | ); |
214 | | env.persist().set_pin(&[0x88; 16], 4).unwrap(); |
215 | | |
216 | | let mut config_data = vec![0xFF; 32]; |
217 | | config_data.extend(&[0x0D, ConfigSubCommand::ToggleAlwaysUv as u8]); |
218 | | let pin_uv_auth_param = |
219 | | authenticate_pin_uv_auth_token(&pin_uv_auth_token, &config_data, pin_uv_auth_protocol); |
220 | | let config_params = AuthenticatorConfigParameters { |
221 | | sub_command: ConfigSubCommand::ToggleAlwaysUv, |
222 | | sub_command_params: None, |
223 | | pin_uv_auth_param: Some(pin_uv_auth_param.clone()), |
224 | | pin_uv_auth_protocol: Some(pin_uv_auth_protocol), |
225 | | }; |
226 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
227 | | if env.customization().enforce_always_uv() { |
228 | | assert_eq!( |
229 | | config_response, |
230 | | Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED) |
231 | | ); |
232 | | return; |
233 | | } |
234 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
235 | | assert!(storage::has_always_uv(&mut env).unwrap()); |
236 | | |
237 | | let config_params = AuthenticatorConfigParameters { |
238 | | sub_command: ConfigSubCommand::ToggleAlwaysUv, |
239 | | sub_command_params: None, |
240 | | pin_uv_auth_param: Some(pin_uv_auth_param), |
241 | | pin_uv_auth_protocol: Some(pin_uv_auth_protocol), |
242 | | }; |
243 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
244 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
245 | | assert!(!storage::has_always_uv(&mut env).unwrap()); |
246 | | } |
247 | | |
248 | | #[test] |
249 | | fn test_process_toggle_always_uv_with_pin_v1() { |
250 | | test_helper_process_toggle_always_uv_with_pin(PinUvAuthProtocol::V1); |
251 | | } |
252 | | |
253 | | #[test] |
254 | | fn test_process_toggle_always_uv_with_pin_v2() { |
255 | | test_helper_process_toggle_always_uv_with_pin(PinUvAuthProtocol::V2); |
256 | | } |
257 | | |
258 | | fn create_min_pin_config_params( |
259 | | min_pin_length: u8, |
260 | | min_pin_length_rp_ids: Option<Vec<String>>, |
261 | | ) -> AuthenticatorConfigParameters { |
262 | | let set_min_pin_length_params = SetMinPinLengthParams { |
263 | | new_min_pin_length: Some(min_pin_length), |
264 | | min_pin_length_rp_ids, |
265 | | force_change_pin: None, |
266 | | }; |
267 | | AuthenticatorConfigParameters { |
268 | | sub_command: ConfigSubCommand::SetMinPinLength, |
269 | | sub_command_params: Some(ConfigSubCommandParams::SetMinPinLength( |
270 | | set_min_pin_length_params, |
271 | | )), |
272 | | pin_uv_auth_param: None, |
273 | | pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1), |
274 | | } |
275 | | } |
276 | | |
277 | | #[test] |
278 | | fn test_process_set_min_pin_length() { |
279 | | let mut env = TestEnv::default(); |
280 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
281 | | let pin_uv_auth_token = [0x55; 32]; |
282 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
283 | | &mut env, |
284 | | key_agreement_key, |
285 | | pin_uv_auth_token, |
286 | | PinUvAuthProtocol::V1, |
287 | | ); |
288 | | |
289 | | // First, increase minimum PIN length from 4 to 6 without PIN auth. |
290 | | let min_pin_length = 6; |
291 | | let config_params = create_min_pin_config_params(min_pin_length, None); |
292 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
293 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
294 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
295 | | |
296 | | // Second, increase minimum PIN length from 6 to 8 with PIN auth. |
297 | | // The stored PIN or its length don't matter since we control the token. |
298 | | env.persist().set_pin(&[0x88; 16], 8).unwrap(); |
299 | | let min_pin_length = 8; |
300 | | let mut config_params = create_min_pin_config_params(min_pin_length, None); |
301 | | let pin_uv_auth_param = vec![ |
302 | | 0x5C, 0x69, 0x71, 0x29, 0xBD, 0xCC, 0x53, 0xE8, 0x3C, 0x97, 0x62, 0xDD, 0x90, 0x29, |
303 | | 0xB2, 0xDE, |
304 | | ]; |
305 | | config_params.pin_uv_auth_param = Some(pin_uv_auth_param); |
306 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
307 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
308 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
309 | | |
310 | | // Third, decreasing the minimum PIN length from 8 to 7 fails. |
311 | | let mut config_params = create_min_pin_config_params(7, None); |
312 | | let pin_uv_auth_param = vec![ |
313 | | 0xC5, 0xEA, 0xC1, 0x5E, 0x7F, 0x80, 0x70, 0x1A, 0x4E, 0xC4, 0xAD, 0x85, 0x35, 0xD8, |
314 | | 0xA7, 0x71, |
315 | | ]; |
316 | | config_params.pin_uv_auth_param = Some(pin_uv_auth_param); |
317 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
318 | | assert_eq!( |
319 | | config_response, |
320 | | Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION) |
321 | | ); |
322 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
323 | | } |
324 | | |
325 | | #[test] |
326 | | fn test_process_set_min_pin_length_rp_ids() { |
327 | | let mut env = TestEnv::default(); |
328 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
329 | | let pin_uv_auth_token = [0x55; 32]; |
330 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
331 | | &mut env, |
332 | | key_agreement_key, |
333 | | pin_uv_auth_token, |
334 | | PinUvAuthProtocol::V1, |
335 | | ); |
336 | | |
337 | | // First, set RP IDs without PIN auth. |
338 | | let min_pin_length = 6; |
339 | | let min_pin_length_rp_ids = vec!["example.com".to_string()]; |
340 | | let config_params = |
341 | | create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone())); |
342 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
343 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
344 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
345 | | assert_eq!( |
346 | | storage::min_pin_length_rp_ids(&mut env), |
347 | | Ok(min_pin_length_rp_ids) |
348 | | ); |
349 | | |
350 | | // Second, change the RP IDs with PIN auth. |
351 | | let min_pin_length = 8; |
352 | | let min_pin_length_rp_ids = vec!["another.example.com".to_string()]; |
353 | | // The stored PIN or its length don't matter since we control the token. |
354 | | env.persist().set_pin(&[0x88; 16], 8).unwrap(); |
355 | | let mut config_params = |
356 | | create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone())); |
357 | | let pin_uv_auth_param = vec![ |
358 | | 0x40, 0x51, 0x2D, 0xAC, 0x2D, 0xE2, 0x15, 0x77, 0x5C, 0xF9, 0x5B, 0x62, 0x9A, 0x2D, |
359 | | 0xD6, 0xDA, |
360 | | ]; |
361 | | config_params.pin_uv_auth_param = Some(pin_uv_auth_param.clone()); |
362 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
363 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
364 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
365 | | assert_eq!( |
366 | | storage::min_pin_length_rp_ids(&mut env), |
367 | | Ok(min_pin_length_rp_ids.clone()) |
368 | | ); |
369 | | |
370 | | // Third, changing RP IDs with bad PIN auth fails. |
371 | | // One PIN auth shouldn't work for different lengths. |
372 | | let mut config_params = |
373 | | create_min_pin_config_params(9, Some(min_pin_length_rp_ids.clone())); |
374 | | config_params.pin_uv_auth_param = Some(pin_uv_auth_param.clone()); |
375 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
376 | | assert_eq!( |
377 | | config_response, |
378 | | Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) |
379 | | ); |
380 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
381 | | assert_eq!( |
382 | | storage::min_pin_length_rp_ids(&mut env), |
383 | | Ok(min_pin_length_rp_ids.clone()) |
384 | | ); |
385 | | |
386 | | // Forth, changing RP IDs with bad PIN auth fails. |
387 | | // One PIN auth shouldn't work for different RP IDs. |
388 | | let mut config_params = create_min_pin_config_params( |
389 | | min_pin_length, |
390 | | Some(vec!["counter.example.com".to_string()]), |
391 | | ); |
392 | | config_params.pin_uv_auth_param = Some(pin_uv_auth_param); |
393 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
394 | | assert_eq!( |
395 | | config_response, |
396 | | Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) |
397 | | ); |
398 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
399 | | assert_eq!( |
400 | | storage::min_pin_length_rp_ids(&mut env), |
401 | | Ok(min_pin_length_rp_ids) |
402 | | ); |
403 | | } |
404 | | |
405 | | #[test] |
406 | | fn test_process_set_min_pin_length_force_pin_change_implicit() { |
407 | | let mut env = TestEnv::default(); |
408 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
409 | | let pin_uv_auth_token = [0x55; 32]; |
410 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
411 | | &mut env, |
412 | | key_agreement_key, |
413 | | pin_uv_auth_token, |
414 | | PinUvAuthProtocol::V1, |
415 | | ); |
416 | | |
417 | | env.persist().set_pin(&[0x88; 16], 4).unwrap(); |
418 | | // Increase min PIN, force PIN change. |
419 | | let min_pin_length = 6; |
420 | | let mut config_params = create_min_pin_config_params(min_pin_length, None); |
421 | | let pin_uv_auth_param = Some(vec![ |
422 | | 0x81, 0x37, 0x37, 0xF3, 0xD8, 0x69, 0xBD, 0x74, 0xFE, 0x88, 0x30, 0x8C, 0xC4, 0x2E, |
423 | | 0xA8, 0xC8, |
424 | | ]); |
425 | | config_params.pin_uv_auth_param = pin_uv_auth_param; |
426 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
427 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
428 | | assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); |
429 | | assert_eq!(env.persist().has_force_pin_change(), Ok(true)); |
430 | | } |
431 | | |
432 | | #[test] |
433 | | fn test_process_set_min_pin_length_force_pin_change_explicit() { |
434 | | let mut env = TestEnv::default(); |
435 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
436 | | let pin_uv_auth_token = [0x55; 32]; |
437 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
438 | | &mut env, |
439 | | key_agreement_key, |
440 | | pin_uv_auth_token, |
441 | | PinUvAuthProtocol::V1, |
442 | | ); |
443 | | |
444 | | env.persist().set_pin(&[0x88; 16], 4).unwrap(); |
445 | | let pin_uv_auth_param = Some(vec![ |
446 | | 0xE3, 0x74, 0xF4, 0x27, 0xBE, 0x7D, 0x40, 0xB5, 0x71, 0xB6, 0xB4, 0x1A, 0xD2, 0xC1, |
447 | | 0x53, 0xD7, |
448 | | ]); |
449 | | let set_min_pin_length_params = SetMinPinLengthParams { |
450 | | new_min_pin_length: Some(storage::min_pin_length(&mut env).unwrap()), |
451 | | min_pin_length_rp_ids: None, |
452 | | force_change_pin: Some(true), |
453 | | }; |
454 | | let config_params = AuthenticatorConfigParameters { |
455 | | sub_command: ConfigSubCommand::SetMinPinLength, |
456 | | sub_command_params: Some(ConfigSubCommandParams::SetMinPinLength( |
457 | | set_min_pin_length_params, |
458 | | )), |
459 | | pin_uv_auth_param, |
460 | | pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1), |
461 | | }; |
462 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
463 | | assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); |
464 | | assert_eq!(env.persist().has_force_pin_change(), Ok(true)); |
465 | | } |
466 | | |
467 | | #[test] |
468 | | fn test_process_config_vendor_prototype() { |
469 | | let mut env = TestEnv::default(); |
470 | | let key_agreement_key = EcdhSk::<TestEnv>::random(env.rng()); |
471 | | let pin_uv_auth_token = [0x55; 32]; |
472 | | let mut client_pin = ClientPin::<TestEnv>::new_test( |
473 | | &mut env, |
474 | | key_agreement_key, |
475 | | pin_uv_auth_token, |
476 | | PinUvAuthProtocol::V1, |
477 | | ); |
478 | | |
479 | | let config_params = AuthenticatorConfigParameters { |
480 | | sub_command: ConfigSubCommand::VendorPrototype, |
481 | | sub_command_params: None, |
482 | | pin_uv_auth_param: None, |
483 | | pin_uv_auth_protocol: None, |
484 | | }; |
485 | | let config_response = process_config(&mut env, &mut client_pin, config_params); |
486 | | assert_eq!( |
487 | | config_response, |
488 | | Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) |
489 | | ); |
490 | | } |
491 | | } |