@@ -80,6 +80,12 @@ pub struct Extension {
80
80
pub hmac_secret : Option < HmacSecretResponse > ,
81
81
}
82
82
83
+ impl Extension {
84
+ fn has_some ( & self ) -> bool {
85
+ self . pin_min_length . is_some ( ) || self . hmac_secret . is_some ( )
86
+ }
87
+ }
88
+
83
89
fn parse_extensions ( input : & [ u8 ] ) -> IResult < & [ u8 ] , Extension , NomError < & [ u8 ] > > {
84
90
serde_to_nom ( input)
85
91
}
@@ -284,23 +290,47 @@ impl<'de> Deserialize<'de> for AuthenticatorData {
284
290
}
285
291
286
292
impl AuthenticatorData {
293
+ // see https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data
294
+ // Authenticator Data
295
+ // Name Length (in bytes)
296
+ // rpIdHash 32
297
+ // flags 1
298
+ // signCount 4
299
+ // attestedCredentialData variable (if present)
300
+ // extensions variable (if present)
287
301
pub fn to_vec ( & self ) -> Result < Vec < u8 > , AuthenticatorError > {
288
302
let mut data = Vec :: new ( ) ;
289
- data. extend ( self . rp_id_hash . 0 ) ;
290
- data. extend ( [ self . flags . bits ( ) ] ) ;
291
- data. extend ( self . counter . to_be_bytes ( ) ) ;
303
+ data. extend ( self . rp_id_hash . 0 ) ; // (1) "rpIDHash", len=32
304
+ data. extend ( [ self . flags . bits ( ) ] ) ; // (2) "flags", len=1 (u8)
305
+ data. extend ( self . counter . to_be_bytes ( ) ) ; // (3) "signCount", len=4, 32-bit unsigned big-endian integer.
292
306
293
- // TODO(baloo ): need to yield credential_data and extensions , but that dependends on flags,
294
- // should we consider another type system?
307
+ // TODO(MS ): Here flags=AT needs to be set , but this data comes from the security device
308
+ // and we (probably?) need to just trust the device to set the right flags
295
309
if let Some ( cred) = & self . credential_data {
296
- data. extend ( cred. aaguid . 0 ) ;
297
- data. extend ( ( cred. credential_id . len ( ) as u16 ) . to_be_bytes ( ) ) ;
298
- data. extend ( & cred. credential_id ) ;
310
+ // see https://www.w3.org/TR/webauthn-2/#sctn-attested-credential-data
311
+ // Attested Credential Data
312
+ // Name Length (in bytes)
313
+ // aaguid 16
314
+ // credentialIdLength 2
315
+ // credentialId L
316
+ // credentialPublicKey variable
317
+ data. extend ( cred. aaguid . 0 ) ; // (1) "aaguid", len=16
318
+ data. extend ( ( cred. credential_id . len ( ) as u16 ) . to_be_bytes ( ) ) ; // (2) "credentialIdLength", len=2, 16-bit unsigned big-endian integer
319
+ data. extend ( & cred. credential_id ) ; // (3) "credentialId", len= see (2)
299
320
data. extend (
321
+ // (4) "credentialPublicKey", len=variable
300
322
& serde_cbor:: to_vec ( & cred. credential_public_key )
301
323
. map_err ( CommandError :: Serializing ) ?,
302
324
) ;
303
325
}
326
+ // TODO(MS): Here flags=ED needs to be set, but this data comes from the security device
327
+ // and we (probably?) need to just trust the device to set the right flags
328
+ if self . extensions . has_some ( ) {
329
+ data. extend (
330
+ // (5) "extensions", len=variable
331
+ & serde_cbor:: to_vec ( & self . extensions ) . map_err ( CommandError :: Serializing ) ?,
332
+ ) ;
333
+ }
304
334
Ok ( data)
305
335
}
306
336
}
@@ -382,35 +412,44 @@ pub enum AttestationStatement {
382
412
// }
383
413
384
414
#[ derive( Debug , Serialize , Deserialize , PartialEq , Eq ) ]
385
- // See https://www.w3.org/TR/webauthn/#fido-u2f-attestation
415
+ // See https://www.w3.org/TR/webauthn-2/#sctn-fido-u2f-attestation
416
+ // u2fStmtFormat = {
417
+ // x5c: [ attestnCert: bytes ],
418
+ // sig: bytes
419
+ // }
386
420
pub struct AttestationStatementFidoU2F {
387
- pub sig : Signature ,
388
-
389
- #[ serde( rename = "x5c" ) ]
390
421
/// Certificate chain in x509 format
391
- pub attestation_cert : Vec < AttestationCertificate > ,
422
+ #[ serde( rename = "x5c" ) ]
423
+ pub attestation_cert : Vec < AttestationCertificate > , // (1) "x5c"
424
+ pub sig : Signature , // (2) "sig"
392
425
}
393
426
394
- #[ allow( dead_code) ] // TODO(MS): Remove me, once we can parse AttestationStatements and use this function
395
427
impl AttestationStatementFidoU2F {
396
428
pub fn new ( cert : & [ u8 ] , signature : & [ u8 ] ) -> Self {
397
429
AttestationStatementFidoU2F {
398
- sig : Signature ( ByteBuf :: from ( signature) ) ,
399
430
attestation_cert : vec ! [ AttestationCertificate ( Vec :: from( cert) ) ] ,
431
+ sig : Signature ( ByteBuf :: from ( signature) ) ,
400
432
}
401
433
}
402
434
}
403
435
404
436
#[ derive( Debug , Serialize , Deserialize , PartialEq , Eq ) ]
405
- // TODO(baloo): there is a couple other options than x5c:
406
- // https://www.w3.org/TR/webauthn/#packed-attestation
437
+ // https://www.w3.org/TR/webauthn-2/#sctn-packed-attestation
438
+ // packedStmtFormat = {
439
+ // alg: COSEAlgorithmIdentifier,
440
+ // sig: bytes,
441
+ // x5c: [ attestnCert: bytes, * (caCert: bytes) ]
442
+ // } //
443
+ // {
444
+ // alg: COSEAlgorithmIdentifier
445
+ // sig: bytes,
446
+ // }
407
447
pub struct AttestationStatementPacked {
408
- pub alg : COSEAlgorithm ,
409
- pub sig : Signature ,
410
-
411
- #[ serde( rename = "x5c" ) ]
448
+ pub alg : COSEAlgorithm , // (1) "alg"
449
+ pub sig : Signature , // (2) "sig"
412
450
/// Certificate chain in x509 format
413
- pub attestation_cert : Vec < AttestationCertificate > ,
451
+ #[ serde( rename = "x5c" , skip_serializing_if = "Vec::is_empty" , default ) ]
452
+ pub attestation_cert : Vec < AttestationCertificate > , // (3) "x5c"
414
453
}
415
454
416
455
#[ derive( Debug , Deserialize , PartialEq , Eq ) ]
@@ -523,31 +562,32 @@ impl Serialize for AttestationObject {
523
562
let map_len = 3 ;
524
563
let mut map = serializer. serialize_map ( Some ( map_len) ) ?;
525
564
526
- let auth_data = self
527
- . auth_data
528
- . to_vec ( )
529
- . map ( serde_cbor:: Value :: Bytes )
530
- . map_err ( |_| SerError :: custom ( "Failed to serialize auth_data" ) ) ?;
531
-
532
565
// CTAP2 canonical CBOR order for these entries is ("fmt", "attStmt", "authData")
533
566
// as strings are sorted by length and then lexically.
567
+ // see https://www.w3.org/TR/webauthn-2/#attestation-object
534
568
match self . att_statement {
535
569
AttestationStatement :: None => {
536
570
// Even with Att None, an empty map is returned in the cbor!
537
- map. serialize_entry ( & "fmt" , & "none" ) ?;
571
+ map. serialize_entry ( & "fmt" , & "none" ) ?; // (1) "fmt"
538
572
let v = serde_cbor:: Value :: Map ( std:: collections:: BTreeMap :: new ( ) ) ;
539
- map. serialize_entry ( & "attStmt" , & v) ?;
573
+ map. serialize_entry ( & "attStmt" , & v) ?; // (2) "attStmt"
540
574
}
541
575
AttestationStatement :: Packed ( ref v) => {
542
- map. serialize_entry ( & "fmt" , & "packed" ) ?;
543
- map. serialize_entry ( & "attStmt" , v) ?;
576
+ map. serialize_entry ( & "fmt" , & "packed" ) ?; // (1) "fmt"
577
+ map. serialize_entry ( & "attStmt" , v) ?; // (2) "attStmt"
544
578
}
545
579
AttestationStatement :: FidoU2F ( ref v) => {
546
- map. serialize_entry ( & "fmt" , & "fido-u2f" ) ?;
547
- map. serialize_entry ( & "attStmt" , v) ?;
580
+ map. serialize_entry ( & "fmt" , & "fido-u2f" ) ?; // (1) "fmt"
581
+ map. serialize_entry ( & "attStmt" , v) ?; // (2) "attStmt"
548
582
}
549
583
}
550
- map. serialize_entry ( & "authData" , & auth_data) ?;
584
+
585
+ let auth_data = self
586
+ . auth_data
587
+ . to_vec ( )
588
+ . map ( serde_cbor:: Value :: Bytes )
589
+ . map_err ( |_| SerError :: custom ( "Failed to serialize auth_data" ) ) ?;
590
+ map. serialize_entry ( & "authData" , & auth_data) ?; // (3) "authData"
551
591
map. end ( )
552
592
}
553
593
}
0 commit comments