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

google / OpenSK / 16751432768

05 Aug 2025 01:27PM UTC coverage: 96.367% (-0.8%) from 97.15%
16751432768

Pull #751

github

web-flow
Merge d006dcd54 into 235a5c235
Pull Request #751: Start measuring coverage in fingerprint code

12787 of 13269 relevant lines covered (96.37%)

10588.01 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✔
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
        {
539
            return false;
×
540
        }
6✔
541
        if customization.preferred_platform_uv_attempts() != 1
6✔
542
            && customization.max_uv_attempts_for_internal_retries() > 5
×
543
        {
544
            return false;
×
545
        }
6✔
546

547
        // Preferred number of UV attempts must be positive.
548
        if customization.preferred_platform_uv_attempts() == 0 {
6✔
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