Coverage Report

Created: 2026-08-14 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}