passkey_types/u2f/register.rs
1use std::array::TryFromSliceError;
2
3use super::ResponseStatusWords;
4
5/// Request payload to register a new user
6#[derive(Debug)]
7pub struct RegisterRequest {
8 /// SHA256 hash challenge issued by the relying party
9 pub challenge: [u8; 32],
10 /// SHA256 of the application identity
11 pub application: [u8; 32],
12}
13
14impl TryFrom<&[u8]> for RegisterRequest {
15 type Error = TryFromSliceError;
16
17 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
18 Ok(Self {
19 challenge: data[..32].try_into()?,
20 application: data[32..].try_into()?,
21 })
22 }
23}
24
25/// Register response payload
26///
27/// This message is output by the U2F token once it created a new keypair in response to the
28/// registration request message. Note that U2F tokens SHOULD verify user presence before returning
29/// a registration response success message (otherwise they SHOULD return a
30/// test-of-user-presence-required message - see above).
31pub struct RegisterResponse {
32 // Reserved byte, value 0x05 which is added in the `encode` method
33 /// This is the (uncompressed) x,y-representation of a curve point on the P-256 NIST elliptic
34 /// curve. User's new public key
35 pub public_key: PublicKey,
36
37 // Key handle length byte which specifies the length of the key handle (see below). The value is
38 // unsigned (range 0-255)
39 /// This a handle that allows the U2F token to identify the generated key pair. U2F tokens MAY
40 /// wrap the generated private key and the application id it was generated for, and output that
41 /// as the key handle.
42 pub key_handle: Vec<u8>,
43
44 /// This is a certificate in X.509 DER format. Parsing of the X.509 certificate unambiguously
45 /// establishes its ending.
46 pub attestation_certificate: Vec<u8>,
47
48 /// This is a ECDSA signature (on P-256) over the following byte string:
49 /// 1. A byte reserved for future use [1 byte] with the value 0x00.
50 /// 2. The application parameter [32 bytes] from the registration request message.
51 /// 3. The challenge parameter [32 bytes] from the registration request message.
52 /// 4. The above key handle [variable length]. (Note that the key handle length is not included in the signature base string.
53 /// This doesn't cause confusion in the signature base string, since all other parameters in the signature base string are fixed-length.)
54 /// 5. The above user public key [65 bytes].
55 pub signature: Vec<u8>,
56}
57
58/// U2F public key is the concatenation of `0x04 | x | y` where `0x04` signifies ecc uncompressed.
59#[derive(Clone, Copy)]
60pub struct PublicKey {
61 // magic 0x04 byte which is added in the `encode` method
62 /// X coordinate of the ECC public key
63 pub x: [u8; 32],
64 /// Y coordinate of the ECC public key
65 pub y: [u8; 32],
66}
67
68impl RegisterResponse {
69 /// Encode the Response to it's binary format for a successfull response
70 #[expect(clippy::as_conversions)]
71 pub fn encode(self) -> Vec<u8> {
72 [0x05] // Reserved magic byte
73 .into_iter()
74 .chain(self.public_key.encode())
75 .chain([self.key_handle.len() as u8])
76 .chain(self.key_handle)
77 .chain(self.attestation_certificate)
78 .chain(self.signature)
79 .chain(ResponseStatusWords::NoError.as_primitive().to_be_bytes()) // NoError indicates success
80 .collect()
81 }
82}
83
84impl PublicKey {
85 /// Encode a Public key into an iterator
86 pub fn encode(self) -> impl Iterator<Item = u8> {
87 [0x04].into_iter().chain(self.x).chain(self.y)
88 }
89}