• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / OpenSK / 16774467507

06 Aug 2025 10:32AM UTC coverage: 97.209% (+0.03%) from 97.177%
16774467507

push

github

web-flow
Fingerprints are stored even on beginEnroll (#752)

* Fingerprints are stored even on beginEnroll

Before we had a bug that when enrollment only needed one single sample,
it wouldn't be saved to persistent storage.

Also makes the fake TockEnv implementation usable with Chrome.
Since it will accept all MakeCredential and GetAssertion requests with
up and uv flag set without any user interaction, proceed with caution
when deploying with the `fingerprint` feature.

Adds some tests for the new fingerprint functionality, after checking
with coveralls.

* Checks that cancelling enrollment clears status

163 of 170 new or added lines in 2 files covered. (95.88%)

34 existing lines in 6 files now uncovered.

13167 of 13545 relevant lines covered (97.21%)

10373.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.29
/libraries/opensk/src/api/customization.rs
1
// Copyright 2022-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
//! This file contains all customizable constants.
16
//!
17
//! If you adapt them, make sure to run the tests before flashing the firmware.
18
//! Our deploy script enforces the invariants.
19

20
use crate::ctap::data_formats::{CredentialProtectionPolicy, EnterpriseAttestationMode};
21
use alloc::string::String;
22
use alloc::vec::Vec;
23

24
pub const AAGUID_LENGTH: usize = 16;
25

26
pub trait Customization {
27
    /// Authenticator Attestation Global Unique Identifier
28
    fn aaguid(&self) -> &'static [u8; AAGUID_LENGTH];
29

30
    // ###########################################################################
31
    // Constants for adjusting privacy and protection levels.
32
    // ###########################################################################
33

34
    /// Removes support for PIN protocol v1.
35
    ///
36
    /// We support PIN protocol v2, "intended to aid FIPS certification".
37
    /// To certify, you might want to remove support for v1 using this customization.
38
    fn allows_pin_protocol_v1(&self) -> bool;
39

40
    /// Changes the default level for the credProtect extension.
41
    ///
42
    /// You can change this value to one of the following for more privacy:
43
    /// - CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList
44
    /// - CredentialProtectionPolicy::UserVerificationRequired
45
    ///
46
    /// UserVerificationOptionalWithCredentialIdList
47
    /// Resident credentials are discoverable with
48
    /// - an allowList,
49
    /// - an excludeList,
50
    /// - user verification.
51
    ///
52
    /// UserVerificationRequired
53
    /// Resident credentials are discoverable with user verification only.
54
    ///
55
    /// This can improve privacy, but can make usage less comfortable.
56
    fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy>;
57

58
    /// Sets the initial minimum PIN length in code points.
59
    ///
60
    /// # Invariant
61
    ///
62
    /// - The minimum PIN length must be at least 4.
63
    /// - The minimum PIN length must be at most 63.
64
    /// - default_min_pin_length_rp_ids() must be non-empty if max_rp_ids_length() is 0.
65
    ///
66
    /// Requiring longer PINs can help establish trust between users and relying
67
    /// parties. It makes user verification harder to break, but less convenient.
68
    /// NIST recommends at least 6-digit PINs in section 5.1.9.1:
69
    /// https://pages.nist.gov/800-63-3/sp800-63b.html
70
    ///
71
    /// Reset reverts the minimum PIN length to this DEFAULT_MIN_PIN_LENGTH.
72
    fn default_min_pin_length(&self) -> u8;
73

74
    /// Lists relying parties that can read the minimum PIN length.
75
    ///
76
    /// # Invariant
77
    ///
78
    /// - default_min_pin_length_rp_ids() must be non-empty if max_rp_ids_length() is 0
79
    ///
80
    /// Only the RP IDs listed in default_min_pin_length_rp_ids are allowed to read
81
    /// the minimum PIN length with the minPinLength extension.
82
    fn default_min_pin_length_rp_ids(&self) -> Vec<String>;
83

84
    /// Enforces the alwaysUv option.
85
    ///
86
    /// When setting to true, commands require a PIN.
87
    /// Also, alwaysUv can not be disabled by commands.
88
    ///
89
    /// A certification (additional to FIDO Alliance's) might require enforcing
90
    /// alwaysUv. Otherwise, users should have the choice to configure alwaysUv.
91
    /// Calling toggleAlwaysUv is preferred over enforcing alwaysUv here.
92
    fn enforce_always_uv(&self) -> bool;
93

94
    /// Allows usage of enterprise attestation.
95
    ///
96
    /// # Invariant
97
    ///
98
    /// - Enterprise and batch attestation can not both be active.
99
    /// - If the mode is VendorFacilitated, enterprise_attestation_mode() must be non-empty.
100
    ///
101
    /// For privacy reasons, it is disabled by default. You can choose between:
102
    /// - EnterpriseAttestationMode::VendorFacilitated
103
    /// - EnterpriseAttestationMode::PlatformManaged
104
    ///
105
    /// VendorFacilitated
106
    /// Enterprise attestation is restricted to enterprise_attestation_mode(). Add your
107
    /// enterprises domain, e.g. "example.com", to the list below.
108
    ///
109
    /// PlatformManaged
110
    /// All relying parties can request an enterprise attestation. The authenticator
111
    /// trusts the platform to filter requests.
112
    ///
113
    /// To enable the feature, send the subcommand enableEnterpriseAttestation in
114
    /// AuthenticatorConfig. An enterprise might want to customize the type of
115
    /// attestation that is used. OpenSK defaults to batch attestation. Configuring
116
    /// individual certificates then makes authenticators identifiable.
117
    ///
118
    /// OpenSK prevents activating batch and enterprise attestation together. The
119
    /// current implementation uses the same key material at the moment, and these
120
    /// two modes have conflicting privacy guarantees.
121
    /// If you implement your own enterprise attestation mechanism, and you want
122
    /// batch attestation at the same time, proceed carefully and remove the
123
    /// assertion.
124
    fn enterprise_attestation_mode(&self) -> Option<EnterpriseAttestationMode>;
125

126
    /// Lists relying party IDs that can perform enterprise attestation.
127
    ///
128
    /// # Invariant
129
    ///
130
    /// - If the mode is VendorFacilitated, enterprise_attestation_mode() must be non-empty.
131
    ///
132
    /// This list is only considered if enterprise attestation is used.
133
    #[cfg(feature = "std")]
134
    fn enterprise_rp_id_list(&self) -> Vec<String>;
135

136
    /// Returns whether the rp_id is contained in enterprise_rp_id_list().
137
    fn is_enterprise_rp_id(&self, rp_id: &str) -> bool;
138

139
    /// Maximum message size send for CTAP commands.
140
    ///
141
    /// The maximum value is 7609, as HID packets can not encode longer messages.
142
    /// 1024 is the default mentioned in the authenticatorLargeBlobs commands.
143
    /// Larger values are preferred, as that allows more parameters in commands.
144
    /// If long commands are too unreliable on your hardware, consider decreasing
145
    /// this value.
146
    fn max_msg_size(&self) -> usize;
147

148
    /// The number of consecutive failed PINs before blocking interaction.
149
    ///
150
    /// # Invariant
151
    ///
152
    /// - CTAP2.0: Maximum PIN retries must be 8.
153
    /// - CTAP2.1: Maximum PIN retries must be 8 at most.
154
    ///
155
    /// The fail retry counter is reset after entering the correct PIN.
156
    fn max_pin_retries(&self) -> u8;
157

158
    /// Maximum number of UV retries performed before returning an error.
159
    ///
160
    /// Corresponds to maxUvAttemptsForInternalRetries in CTAP 2.1 onwards.
161
    /// When internal retries are allowed, the authenticator will retry UV
162
    /// before communicating its result through CTAP.
163
    ///
164
    /// # Invariant
165
    ///
166
    /// - Must be in the range [1, `max_uv_retries`].
167
    /// - If `preferred_platform_uv_attempts` is not 1, must be in [1, 5].
168
    #[cfg(feature = "fingerprint")]
169
    fn max_uv_attempts_for_internal_retries(&self) -> u8;
170

171
    /// The number of consecutive failed UV attempts before blocking built-in UV.
172
    ///
173
    /// Corresponds to maxUvRetries in CTAP 2.1 onwards.
174
    ///
175
    /// # Invariant
176
    ///
177
    /// - Must be in the range [1, 25].
178
    #[cfg(feature = "fingerprint")]
179
    fn max_uv_retries(&self) -> u8;
180

181
    /// Preferred number of UV attempts before falling back to PIN.
182
    ///
183
    /// Corresponds to preferredPlatformUvAttempts in CTAP 2.1 onwards.
184
    /// States the preference how often the platform should invoke
185
    /// `getPinUvAuthTokenUsingUvWithPermissions` before falling back to
186
    /// `getPinUvAuthTokenUsingPinWithPermissions` or displaying an error.
187
    ///
188
    /// # Invariant
189
    ///
190
    /// - Must be greater than 0.
191
    #[cfg(feature = "fingerprint")]
192
    fn preferred_platform_uv_attempts(&self) -> usize;
193

194
    /// Enables or disables basic attestation for FIDO2.
195
    ///
196
    /// # Invariant
197
    ///
198
    /// - Enterprise and batch attestation can not both be active (see above).
199
    ///
200
    /// The basic attestation uses the signing key configured with a vendor command
201
    /// as a batch key. If you turn batch attestation on, be aware that it is your
202
    /// responsibility to safely generate and store the key material. Also, the
203
    /// batches must have size of at least 100k authenticators before using new key
204
    /// material.
205
    /// U2F is unaffected by this setting.
206
    ///
207
    /// https://www.w3.org/TR/webauthn/#attestation
208
    fn use_batch_attestation(&self) -> bool;
209

210
    /// Enables or disables signature counters.
211
    ///
212
    /// The signature counter is currently implemented as a global counter.
213
    /// The specification strongly suggests to have per-credential counters.
214
    /// Implementing those means you can't have an infinite amount of server-side
215
    /// credentials anymore. Also, since counters need frequent writes on the
216
    /// persistent storage, we might need a flash friendly implementation. This
217
    /// solution is a compromise to be compatible with U2F and not wasting storage.
218
    ///
219
    /// https://www.w3.org/TR/webauthn/#signature-counter
220
    fn use_signature_counter(&self) -> bool;
221

222
    // ###########################################################################
223
    // Constants for performance optimization or adapting to different hardware.
224
    //
225
    // Those constants may be modified before compilation to tune the behavior of
226
    // the key.
227
    // ###########################################################################
228

229
    /// Sets the maximum blob size stored with the credBlob extension.
230
    ///
231
    /// # Invariant
232
    ///
233
    /// - The length must be at least 32.
234
    /// - OpenSK puts a limit that the length must be at most 64, as it needs to
235
    ///   be persisted in the credential ID.
236
    fn max_cred_blob_length(&self) -> usize;
237

238
    /// Limits the number of considered entries in credential lists.
239
    ///
240
    /// # Invariant
241
    ///
242
    /// - This value, if present, must be at least 1 (more is preferred).
243
    ///
244
    /// Depending on your memory, you can use Some(n) to limit request sizes in
245
    /// MakeCredential and GetAssertion. This affects allowList and excludeList.
246
    fn max_credential_count_in_list(&self) -> Option<usize>;
247

248
    /// Limits the size of largeBlobs the authenticator stores.
249
    ///
250
    /// # Invariant
251
    ///
252
    /// - The allowed size must be at least 1024.
253
    /// - The array must fit into the shards reserved in storage/key.rs.
254
    fn max_large_blob_array_size(&self) -> usize;
255

256
    /// Limits the number of RP IDs that can change the minimum PIN length.
257
    ///
258
    /// # Invariant
259
    ///
260
    /// - If this value is 0, default_min_pin_length_rp_ids() must be non-empty.
261
    ///
262
    /// You can use this constant to have an upper limit in storage requirements.
263
    /// This might be useful if you want to more reliably predict the remaining
264
    /// storage. Stored string can still be of arbitrary length though, until RP ID
265
    /// truncation is implemented.
266
    /// Outside of memory considerations, you can set this value to 0 if only RP IDs
267
    /// in default_min_pin_length_rp_ids() should be allowed to change the minimum
268
    /// PIN length.
269
    fn max_rp_ids_length(&self) -> usize;
270

271
    /// Sets the number of resident keys you can store.
272
    ///
273
    /// # Invariant
274
    ///
275
    /// - The storage key CREDENTIALS must fit at least this number of credentials.
276
    ///
277
    /// Limiting the number of resident keys permits to ensure a minimum number of
278
    /// counter increments.
279
    /// Let:
280
    /// - P the number of pages (NUM_PAGES in the board definition)
281
    /// - K the maximum number of resident keys (max_supported_resident_keys())
282
    /// - S the maximum size of a resident key (about 500)
283
    /// - C the number of erase cycles (10000)
284
    /// - I the minimum number of counter increments
285
    ///
286
    /// We have: I = (P * 4084 - 5107 - K * S) / 8 * C
287
    ///
288
    /// With P=20 and K=150, we have I=2M which is enough for 500 increments per day
289
    /// for 10 years.
290
    fn max_supported_resident_keys(&self) -> usize;
291

292
    /// Maximum size of a friendly name for a UV template.
293
    ///
294
    /// This value limits storage requirements for fingerprints.
295
    #[cfg(feature = "fingerprint")]
296
    fn max_template_friendly_name(&self) -> usize;
297
}
298

299
#[derive(Clone)]
300
pub struct CustomizationImpl {
301
    pub aaguid: &'static [u8; AAGUID_LENGTH],
302
    pub allows_pin_protocol_v1: bool,
303
    pub default_cred_protect: Option<CredentialProtectionPolicy>,
304
    pub default_min_pin_length: u8,
305
    pub default_min_pin_length_rp_ids: &'static [&'static str],
306
    pub enforce_always_uv: bool,
307
    pub enterprise_attestation_mode: Option<EnterpriseAttestationMode>,
308
    pub enterprise_rp_id_list: &'static [&'static str],
309
    pub max_msg_size: usize,
310
    pub max_pin_retries: u8,
311
    #[cfg(feature = "fingerprint")]
312
    pub max_uv_attempts_for_internal_retries: u8,
313
    #[cfg(feature = "fingerprint")]
314
    pub max_uv_retries: u8,
315
    #[cfg(feature = "fingerprint")]
316
    pub preferred_platform_uv_attempts: usize,
317
    pub use_batch_attestation: bool,
318
    pub use_signature_counter: bool,
319
    pub max_cred_blob_length: usize,
320
    pub max_credential_count_in_list: Option<usize>,
321
    pub max_large_blob_array_size: usize,
322
    pub max_rp_ids_length: usize,
323
    pub max_supported_resident_keys: usize,
324
    #[cfg(feature = "fingerprint")]
325
    pub max_template_friendly_name: usize,
326
}
327

328
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
329
    aaguid: &[0; AAGUID_LENGTH],
330
    allows_pin_protocol_v1: true,
331
    default_cred_protect: None,
332
    default_min_pin_length: 4,
333
    default_min_pin_length_rp_ids: &[],
334
    enforce_always_uv: false,
335
    enterprise_attestation_mode: None,
336
    enterprise_rp_id_list: &[],
337
    max_msg_size: 7609,
338
    max_pin_retries: 8,
339
    #[cfg(feature = "fingerprint")]
340
    max_uv_attempts_for_internal_retries: 1,
341
    #[cfg(feature = "fingerprint")]
342
    max_uv_retries: 8,
343
    #[cfg(feature = "fingerprint")]
344
    preferred_platform_uv_attempts: 1,
345
    use_batch_attestation: false,
346
    use_signature_counter: true,
347
    max_cred_blob_length: 32,
348
    max_credential_count_in_list: None,
349
    max_large_blob_array_size: 2048,
350
    max_rp_ids_length: 8,
351
    max_supported_resident_keys: 150,
352
    #[cfg(feature = "fingerprint")]
353
    max_template_friendly_name: 64,
354
};
355

356
impl Customization for CustomizationImpl {
357
    fn aaguid(&self) -> &'static [u8; AAGUID_LENGTH] {
2✔
358
        self.aaguid
2✔
359
    }
2✔
360

361
    fn allows_pin_protocol_v1(&self) -> bool {
2✔
362
        self.allows_pin_protocol_v1
2✔
363
    }
2✔
364

365
    fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy> {
2✔
366
        self.default_cred_protect
2✔
367
    }
2✔
368

369
    fn default_min_pin_length(&self) -> u8 {
6✔
370
        self.default_min_pin_length
6✔
371
    }
6✔
372

373
    fn default_min_pin_length_rp_ids(&self) -> Vec<String> {
2✔
374
        self.default_min_pin_length_rp_ids
2✔
375
            .iter()
2✔
376
            .map(|s| String::from(*s))
3✔
377
            .collect()
2✔
378
    }
2✔
379

380
    fn enforce_always_uv(&self) -> bool {
2✔
381
        self.enforce_always_uv
2✔
382
    }
2✔
383

384
    fn enterprise_attestation_mode(&self) -> Option<EnterpriseAttestationMode> {
6✔
385
        self.enterprise_attestation_mode
6✔
386
    }
6✔
387

388
    #[cfg(feature = "std")]
389
    fn enterprise_rp_id_list(&self) -> Vec<String> {
4✔
390
        self.enterprise_rp_id_list
4✔
391
            .iter()
4✔
392
            .map(|s| String::from(*s))
4✔
393
            .collect()
4✔
394
    }
4✔
395

396
    fn is_enterprise_rp_id(&self, rp_id: &str) -> bool {
×
397
        self.enterprise_rp_id_list.contains(&rp_id)
×
398
    }
×
399

400
    fn max_msg_size(&self) -> usize {
6✔
401
        self.max_msg_size
6✔
402
    }
6✔
403

404
    fn max_pin_retries(&self) -> u8 {
4✔
405
        self.max_pin_retries
4✔
406
    }
4✔
407

408
    #[cfg(feature = "fingerprint")]
409
    fn max_uv_attempts_for_internal_retries(&self) -> u8 {
6✔
410
        self.max_uv_attempts_for_internal_retries
6✔
411
    }
6✔
412

413
    #[cfg(feature = "fingerprint")]
414
    fn max_uv_retries(&self) -> u8 {
8✔
415
        self.max_uv_retries
8✔
416
    }
8✔
417

418
    #[cfg(feature = "fingerprint")]
419
    fn preferred_platform_uv_attempts(&self) -> usize {
6✔
420
        self.preferred_platform_uv_attempts
6✔
421
    }
6✔
422

423
    fn use_batch_attestation(&self) -> bool {
4✔
424
        self.use_batch_attestation
4✔
425
    }
4✔
426

427
    fn use_signature_counter(&self) -> bool {
2✔
428
        self.use_signature_counter
2✔
429
    }
2✔
430

431
    fn max_cred_blob_length(&self) -> usize {
6✔
432
        self.max_cred_blob_length
6✔
433
    }
6✔
434

435
    fn max_credential_count_in_list(&self) -> Option<usize> {
4✔
436
        self.max_credential_count_in_list
4✔
437
    }
4✔
438

439
    fn max_large_blob_array_size(&self) -> usize {
4✔
440
        self.max_large_blob_array_size
4✔
441
    }
4✔
442

443
    fn max_rp_ids_length(&self) -> usize {
4✔
444
        self.max_rp_ids_length
4✔
445
    }
4✔
446

447
    fn max_supported_resident_keys(&self) -> usize {
2✔
448
        self.max_supported_resident_keys
2✔
449
    }
2✔
450

451
    #[cfg(feature = "fingerprint")]
452
    fn max_template_friendly_name(&self) -> usize {
2✔
453
        self.max_template_friendly_name
2✔
454
    }
2✔
455
}
456

457
#[cfg(feature = "std")]
458
pub fn is_valid(customization: &impl Customization) -> bool {
6✔
459
    // Two invariants are currently tested in different files:
460
    // - storage.rs: if max_large_blob_array_size() fits the shards
461
    // - storage/key.rs: if max_supported_resident_keys() fits CREDENTIALS
462

463
    // Max message size must be between 1024 and 7609.
464
    if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
6✔
465
        return false;
×
466
    }
6✔
467

468
    // Default min pin length must be between 4 and 63.
469
    if customization.default_min_pin_length() < 4 || customization.default_min_pin_length() > 63 {
6✔
470
        return false;
×
471
    }
6✔
472

473
    // OpenSK prevents activating batch and enterprise attestation together. The
474
    // current implementation uses the same key material at the moment, and these
475
    // two modes have conflicting privacy guarantees.
476
    if customization.use_batch_attestation()
6✔
477
        && customization.enterprise_attestation_mode().is_some()
×
478
    {
479
        return false;
×
480
    }
6✔
481

482
    // enterprise_rp_id_list() should be non-empty in vendor facilitated mode.
483
    if matches!(
6✔
484
        customization.enterprise_attestation_mode(),
6✔
485
        Some(EnterpriseAttestationMode::VendorFacilitated)
486
    ) && customization.enterprise_rp_id_list().is_empty()
×
487
    {
488
        return false;
×
489
    }
6✔
490

491
    // enterprise_rp_id_list() should be empty without an enterprise attestation mode.
492
    if customization.enterprise_attestation_mode().is_none()
6✔
493
        && !customization.enterprise_rp_id_list().is_empty()
4✔
494
    {
495
        return false;
×
496
    }
6✔
497

498
    // Max pin retries must be less or equal than 8.
499
    if customization.max_pin_retries() > 8 {
6✔
500
        return false;
×
501
    }
6✔
502

503
    // Max cred blob length should be at least 32, and at most 64.
504
    if customization.max_cred_blob_length() < 32 || customization.max_cred_blob_length() > 64 {
6✔
505
        return false;
×
506
    }
6✔
507

508
    // Max credential count in list should be positive if exists.
509
    if let Some(count) = customization.max_credential_count_in_list() {
6✔
510
        if count < 1 {
×
511
            return false;
×
512
        }
×
513
    }
6✔
514

515
    // Max large blob array size should not be less than 1024.
516
    if customization.max_large_blob_array_size() < 1024 {
6✔
517
        return false;
×
518
    }
6✔
519

520
    // Default min pin length rp ids must be non-empty if max rp ids length is 0.
521
    if customization.max_rp_ids_length() == 0
6✔
522
        && customization.default_min_pin_length_rp_ids().is_empty()
×
523
    {
524
        return false;
×
525
    }
6✔
526

527
    #[cfg(feature = "fingerprint")]
528
    {
529
        // Maximum UV retries must be in range [1, 25].
530
        if customization.max_uv_retries() < 1 || customization.max_uv_retries() > 25 {
6✔
UNCOV
531
            return false;
×
532
        }
6✔
533

534
        // Maximum internal UV attemps must be in range [1, `max_uv_retries`].
535
        // If preferred platform UV attemps is not 1, must additionally be in [1, 5].
536
        if customization.max_uv_attempts_for_internal_retries() < 1
6✔
537
            || customization.max_uv_attempts_for_internal_retries() > customization.max_uv_retries()
6✔
538
        {
UNCOV
539
            return false;
×
540
        }
6✔
541
        if customization.preferred_platform_uv_attempts() != 1
6✔
UNCOV
542
            && customization.max_uv_attempts_for_internal_retries() > 5
×
543
        {
UNCOV
544
            return false;
×
545
        }
6✔
546

547
        // Preferred number of UV attempts must be positive.
548
        if customization.preferred_platform_uv_attempts() == 0 {
6✔
UNCOV
549
            return false;
×
550
        }
6✔
551
    }
552

553
    true
6✔
554
}
6✔
555

556
#[cfg(test)]
557
mod test {
558
    use super::*;
559

560
    #[test]
561
    fn test_invariants() {
2✔
562
        assert!(is_valid(&DEFAULT_CUSTOMIZATION));
2✔
563
    }
2✔
564

565
    #[test]
566
    fn test_accessors() {
2✔
567
        let customization = CustomizationImpl {
2✔
568
            aaguid: &[0; AAGUID_LENGTH],
2✔
569
            allows_pin_protocol_v1: true,
2✔
570
            default_cred_protect: None,
2✔
571
            default_min_pin_length: 4,
2✔
572
            default_min_pin_length_rp_ids: &["example.com"],
2✔
573
            enforce_always_uv: false,
2✔
574
            enterprise_attestation_mode: None,
2✔
575
            enterprise_rp_id_list: &[],
2✔
576
            max_msg_size: 7609,
2✔
577
            max_pin_retries: 8,
2✔
578
            #[cfg(feature = "fingerprint")]
2✔
579
            max_uv_attempts_for_internal_retries: 1,
2✔
580
            #[cfg(feature = "fingerprint")]
2✔
581
            max_uv_retries: 8,
2✔
582
            #[cfg(feature = "fingerprint")]
2✔
583
            preferred_platform_uv_attempts: 1,
2✔
584
            use_batch_attestation: true,
2✔
585
            use_signature_counter: true,
2✔
586
            max_cred_blob_length: 32,
2✔
587
            max_credential_count_in_list: Some(3),
2✔
588
            max_large_blob_array_size: 2048,
2✔
589
            max_rp_ids_length: 8,
2✔
590
            max_supported_resident_keys: 150,
2✔
591
            #[cfg(feature = "fingerprint")]
2✔
592
            max_template_friendly_name: 64,
2✔
593
        };
2✔
594
        assert_eq!(customization.aaguid(), &[0; AAGUID_LENGTH]);
2✔
595
        assert!(customization.allows_pin_protocol_v1());
2✔
596
        assert!(customization.default_cred_protect().is_none());
2✔
597
        assert_eq!(customization.default_min_pin_length(), 4);
2✔
598
        assert_eq!(
2✔
599
            customization.default_min_pin_length_rp_ids(),
2✔
600
            vec![String::from("example.com")]
2✔
601
        );
602
        assert!(!customization.enforce_always_uv());
2✔
603
        assert!(customization.enterprise_attestation_mode().is_none());
2✔
604
        assert!(customization.enterprise_rp_id_list().is_empty());
2✔
605
        assert_eq!(customization.max_msg_size(), 7609);
2✔
606
        assert_eq!(customization.max_pin_retries(), 8);
2✔
607
        #[cfg(feature = "fingerprint")]
608
        assert_eq!(customization.max_uv_attempts_for_internal_retries(), 1);
2✔
609
        #[cfg(feature = "fingerprint")]
610
        assert_eq!(customization.max_uv_retries(), 8);
2✔
611
        #[cfg(feature = "fingerprint")]
612
        assert_eq!(customization.preferred_platform_uv_attempts(), 1);
2✔
613
        assert!(customization.use_batch_attestation());
2✔
614
        assert!(customization.use_signature_counter());
2✔
615
        assert_eq!(customization.max_cred_blob_length(), 32);
2✔
616
        assert_eq!(customization.max_credential_count_in_list(), Some(3));
2✔
617
        assert_eq!(customization.max_large_blob_array_size(), 2048);
2✔
618
        assert_eq!(customization.max_rp_ids_length(), 8);
2✔
619
        assert_eq!(customization.max_supported_resident_keys(), 150);
2✔
620
        #[cfg(feature = "fingerprint")]
621
        assert_eq!(customization.max_template_friendly_name(), 64);
2✔
622
    }
2✔
623
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc