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}