use crate::ctap2::Flags;
use std::array::TryFromSliceError;
use super::ResponseStatusWords;
#[repr(u8)]
#[derive(Debug)]
pub enum AuthenticationParameter {
CheckOnly = 0x07,
EnforceUserPresence = 0x03,
DontEnforceUserPresence = 0x08,
}
impl From<AuthenticationParameter> for u8 {
#[expect(clippy::as_conversions)]
fn from(src: AuthenticationParameter) -> Self {
src as u8
}
}
impl From<u8> for AuthenticationParameter {
fn from(src: u8) -> Self {
match src {
0x07 => AuthenticationParameter::CheckOnly,
0x03 => AuthenticationParameter::EnforceUserPresence,
0x08 => AuthenticationParameter::DontEnforceUserPresence,
_ => unreachable!("U2F Authentication parameter which is not in the spec"),
}
}
}
#[derive(Debug)]
pub struct AuthenticationRequest {
pub parameter: AuthenticationParameter,
pub challenge: [u8; 32],
pub application: [u8; 32],
pub key_handle: Vec<u8>,
}
impl AuthenticationRequest {
#[expect(clippy::as_conversions)]
pub fn try_from(
data: &[u8],
parameter: impl Into<AuthenticationParameter>,
) -> Result<Self, TryFromSliceError> {
let (challenge, data) = data.split_at(32);
let (application, data) = data.split_at(32);
let (handle_len, data) = data.split_at(1);
let key_handle = data[..handle_len[0] as usize].to_vec();
Ok(Self {
parameter: parameter.into(),
challenge: challenge.try_into()?,
application: application.try_into()?,
key_handle,
})
}
}
pub struct AuthenticationResponse {
pub user_presence: Flags,
pub counter: u32,
pub signature: Vec<u8>,
}
impl AuthenticationResponse {
pub fn encode(self) -> Vec<u8> {
[self.user_presence.into()]
.into_iter()
.chain(self.counter.to_be_bytes())
.chain(self.signature)
.chain(u16::from(ResponseStatusWords::NoError).to_be_bytes()) .collect()
}
}