Coverage Report

Created: 2025-11-16 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/OpenSK/libraries/opensk/src/ctap/command.rs
Line
Count
Source
1
// Copyright 2019-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::cbor_read;
16
#[cfg(feature = "fingerprint")]
17
use super::data_formats::extract_bool;
18
use super::data_formats::{
19
    extract_array, extract_byte_string, extract_map, extract_text_string, extract_unsigned,
20
    ok_or_missing, ClientPinSubCommand, CoseKey, CredentialManagementSubCommand,
21
    CredentialManagementSubCommandParameters, GetAssertionExtensions, GetAssertionOptions,
22
    MakeCredentialExtensions, MakeCredentialOptions, PinUvAuthProtocol,
23
    PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
24
    PublicKeyCredentialUserEntity,
25
};
26
#[cfg(feature = "config_command")]
27
use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLengthParams};
28
use super::status_code::Ctap2StatusCode;
29
use crate::ctap::status_code::CtapResult;
30
use alloc::string::String;
31
use alloc::vec::Vec;
32
#[cfg(feature = "fuzz")]
33
use arbitrary::Arbitrary;
34
use core::convert::TryFrom;
35
#[cfg(all(test, feature = "fingerprint"))]
36
use enum_iterator::IntoEnumIterator;
37
use sk_cbor as cbor;
38
#[cfg(feature = "fingerprint")]
39
use sk_cbor::cbor_map_options;
40
use sk_cbor::destructure_cbor_map;
41
42
// CTAP specification (version 20190130) section 6.1
43
#[derive(Debug, PartialEq, Eq)]
44
#[allow(clippy::enum_variant_names)]
45
pub enum Command {
46
    AuthenticatorMakeCredential(AuthenticatorMakeCredentialParameters),
47
    AuthenticatorGetAssertion(AuthenticatorGetAssertionParameters),
48
    AuthenticatorGetInfo,
49
    AuthenticatorClientPin(AuthenticatorClientPinParameters),
50
    AuthenticatorReset,
51
    AuthenticatorGetNextAssertion,
52
    #[cfg(feature = "fingerprint")]
53
    AuthenticatorBioEnrollment(AuthenticatorBioEnrollmentParameters),
54
    AuthenticatorCredentialManagement(AuthenticatorCredentialManagementParameters),
55
    AuthenticatorSelection,
56
    AuthenticatorLargeBlobs(AuthenticatorLargeBlobsParameters),
57
    #[cfg(feature = "config_command")]
58
    AuthenticatorConfig(AuthenticatorConfigParameters),
59
}
60
61
impl Command {
62
    const AUTHENTICATOR_MAKE_CREDENTIAL: u8 = 0x01;
63
    const AUTHENTICATOR_GET_ASSERTION: u8 = 0x02;
64
    const AUTHENTICATOR_GET_INFO: u8 = 0x04;
65
    const AUTHENTICATOR_CLIENT_PIN: u8 = 0x06;
66
    const AUTHENTICATOR_RESET: u8 = 0x07;
67
    const AUTHENTICATOR_GET_NEXT_ASSERTION: u8 = 0x08;
68
    #[cfg(feature = "fingerprint")]
69
    const AUTHENTICATOR_BIO_ENROLLMENT: u8 = 0x09;
70
    const AUTHENTICATOR_CREDENTIAL_MANAGEMENT: u8 = 0x0A;
71
    const AUTHENTICATOR_SELECTION: u8 = 0x0B;
72
    const AUTHENTICATOR_LARGE_BLOBS: u8 = 0x0C;
73
    #[cfg(feature = "config_command")]
74
    const AUTHENTICATOR_CONFIG: u8 = 0x0D;
75
    const _AUTHENTICATOR_VENDOR_FIRST: u8 = 0x40;
76
    // This commands is the same as AUTHENTICATOR_CREDENTIAL_MANAGEMENT but is duplicated as a
77
    // vendor command for legacy and compatibility reasons. See
78
    // https://github.com/Yubico/libfido2/issues/628 for more information.
79
    const AUTHENTICATOR_VENDOR_CREDENTIAL_MANAGEMENT: u8 = 0x41;
80
    const _AUTHENTICATOR_VENDOR_LAST: u8 = 0xBF;
81
82
8.48k
    pub fn deserialize(bytes: &[u8]) -> CtapResult<Command> {
83
8.48k
        if bytes.is_empty() {
84
            // The error to return is not specified, missing parameter seems to fit best.
85
242
            return Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER);
86
8.24k
        }
87
88
8.24k
        let command_value = bytes[0];
89
8.24k
        match command_value {
90
            Command::AUTHENTICATOR_MAKE_CREDENTIAL => {
91
2.22k
                let decoded_cbor = cbor_read(&bytes[1..])?;
92
                Ok(Command::AuthenticatorMakeCredential(
93
2.03k
                    AuthenticatorMakeCredentialParameters::try_from(decoded_cbor)?,
94
                ))
95
            }
96
            Command::AUTHENTICATOR_GET_ASSERTION => {
97
1.61k
                let decoded_cbor = cbor_read(&bytes[1..])?;
98
                Ok(Command::AuthenticatorGetAssertion(
99
1.41k
                    AuthenticatorGetAssertionParameters::try_from(decoded_cbor)?,
100
                ))
101
            }
102
            Command::AUTHENTICATOR_GET_INFO => {
103
                // Parameters are ignored.
104
45
                Ok(Command::AuthenticatorGetInfo)
105
            }
106
            Command::AUTHENTICATOR_CLIENT_PIN => {
107
2.09k
                let decoded_cbor = cbor_read(&bytes[1..])?;
108
                Ok(Command::AuthenticatorClientPin(
109
1.95k
                    AuthenticatorClientPinParameters::try_from(decoded_cbor)?,
110
                ))
111
            }
112
            Command::AUTHENTICATOR_RESET => {
113
                // Parameters are ignored.
114
33
                Ok(Command::AuthenticatorReset)
115
            }
116
            Command::AUTHENTICATOR_GET_NEXT_ASSERTION => {
117
                // Parameters are ignored.
118
2
                Ok(Command::AuthenticatorGetNextAssertion)
119
            }
120
            #[cfg(feature = "fingerprint")]
121
            Command::AUTHENTICATOR_BIO_ENROLLMENT => {
122
                let decoded_cbor = cbor_read(&bytes[1..])?;
123
                Ok(Command::AuthenticatorBioEnrollment(
124
                    AuthenticatorBioEnrollmentParameters::try_from(decoded_cbor)?,
125
                ))
126
            }
127
            Command::AUTHENTICATOR_CREDENTIAL_MANAGEMENT
128
            | Command::AUTHENTICATOR_VENDOR_CREDENTIAL_MANAGEMENT => {
129
522
                let decoded_cbor = cbor_read(&bytes[1..])?;
130
                Ok(Command::AuthenticatorCredentialManagement(
131
383
                    AuthenticatorCredentialManagementParameters::try_from(decoded_cbor)?,
132
                ))
133
            }
134
            Command::AUTHENTICATOR_SELECTION => {
135
                // Parameters are ignored.
136
1
                Ok(Command::AuthenticatorSelection)
137
            }
138
            Command::AUTHENTICATOR_LARGE_BLOBS => {
139
971
                let decoded_cbor = cbor_read(&bytes[1..])?;
140
                Ok(Command::AuthenticatorLargeBlobs(
141
815
                    AuthenticatorLargeBlobsParameters::try_from(decoded_cbor)?,
142
                ))
143
            }
144
            #[cfg(feature = "config_command")]
145
            Command::AUTHENTICATOR_CONFIG => {
146
739
                let decoded_cbor = cbor_read(&bytes[1..])?;
147
                Ok(Command::AuthenticatorConfig(
148
506
                    AuthenticatorConfigParameters::try_from(decoded_cbor)?,
149
                ))
150
            }
151
1
            _ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND),
152
        }
153
8.48k
    }
154
}
155
156
#[derive(Clone, Debug, PartialEq, Eq)]
157
0
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorMakeCredentialParameters as arbitrary::Arbitrary>::arbitrary_take_rest
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorMakeCredentialParameters as arbitrary::Arbitrary>::arbitrary
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorMakeCredentialParameters as arbitrary::Arbitrary>::shrink::{closure#0}
158
pub struct AuthenticatorMakeCredentialParameters {
159
    pub client_data_hash: Vec<u8>,
160
    pub rp: PublicKeyCredentialRpEntity,
161
    pub user: PublicKeyCredentialUserEntity,
162
    pub pub_key_cred_params: Vec<PublicKeyCredentialParameter>,
163
    pub exclude_list: Option<Vec<PublicKeyCredentialDescriptor>>,
164
    // Extensions are optional, but we can use defaults for all missing fields.
165
    pub extensions: MakeCredentialExtensions,
166
    // Same for options, use defaults when not present.
167
    pub options: MakeCredentialOptions,
168
    pub pin_uv_auth_param: Option<Vec<u8>>,
169
    pub pin_uv_auth_protocol: Option<PinUvAuthProtocol>,
170
    pub enterprise_attestation: Option<u64>,
171
}
172
173
impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
174
    type Error = Ctap2StatusCode;
175
176
4.76k
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
177
4.61k
        destructure_cbor_map! {
178
            let {
179
                0x01 => client_data_hash,
180
                0x02 => rp,
181
                0x03 => user,
182
                0x04 => cred_param_vec,
183
                0x05 => exclude_list,
184
                0x06 => extensions,
185
                0x07 => options,
186
                0x08 => pin_uv_auth_param,
187
                0x09 => pin_uv_auth_protocol,
188
                0x0A => enterprise_attestation,
189
4.76k
            } = extract_map(cbor_value)?;
190
        }
191
192
4.61k
        let client_data_hash = extract_byte_string(ok_or_missing(client_data_hash)?)?;
193
4.20k
        let rp = PublicKeyCredentialRpEntity::try_from(ok_or_missing(rp)?)?;
194
4.09k
        let user = PublicKeyCredentialUserEntity::try_from(ok_or_missing(user)?)?;
195
196
4.03k
        let cred_param_vec = extract_array(ok_or_missing(cred_param_vec)?)?;
197
3.99k
        let pub_key_cred_params = cred_param_vec
198
3.99k
            .into_iter()
199
3.99k
            .map(PublicKeyCredentialParameter::try_from)
200
3.99k
            .collect::<CtapResult<Vec<PublicKeyCredentialParameter>>>()?;
201
202
3.84k
        let exclude_list = match exclude_list {
203
485
            Some(entry) => {
204
485
                let exclude_list_vec = extract_array(entry)?;
205
480
                let exclude_list = exclude_list_vec
206
480
                    .into_iter()
207
480
                    .map(PublicKeyCredentialDescriptor::try_from)
208
480
                    .collect::<CtapResult<Vec<PublicKeyCredentialDescriptor>>>()?;
209
341
                Some(exclude_list)
210
            }
211
3.36k
            None => None,
212
        };
213
214
3.70k
        let extensions = extensions
215
3.70k
            .map(MakeCredentialExtensions::try_from)
216
3.70k
            .transpose()?
217
3.39k
            .unwrap_or_default();
218
219
3.39k
        let options = options
220
3.39k
            .map(MakeCredentialOptions::try_from)
221
3.39k
            .transpose()?
222
3.32k
            .unwrap_or_default();
223
224
3.32k
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
225
3.31k
        let pin_uv_auth_protocol = pin_uv_auth_protocol
226
3.31k
            .map(PinUvAuthProtocol::try_from)
227
3.31k
            .transpose()?;
228
        // We don't convert into EnterpriseAttestationMode to maintain the correct order of errors.
229
3.30k
        let enterprise_attestation = enterprise_attestation.map(extract_unsigned).transpose()?;
230
231
3.29k
        Ok(AuthenticatorMakeCredentialParameters {
232
3.29k
            client_data_hash,
233
3.29k
            rp,
234
3.29k
            user,
235
3.29k
            pub_key_cred_params,
236
3.29k
            exclude_list,
237
3.29k
            extensions,
238
3.29k
            options,
239
3.29k
            pin_uv_auth_param,
240
3.29k
            pin_uv_auth_protocol,
241
3.29k
            enterprise_attestation,
242
3.29k
        })
243
4.76k
    }
244
}
245
246
#[derive(Debug, PartialEq, Eq)]
247
0
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorGetAssertionParameters as arbitrary::Arbitrary>::arbitrary_take_rest
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorGetAssertionParameters as arbitrary::Arbitrary>::arbitrary
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorGetAssertionParameters as arbitrary::Arbitrary>::shrink::{closure#0}
248
pub struct AuthenticatorGetAssertionParameters {
249
    pub rp_id: String,
250
    pub client_data_hash: Vec<u8>,
251
    pub allow_list: Option<Vec<PublicKeyCredentialDescriptor>>,
252
    // Extensions are optional, but we can use defaults for all missing fields.
253
    pub extensions: GetAssertionExtensions,
254
    // Same for options, use defaults when not present.
255
    pub options: GetAssertionOptions,
256
    pub pin_uv_auth_param: Option<Vec<u8>>,
257
    pub pin_uv_auth_protocol: Option<PinUvAuthProtocol>,
258
}
259
260
impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
261
    type Error = Ctap2StatusCode;
262
263
3.50k
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
264
3.35k
        destructure_cbor_map! {
265
            let {
266
                0x01 => rp_id,
267
                0x02 => client_data_hash,
268
                0x03 => allow_list,
269
                0x04 => extensions,
270
                0x05 => options,
271
                0x06 => pin_uv_auth_param,
272
                0x07 => pin_uv_auth_protocol,
273
3.50k
            } = extract_map(cbor_value)?;
274
        }
275
276
3.35k
        let rp_id = extract_text_string(ok_or_missing(rp_id)?)?;
277
3.03k
        let client_data_hash = extract_byte_string(ok_or_missing(client_data_hash)?)?;
278
279
3.02k
        let allow_list = match allow_list {
280
1.02k
            Some(entry) => {
281
1.02k
                let allow_list_vec = extract_array(entry)?;
282
1.01k
                let allow_list = allow_list_vec
283
1.01k
                    .into_iter()
284
1.01k
                    .map(PublicKeyCredentialDescriptor::try_from)
285
1.01k
                    .collect::<CtapResult<Vec<PublicKeyCredentialDescriptor>>>()?;
286
767
                Some(allow_list)
287
            }
288
1.99k
            None => None,
289
        };
290
291
2.76k
        let extensions = extensions
292
2.76k
            .map(GetAssertionExtensions::try_from)
293
2.76k
            .transpose()?
294
2.51k
            .unwrap_or_default();
295
296
2.51k
        let options = options
297
2.51k
            .map(GetAssertionOptions::try_from)
298
2.51k
            .transpose()?
299
2.44k
            .unwrap_or_default();
300
301
2.44k
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
302
2.42k
        let pin_uv_auth_protocol = pin_uv_auth_protocol
303
2.42k
            .map(PinUvAuthProtocol::try_from)
304
2.42k
            .transpose()?;
305
306
2.40k
        Ok(AuthenticatorGetAssertionParameters {
307
2.40k
            rp_id,
308
2.40k
            client_data_hash,
309
2.40k
            allow_list,
310
2.40k
            extensions,
311
2.40k
            options,
312
2.40k
            pin_uv_auth_param,
313
2.40k
            pin_uv_auth_protocol,
314
2.40k
        })
315
3.50k
    }
316
}
317
318
#[derive(Clone, Debug, PartialEq, Eq)]
319
0
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorClientPinParameters as arbitrary::Arbitrary>::arbitrary_take_rest
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorClientPinParameters as arbitrary::Arbitrary>::arbitrary
Unexecuted instantiation: <opensk::ctap::command::AuthenticatorClientPinParameters as arbitrary::Arbitrary>::shrink::{closure#0}
320
pub struct AuthenticatorClientPinParameters {
321
    pub pin_uv_auth_protocol: PinUvAuthProtocol,
322
    pub sub_command: ClientPinSubCommand,
323
    pub key_agreement: Option<CoseKey>,
324
    pub pin_uv_auth_param: Option<Vec<u8>>,
325
    pub new_pin_enc: Option<Vec<u8>>,
326
    pub pin_hash_enc: Option<Vec<u8>>,
327
    pub permissions: Option<u8>,
328
    pub permissions_rp_id: Option<String>,
329
}
330
331
impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
332
    type Error = Ctap2StatusCode;
333
334
3.98k
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
335
3.83k
        destructure_cbor_map! {
336
            let {
337
                0x01 => pin_uv_auth_protocol,
338
                0x02 => sub_command,
339
                0x03 => key_agreement,
340
                0x04 => pin_uv_auth_param,
341
                0x05 => new_pin_enc,
342
                0x06 => pin_hash_enc,
343
                0x09 => permissions,
344
                0x0A => permissions_rp_id,
345
3.98k
            } = extract_map(cbor_value)?;
346
        }
347
348
3.53k
        let pin_uv_auth_protocol =
349
3.83k
            PinUvAuthProtocol::try_from(ok_or_missing(pin_uv_auth_protocol)?)?;
350
3.53k
        let sub_command = ClientPinSubCommand::try_from(ok_or_missing(sub_command)?)?;
351
3.36k
        let key_agreement = key_agreement.map(CoseKey::try_from).transpose()?;
352
3.18k
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
353
3.18k
        let new_pin_enc = new_pin_enc.map(extract_byte_string).transpose()?;
354
3.17k
        let pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
355
        // We expect a bit field of 8 bits, and drop everything else.
356
        // This means we ignore extensions in future versions.
357
3.16k
        let permissions = permissions
358
3.16k
            .map(extract_unsigned)
359
3.16k
            .transpose()?
360
3.13k
            .map(|p| p as u8);
361
3.13k
        let permissions_rp_id = permissions_rp_id.map(extract_text_string).transpose()?;
362
363
3.11k
        Ok(AuthenticatorClientPinParameters {
364
3.11k
            pin_uv_auth_protocol,
365
3.11k
            sub_command,
366
3.11k
            key_agreement,
367
3.11k
            pin_uv_auth_param,
368
3.11k
            new_pin_enc,
369
3.11k
            pin_hash_enc,
370
3.11k
            permissions,
371
3.11k
            permissions_rp_id,
372
3.11k
        })
373
3.98k
    }
374
}
375
376
#[cfg(feature = "fingerprint")]
377
#[derive(Clone, Debug, PartialEq, Eq)]
378
pub struct AuthenticatorBioEnrollmentParameters {
379
    pub modality: Option<u64>,
380
    pub sub_command: Option<BioEnrollmentSubCommand>,
381
    pub sub_command_params: Option<BioEnrollmentSubCommandParams>,
382
    pub pin_uv_auth_protocol: Option<PinUvAuthProtocol>,
383
    pub pin_uv_auth_param: Option<Vec<u8>>,
384
    pub get_modality: Option<bool>,
385
}
386
387
#[cfg(feature = "fingerprint")]
388
impl TryFrom<cbor::Value> for AuthenticatorBioEnrollmentParameters {
389
    type Error = Ctap2StatusCode;
390
391
    fn try_from(cbor_value: cbor::Value) -> Result<Self, Self::Error> {
392
        destructure_cbor_map! {
393
            let {
394
                0x01 => modality,
395
                0x02 => sub_command,
396
                0x03 => sub_command_params,
397
                0x04 => pin_uv_auth_protocol,
398
                0x05 => pin_uv_auth_param,
399
                0x06 => get_modality,
400
            } = extract_map(cbor_value)?;
401
        }
402
403
        let modality = modality.map(extract_unsigned).transpose()?;
404
        let sub_command = sub_command
405
            .map(BioEnrollmentSubCommand::try_from)
406
            .transpose()?;
407
        let sub_command_params = sub_command_params
408
            .map(BioEnrollmentSubCommandParams::try_from)
409
            .transpose()?;
410
        let pin_uv_auth_protocol = pin_uv_auth_protocol
411
            .map(PinUvAuthProtocol::try_from)
412
            .transpose()?;
413
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
414
        let get_modality = get_modality.map(extract_bool).transpose()?;
415
416
        Ok(AuthenticatorBioEnrollmentParameters {
417
            modality,
418
            sub_command,
419
            sub_command_params,
420
            pin_uv_auth_protocol,
421
            pin_uv_auth_param,
422
            get_modality,
423
        })
424
    }
425
}
426
427
#[cfg(feature = "fingerprint")]
428
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
429
#[cfg_attr(test, derive(IntoEnumIterator))]
430
pub enum BioEnrollmentSubCommand {
431
    EnrollBegin = 0x01,
432
    EnrollCaptureNextSample = 0x02,
433
    CancelCurrentEnrollment = 0x03,
434
    EnumerateEnrollments = 0x04,
435
    SetFriendlyName = 0x05,
436
    RemoveEnrollment = 0x06,
437
    GetFingerprintSensorInfo = 0x07,
438
}
439
440
#[cfg(feature = "fingerprint")]
441
impl From<BioEnrollmentSubCommand> for cbor::Value {
442
    fn from(subcommand: BioEnrollmentSubCommand) -> Self {
443
        (subcommand as u64).into()
444
    }
445
}
446
447
#[cfg(feature = "fingerprint")]
448
impl TryFrom<cbor::Value> for BioEnrollmentSubCommand {
449
    type Error = Ctap2StatusCode;
450
451
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
452
        let subcommand_int = extract_unsigned(cbor_value)?;
453
        match subcommand_int {
454
            0x01 => Ok(BioEnrollmentSubCommand::EnrollBegin),
455
            0x02 => Ok(BioEnrollmentSubCommand::EnrollCaptureNextSample),
456
            0x03 => Ok(BioEnrollmentSubCommand::CancelCurrentEnrollment),
457
            0x04 => Ok(BioEnrollmentSubCommand::EnumerateEnrollments),
458
            0x05 => Ok(BioEnrollmentSubCommand::SetFriendlyName),
459
            0x06 => Ok(BioEnrollmentSubCommand::RemoveEnrollment),
460
            0x07 => Ok(BioEnrollmentSubCommand::GetFingerprintSensorInfo),
461
            _ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND),
462
        }
463
    }
464
}
465
466
#[cfg(feature = "fingerprint")]
467
#[derive(Clone, Debug, PartialEq, Eq)]
468
pub struct BioEnrollmentSubCommandParams {
469
    pub template_id: Option<Vec<u8>>,
470
    pub template_friendly_name: Option<String>,
471
    pub timeout_milliseconds: Option<usize>,
472
}
473
474
#[cfg(feature = "fingerprint")]
475
impl TryFrom<cbor::Value> for BioEnrollmentSubCommandParams {
476
    type Error = Ctap2StatusCode;
477
478
    fn try_from(cbor_value: cbor::Value) -> Result<Self, Self::Error> {
479
        destructure_cbor_map! {
480
            let {
481
                0x01 => template_id,
482
                0x02 => template_friendly_name,
483
                0x03 => timeout_milliseconds,
484
            } = extract_map(cbor_value)?;
485
        }
486
487
        let template_id = template_id.map(extract_byte_string).transpose()?;
488
        let template_friendly_name = template_friendly_name
489
            .map(extract_text_string)
490
            .transpose()?;
491
        let timeout_milliseconds = timeout_milliseconds
492
            .map(extract_unsigned)
493
            .transpose()?
494
            .map(usize::try_from)
495
            .transpose()
496
            .map_err(|_| Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?;
497
498
        Ok(BioEnrollmentSubCommandParams {
499
            template_id,
500
            template_friendly_name,
501
            timeout_milliseconds,
502
        })
503
    }
504
}
505
506
#[cfg(feature = "fingerprint")]
507
impl From<BioEnrollmentSubCommandParams> for cbor::Value {
508
    fn from(sub_command_params: BioEnrollmentSubCommandParams) -> Self {
509
        cbor_map_options! {
510
            0x01 => sub_command_params.template_id,
511
            0x02 => sub_command_params.template_friendly_name,
512
            0x03 => sub_command_params.timeout_milliseconds.map(|u| u as u64),
513
        }
514
    }
515
}
516
517
#[derive(Debug, PartialEq, Eq)]
518
pub struct AuthenticatorLargeBlobsParameters {
519
    pub get: Option<usize>,
520
    pub set: Option<Vec<u8>>,
521
    pub offset: usize,
522
    pub length: Option<usize>,
523
    pub pin_uv_auth_param: Option<Vec<u8>>,
524
    pub pin_uv_auth_protocol: Option<PinUvAuthProtocol>,
525
}
526
527
impl TryFrom<cbor::Value> for AuthenticatorLargeBlobsParameters {
528
    type Error = Ctap2StatusCode;
529
530
815
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
531
782
        destructure_cbor_map! {
532
            let {
533
                0x01 => get,
534
                0x02 => set,
535
                0x03 => offset,
536
                0x04 => length,
537
                0x05 => pin_uv_auth_param,
538
                0x06 => pin_uv_auth_protocol,
539
815
            } = extract_map(cbor_value)?;
540
        }
541
542
        // careful: some missing parameters here are CTAP1_ERR_INVALID_PARAMETER
543
782
        let get = get.map(extract_unsigned).transpose()?.map(|u| u as usize);
544
781
        let set = set.map(extract_byte_string).transpose()?;
545
700
        let offset =
546
776
            extract_unsigned(offset.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?)? as usize;
547
700
        let length = length
548
700
            .map(extract_unsigned)
549
700
            .transpose()?
550
697
            .map(|u| u as usize);
551
697
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
552
693
        let pin_uv_auth_protocol = pin_uv_auth_protocol
553
693
            .map(PinUvAuthProtocol::try_from)
554
693
            .transpose()?;
555
556
690
        if get.is_some() == set.is_some() {
557
6
            return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
558
684
        }
559
684
        if get.is_some()
560
333
            && (length.is_some() || pin_uv_auth_param.is_some() || pin_uv_auth_protocol.is_some())
561
        {
562
10
            return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
563
674
        }
564
674
        if set.is_some() && ((offset == 0) != length.is_some()) {
565
2
            return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
566
672
        }
567
568
672
        Ok(AuthenticatorLargeBlobsParameters {
569
672
            get,
570
672
            set,
571
672
            offset,
572
672
            length,
573
672
            pin_uv_auth_param,
574
672
            pin_uv_auth_protocol,
575
672
        })
576
815
    }
577
}
578
579
#[cfg(feature = "config_command")]
580
#[derive(Debug, PartialEq, Eq)]
581
pub struct AuthenticatorConfigParameters {
582
    pub sub_command: ConfigSubCommand,
583
    pub sub_command_params: Option<ConfigSubCommandParams>,
584
    pub pin_uv_auth_protocol: Option<PinUvAuthProtocol>,
585
    pub pin_uv_auth_param: Option<Vec<u8>>,
586
}
587
588
#[cfg(feature = "config_command")]
589
impl TryFrom<cbor::Value> for AuthenticatorConfigParameters {
590
    type Error = Ctap2StatusCode;
591
592
506
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
593
474
        destructure_cbor_map! {
594
            let {
595
                0x01 => sub_command,
596
                0x02 => sub_command_params,
597
                0x03 => pin_uv_auth_protocol,
598
                0x04 => pin_uv_auth_param,
599
506
            } = extract_map(cbor_value)?;
600
        }
601
602
474
        let sub_command = ConfigSubCommand::try_from(ok_or_missing(sub_command)?)?;
603
364
        let sub_command_params = match sub_command {
604
            ConfigSubCommand::SetMinPinLength => Some(ConfigSubCommandParams::SetMinPinLength(
605
344
                SetMinPinLengthParams::try_from(ok_or_missing(sub_command_params)?)?,
606
            )),
607
20
            _ => None,
608
        };
609
247
        let pin_uv_auth_protocol = pin_uv_auth_protocol
610
247
            .map(PinUvAuthProtocol::try_from)
611
247
            .transpose()?;
612
239
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
613
614
231
        Ok(AuthenticatorConfigParameters {
615
231
            sub_command,
616
231
            sub_command_params,
617
231
            pin_uv_auth_protocol,
618
231
            pin_uv_auth_param,
619
231
        })
620
506
    }
621
}
622
623
#[derive(Debug, PartialEq, Eq)]
624
pub struct AuthenticatorCredentialManagementParameters {
625
    pub sub_command: CredentialManagementSubCommand,
626
    pub sub_command_params: Option<CredentialManagementSubCommandParameters>,
627
    pub pin_uv_auth_protocol: Option<PinUvAuthProtocol>,
628
    pub pin_uv_auth_param: Option<Vec<u8>>,
629
}
630
631
impl TryFrom<cbor::Value> for AuthenticatorCredentialManagementParameters {
632
    type Error = Ctap2StatusCode;
633
634
383
    fn try_from(cbor_value: cbor::Value) -> CtapResult<Self> {
635
363
        destructure_cbor_map! {
636
            let {
637
                0x01 => sub_command,
638
                0x02 => sub_command_params,
639
                0x03 => pin_uv_auth_protocol,
640
                0x04 => pin_uv_auth_param,
641
383
            } = extract_map(cbor_value)?;
642
        }
643
644
363
        let sub_command = CredentialManagementSubCommand::try_from(ok_or_missing(sub_command)?)?;
645
239
        let sub_command_params = sub_command_params
646
239
            .map(CredentialManagementSubCommandParameters::try_from)
647
239
            .transpose()?;
648
126
        let pin_uv_auth_protocol = pin_uv_auth_protocol
649
126
            .map(PinUvAuthProtocol::try_from)
650
126
            .transpose()?;
651
119
        let pin_uv_auth_param = pin_uv_auth_param.map(extract_byte_string).transpose()?;
652
653
106
        Ok(AuthenticatorCredentialManagementParameters {
654
106
            sub_command,
655
106
            sub_command_params,
656
106
            pin_uv_auth_protocol,
657
106
            pin_uv_auth_param,
658
106
        })
659
383
    }
660
}
661
662
#[cfg(test)]
663
mod test {
664
    use super::super::data_formats::{
665
        AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
666
        PublicKeyCredentialUserEntity,
667
    };
668
    use super::super::ES256_CRED_PARAM;
669
    use super::*;
670
    #[cfg(feature = "fingerprint")]
671
    use cbor::cbor_int;
672
    use cbor::{cbor_array, cbor_map};
673
674
    #[test]
675
    fn test_from_cbor_make_credential_parameters() {
676
        let cbor_value = cbor_map! {
677
            0x01 => vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F],
678
            0x02 => cbor_map! {
679
                "id" => "example.com",
680
                "icon" => "example.com/icon.png",
681
                "name" => "Example",
682
            },
683
            0x03 => cbor_map! {
684
                "id" => vec![0x1D, 0x1D, 0x1D, 0x1D],
685
                "icon" => "example.com/foo/icon.png",
686
                "name" => "foo",
687
                "displayName" => "bar",
688
            },
689
            0x04 => cbor_array![ES256_CRED_PARAM],
690
            0x05 => cbor_array![],
691
            0x08 => vec![0x12, 0x34],
692
            0x09 => 1,
693
            0x0A => 2,
694
        };
695
        let returned_make_credential_parameters =
696
            AuthenticatorMakeCredentialParameters::try_from(cbor_value).unwrap();
697
698
        let client_data_hash = vec![
699
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
700
            0x0E, 0x0F,
701
        ];
702
        let rp = PublicKeyCredentialRpEntity {
703
            rp_id: "example.com".to_string(),
704
            rp_name: Some("Example".to_string()),
705
            rp_icon: Some("example.com/icon.png".to_string()),
706
        };
707
        let user = PublicKeyCredentialUserEntity {
708
            user_id: vec![0x1D, 0x1D, 0x1D, 0x1D],
709
            user_name: Some("foo".to_string()),
710
            user_display_name: Some("bar".to_string()),
711
            user_icon: Some("example.com/foo/icon.png".to_string()),
712
        };
713
        let options = MakeCredentialOptions {
714
            rk: false,
715
            uv: false,
716
        };
717
        let expected_make_credential_parameters = AuthenticatorMakeCredentialParameters {
718
            client_data_hash,
719
            rp,
720
            user,
721
            pub_key_cred_params: vec![ES256_CRED_PARAM],
722
            exclude_list: Some(vec![]),
723
            extensions: MakeCredentialExtensions::default(),
724
            options,
725
            pin_uv_auth_param: Some(vec![0x12, 0x34]),
726
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
727
            enterprise_attestation: Some(2),
728
        };
729
730
        assert_eq!(
731
            returned_make_credential_parameters,
732
            expected_make_credential_parameters
733
        );
734
    }
735
736
    #[test]
737
    fn test_from_cbor_get_assertion_parameters() {
738
        let cbor_value = cbor_map! {
739
            0x01 => "example.com",
740
            0x02 => vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F],
741
            0x03 => cbor_array![ cbor_map! {
742
                "id" => vec![0x2D, 0x2D, 0x2D, 0x2D],
743
                "type" => "public-key",
744
                "transports" => cbor_array!["usb"],
745
            } ],
746
            0x06 => vec![0x12, 0x34],
747
            0x07 => 1,
748
        };
749
        let returned_get_assertion_parameters =
750
            AuthenticatorGetAssertionParameters::try_from(cbor_value).unwrap();
751
752
        let rp_id = "example.com".to_string();
753
        let client_data_hash = vec![
754
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
755
            0x0E, 0x0F,
756
        ];
757
        let pub_key_cred_descriptor = PublicKeyCredentialDescriptor {
758
            key_type: PublicKeyCredentialType::PublicKey,
759
            key_id: vec![0x2D, 0x2D, 0x2D, 0x2D],
760
            transports: Some(vec![AuthenticatorTransport::Usb]),
761
        };
762
        let options = GetAssertionOptions {
763
            up: true,
764
            uv: false,
765
        };
766
        let expected_get_assertion_parameters = AuthenticatorGetAssertionParameters {
767
            rp_id,
768
            client_data_hash,
769
            allow_list: Some(vec![pub_key_cred_descriptor]),
770
            extensions: GetAssertionExtensions::default(),
771
            options,
772
            pin_uv_auth_param: Some(vec![0x12, 0x34]),
773
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
774
        };
775
776
        assert_eq!(
777
            returned_get_assertion_parameters,
778
            expected_get_assertion_parameters
779
        );
780
    }
781
782
    #[test]
783
    fn test_from_cbor_client_pin_parameters() {
784
        let cose_key = CoseKey::example_ecdh_pubkey();
785
        let cbor_value = cbor_map! {
786
            0x01 => 1,
787
            0x02 => ClientPinSubCommand::GetPinRetries,
788
            0x03 => cbor::Value::from(cose_key.clone()),
789
            0x04 => vec! [0xBB],
790
            0x05 => vec! [0xCC],
791
            0x06 => vec! [0xDD],
792
            0x09 => 0x03,
793
            0x0A => "example.com",
794
        };
795
        let returned_client_pin_parameters =
796
            AuthenticatorClientPinParameters::try_from(cbor_value).unwrap();
797
798
        let expected_client_pin_parameters = AuthenticatorClientPinParameters {
799
            pin_uv_auth_protocol: PinUvAuthProtocol::V1,
800
            sub_command: ClientPinSubCommand::GetPinRetries,
801
            key_agreement: Some(cose_key),
802
            pin_uv_auth_param: Some(vec![0xBB]),
803
            new_pin_enc: Some(vec![0xCC]),
804
            pin_hash_enc: Some(vec![0xDD]),
805
            permissions: Some(0x03),
806
            permissions_rp_id: Some("example.com".to_string()),
807
        };
808
        assert_eq!(
809
            returned_client_pin_parameters,
810
            expected_client_pin_parameters
811
        );
812
    }
813
814
    #[test]
815
    fn test_deserialize_get_info() {
816
        let cbor_bytes = [Command::AUTHENTICATOR_GET_INFO];
817
        let command = Command::deserialize(&cbor_bytes);
818
        assert_eq!(command, Ok(Command::AuthenticatorGetInfo));
819
    }
820
821
    #[test]
822
    fn test_deserialize_reset() {
823
        // Adding some random bytes to see if they are ignored.
824
        let cbor_bytes = [Command::AUTHENTICATOR_RESET, 0xAB, 0xCD, 0xEF];
825
        let command = Command::deserialize(&cbor_bytes);
826
        assert_eq!(command, Ok(Command::AuthenticatorReset));
827
    }
828
829
    #[test]
830
    fn test_deserialize_get_next_assertion() {
831
        let cbor_bytes = [Command::AUTHENTICATOR_GET_NEXT_ASSERTION];
832
        let command = Command::deserialize(&cbor_bytes);
833
        assert_eq!(command, Ok(Command::AuthenticatorGetNextAssertion));
834
    }
835
836
    #[test]
837
    #[cfg(feature = "fingerprint")]
838
    fn test_from_cbor_bio_enrollment_parameters() {
839
        let sub_command_params_cbor_value = cbor_map! {
840
            0x01 => vec![0x01],
841
            0x02 => "Name",
842
            0x03 => 1000,
843
        };
844
        let cbor_value = cbor_map! {
845
            0x01 => 1,
846
            0x02 => BioEnrollmentSubCommand::EnrollBegin,
847
            0x03 => sub_command_params_cbor_value,
848
            0x04 => 1,
849
            0x05 => vec![0xBB],
850
            0x06 => true,
851
        };
852
        let returned_bio_enrollment_parameters =
853
            AuthenticatorBioEnrollmentParameters::try_from(cbor_value).unwrap();
854
855
        let expected_sub_command_params = BioEnrollmentSubCommandParams {
856
            template_id: Some(vec![0x01]),
857
            template_friendly_name: Some(String::from("Name")),
858
            timeout_milliseconds: Some(1000),
859
        };
860
        let expected_bio_enrollment_parameters = AuthenticatorBioEnrollmentParameters {
861
            modality: Some(1),
862
            sub_command: Some(BioEnrollmentSubCommand::EnrollBegin),
863
            sub_command_params: Some(expected_sub_command_params),
864
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
865
            pin_uv_auth_param: Some(vec![0xBB]),
866
            get_modality: Some(true),
867
        };
868
        assert_eq!(
869
            returned_bio_enrollment_parameters,
870
            expected_bio_enrollment_parameters
871
        );
872
    }
873
874
    #[test]
875
    #[cfg(feature = "fingerprint")]
876
    fn test_from_into_bio_enrollment_sub_command() {
877
        let cbor_sub_command: cbor::Value = cbor_int!(0x01);
878
        let sub_command = BioEnrollmentSubCommand::try_from(cbor_sub_command.clone());
879
        let expected_sub_command = BioEnrollmentSubCommand::EnrollBegin;
880
        assert_eq!(sub_command, Ok(expected_sub_command));
881
        let created_cbor: cbor::Value = sub_command.unwrap().into();
882
        assert_eq!(created_cbor, cbor_sub_command);
883
884
        for command in BioEnrollmentSubCommand::into_enum_iter() {
885
            let created_cbor: cbor::Value = command.clone().into();
886
            let reconstructed = BioEnrollmentSubCommand::try_from(created_cbor).unwrap();
887
            assert_eq!(command, reconstructed);
888
        }
889
    }
890
891
    #[test]
892
    #[cfg(feature = "fingerprint")]
893
    fn test_from_into_bio_enrollment_sub_command_params() {
894
        let cbor_sub_command_params = cbor_map! {
895
            0x01 => vec![0x1D; 32],
896
            0x02 => "Name",
897
            0x03 => 30_000,
898
        };
899
        let sub_command_params =
900
            BioEnrollmentSubCommandParams::try_from(cbor_sub_command_params.clone());
901
        let expected_sub_command_params = BioEnrollmentSubCommandParams {
902
            template_id: Some(vec![0x1D; 32]),
903
            template_friendly_name: Some(String::from("Name")),
904
            timeout_milliseconds: Some(30_000),
905
        };
906
        assert_eq!(sub_command_params, Ok(expected_sub_command_params));
907
        let created_cbor: cbor::Value = sub_command_params.unwrap().into();
908
        assert_eq!(created_cbor, cbor_sub_command_params);
909
    }
910
911
    #[test]
912
    #[cfg(feature = "config_command")]
913
    fn test_from_cbor_config_parameters() {
914
        let cbor_value = cbor_map! {
915
            0x01 => ConfigSubCommand::SetMinPinLength as u64,
916
            0x02 => cbor_map!{
917
                0x01 => 6,
918
                0x02 => cbor_array![String::from("example.com")],
919
                0x03 => true,
920
            },
921
            0x03 => 1,
922
            0x04 => vec! [0x9A; 16],
923
        };
924
        let returned_config_parameters =
925
            AuthenticatorConfigParameters::try_from(cbor_value).unwrap();
926
927
        let sub_command_params = ConfigSubCommandParams::SetMinPinLength(SetMinPinLengthParams {
928
            new_min_pin_length: Some(6),
929
            min_pin_length_rp_ids: Some(vec![String::from("example.com")]),
930
            force_change_pin: Some(true),
931
        });
932
        let expected_config_parameters = AuthenticatorConfigParameters {
933
            sub_command: ConfigSubCommand::SetMinPinLength,
934
            sub_command_params: Some(sub_command_params),
935
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
936
            pin_uv_auth_param: Some(vec![0x9A; 16]),
937
        };
938
939
        assert_eq!(returned_config_parameters, expected_config_parameters);
940
    }
941
942
    #[test]
943
    fn test_from_cbor_cred_management_parameters() {
944
        let cbor_value = cbor_map! {
945
            0x01 => CredentialManagementSubCommand::EnumerateCredentialsBegin as u64,
946
            0x02 => cbor_map!{
947
                0x01 => vec![0x1D; 32],
948
            },
949
            0x03 => 1,
950
            0x04 => vec! [0x9A; 16],
951
        };
952
        let returned_cred_management_parameters =
953
            AuthenticatorCredentialManagementParameters::try_from(cbor_value).unwrap();
954
955
        let params = CredentialManagementSubCommandParameters {
956
            rp_id_hash: Some(vec![0x1D; 32]),
957
            credential_id: None,
958
            user: None,
959
        };
960
        let expected_cred_management_parameters = AuthenticatorCredentialManagementParameters {
961
            sub_command: CredentialManagementSubCommand::EnumerateCredentialsBegin,
962
            sub_command_params: Some(params),
963
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
964
            pin_uv_auth_param: Some(vec![0x9A; 16]),
965
        };
966
967
        assert_eq!(
968
            returned_cred_management_parameters,
969
            expected_cred_management_parameters
970
        );
971
    }
972
973
    #[test]
974
    fn test_deserialize_selection() {
975
        let cbor_bytes = [Command::AUTHENTICATOR_SELECTION];
976
        let command = Command::deserialize(&cbor_bytes);
977
        assert_eq!(command, Ok(Command::AuthenticatorSelection));
978
    }
979
980
    #[test]
981
    fn test_from_cbor_large_blobs_parameters() {
982
        const MIN_LARGE_BLOB_LEN: usize = 17;
983
984
        // successful get
985
        let cbor_value = cbor_map! {
986
            0x01 => 2,
987
            0x03 => 4,
988
        };
989
        let returned_large_blobs_parameters =
990
            AuthenticatorLargeBlobsParameters::try_from(cbor_value).unwrap();
991
        let expected_large_blobs_parameters = AuthenticatorLargeBlobsParameters {
992
            get: Some(2),
993
            set: None,
994
            offset: 4,
995
            length: None,
996
            pin_uv_auth_param: None,
997
            pin_uv_auth_protocol: None,
998
        };
999
        assert_eq!(
1000
            returned_large_blobs_parameters,
1001
            expected_large_blobs_parameters
1002
        );
1003
1004
        // successful first set
1005
        let cbor_value = cbor_map! {
1006
            0x02 => vec! [0x5E],
1007
            0x03 => 0,
1008
            0x04 => MIN_LARGE_BLOB_LEN as u64,
1009
            0x05 => vec! [0xA9],
1010
            0x06 => 1,
1011
        };
1012
        let returned_large_blobs_parameters =
1013
            AuthenticatorLargeBlobsParameters::try_from(cbor_value).unwrap();
1014
        let expected_large_blobs_parameters = AuthenticatorLargeBlobsParameters {
1015
            get: None,
1016
            set: Some(vec![0x5E]),
1017
            offset: 0,
1018
            length: Some(MIN_LARGE_BLOB_LEN),
1019
            pin_uv_auth_param: Some(vec![0xA9]),
1020
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
1021
        };
1022
        assert_eq!(
1023
            returned_large_blobs_parameters,
1024
            expected_large_blobs_parameters
1025
        );
1026
1027
        // successful next set
1028
        let cbor_value = cbor_map! {
1029
            0x02 => vec! [0x5E],
1030
            0x03 => 1,
1031
            0x05 => vec! [0xA9],
1032
            0x06 => 1,
1033
        };
1034
        let returned_large_blobs_parameters =
1035
            AuthenticatorLargeBlobsParameters::try_from(cbor_value).unwrap();
1036
        let expected_large_blobs_parameters = AuthenticatorLargeBlobsParameters {
1037
            get: None,
1038
            set: Some(vec![0x5E]),
1039
            offset: 1,
1040
            length: None,
1041
            pin_uv_auth_param: Some(vec![0xA9]),
1042
            pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
1043
        };
1044
        assert_eq!(
1045
            returned_large_blobs_parameters,
1046
            expected_large_blobs_parameters
1047
        );
1048
1049
        // failing with neither get nor set
1050
        let cbor_value = cbor_map! {
1051
            0x03 => 4,
1052
            0x05 => vec! [0xA9],
1053
            0x06 => 1,
1054
        };
1055
        assert_eq!(
1056
            AuthenticatorLargeBlobsParameters::try_from(cbor_value),
1057
            Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
1058
        );
1059
1060
        // failing with get and set
1061
        let cbor_value = cbor_map! {
1062
            0x01 => 2,
1063
            0x02 => vec! [0x5E],
1064
            0x03 => 4,
1065
            0x05 => vec! [0xA9],
1066
            0x06 => 1,
1067
        };
1068
        assert_eq!(
1069
            AuthenticatorLargeBlobsParameters::try_from(cbor_value),
1070
            Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
1071
        );
1072
1073
        // failing with get and length
1074
        let cbor_value = cbor_map! {
1075
            0x01 => 2,
1076
            0x03 => 4,
1077
            0x04 => MIN_LARGE_BLOB_LEN as u64,
1078
            0x05 => vec! [0xA9],
1079
            0x06 => 1,
1080
        };
1081
        assert_eq!(
1082
            AuthenticatorLargeBlobsParameters::try_from(cbor_value),
1083
            Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
1084
        );
1085
1086
        // failing with zero offset and no length present
1087
        let cbor_value = cbor_map! {
1088
            0x02 => vec! [0x5E],
1089
            0x03 => 0,
1090
            0x05 => vec! [0xA9],
1091
            0x06 => 1,
1092
        };
1093
        assert_eq!(
1094
            AuthenticatorLargeBlobsParameters::try_from(cbor_value),
1095
            Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
1096
        );
1097
1098
        // failing with non-zero offset and length present
1099
        let cbor_value = cbor_map! {
1100
            0x02 => vec! [0x5E],
1101
            0x03 => 4,
1102
            0x04 => MIN_LARGE_BLOB_LEN as u64,
1103
            0x05 => vec! [0xA9],
1104
            0x06 => 1,
1105
        };
1106
        assert_eq!(
1107
            AuthenticatorLargeBlobsParameters::try_from(cbor_value),
1108
            Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
1109
        );
1110
    }
1111
}