@@ -347,6 +347,42 @@ mod test {
347347 assert ! ( decrypt_enc_identifier( & token, & [ ] ) . is_err( ) ) ;
348348 }
349349
350+ // Known-answer vector for the encIdentifier construction (CTAP 2.3-PS 6.4). The
351+ // expected key and ciphertext were computed independently of this crate (RFC 5869
352+ // HKDF-SHA-256 via Python hashlib/hmac, AES-128-CBC via openssl), so a self-
353+ // consistent but spec-wrong change to the HKDF salt, info string, output length, or
354+ // cipher is caught here even though the round-trip tests would still pass.
355+ #[ test]
356+ fn enc_identifier_matches_known_answer_vector ( ) {
357+ let token: [ u8 ; 32 ] = [
358+ 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D ,
359+ 0x0E , 0x0F , 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B ,
360+ 0x1C , 0x1D , 0x1E , 0x1F ,
361+ ] ;
362+ let device_identifier: [ u8 ; 16 ] = [
363+ 0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD ,
364+ 0xAE , 0xAF ,
365+ ] ;
366+ // HKDF-SHA-256(salt = 32 zero bytes, IKM = token, L = 16, info = "encIdentifier").
367+ let expected_key: [ u8 ; 16 ] = [
368+ 0x24 , 0x15 , 0x4D , 0xBE , 0x7E , 0xF3 , 0xCE , 0x2D , 0x6A , 0xDD , 0x02 , 0xC4 , 0xE4 , 0x8D ,
369+ 0xBB , 0x69 ,
370+ ] ;
371+ assert_eq ! ( enc_identifier_key( & token) . unwrap( ) , expected_key) ;
372+
373+ // iv (0x30..0x3F) || ct, where ct = AES-128-CBC(expected_key, iv, device_identifier)
374+ // with no padding (the device identifier is exactly one block).
375+ let enc_identifier: [ u8 ; 32 ] = [
376+ 0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D ,
377+ 0x3E , 0x3F , 0xF4 , 0xD6 , 0x82 , 0xA3 , 0x6E , 0x94 , 0x0D , 0x68 , 0xD8 , 0x62 , 0xE3 , 0x09 ,
378+ 0x9C , 0x6C , 0xE9 , 0xB4 ,
379+ ] ;
380+ assert_eq ! (
381+ decrypt_enc_identifier( & token, & enc_identifier) . unwrap( ) ,
382+ device_identifier
383+ ) ;
384+ }
385+
350386 #[ tokio:: test]
351387 async fn recognizes_matching_record ( ) {
352388 let store = MemoryPersistentTokenStore :: new ( ) ;
0 commit comments