1use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
2use chrono::TimeDelta;
3use serde::{Deserialize, Serialize};
4
5use crate::authorization::*;
6use crate::messages::*;
7use crate::register::*;
8use crate::u2ferror::U2fError;
9use crate::util::*;
10
11#[cfg(feature = "oapi-schema")]
12use impulse_server_kit::salvo;
13
14type Result<T> = ::std::result::Result<T, U2fError>;
15
16#[derive(Clone)]
17pub struct U2f {
18 app_id: String,
19}
20
21#[cfg_attr(feature = "oapi-schema", derive(salvo::oapi::ToSchema))]
22#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash, Default)]
23#[serde(rename_all = "camelCase")]
24pub struct Challenge {
25 pub app_id: String,
26 pub challenge: String,
27 pub timestamp: String,
28}
29
30impl U2f {
31 pub fn new(app_id: String) -> Self {
33 U2f { app_id }
34 }
35
36 #[cfg(feature = "rand")]
37 pub fn generate_challenge(&self) -> Challenge {
38 let utc: chrono::DateTime<chrono::Utc> = chrono::Utc::now();
39 let challenge_bytes = generate_challenge(32);
40
41 Challenge {
42 challenge: URL_SAFE_NO_PAD.encode(&challenge_bytes),
43 timestamp: format!("{utc:?}"),
44 app_id: self.app_id.clone(),
45 }
46 }
47
48 pub fn request(&self, challenge: Challenge, registrations: Vec<Registration>) -> Result<U2fRegisterRequest> {
49 let u2f_request = U2fRegisterRequest {
50 app_id: self.app_id.clone(),
51 register_requests: self.register_request(challenge),
52 registered_keys: self.registered_keys(registrations),
53 };
54
55 Ok(u2f_request)
56 }
57
58 fn register_request(&self, challenge: Challenge) -> Vec<RegisterRequest> {
59 let mut requests: Vec<RegisterRequest> = vec![];
60
61 let request = RegisterRequest {
62 version: U2F_V2.into(),
63 challenge: challenge.challenge,
64 };
65 requests.push(request);
66
67 requests
68 }
69
70 pub fn register_response(&self, challenge: Challenge, response: RegisterResponse) -> Result<Registration> {
71 if expiration(challenge.timestamp) > TimeDelta::seconds(300) {
72 return Err(U2fError::ChallengeExpired);
73 }
74
75 let registration_data: Vec<u8> = URL_SAFE_NO_PAD.decode(&response.registration_data[..]).unwrap();
76 let client_data: Vec<u8> = URL_SAFE_NO_PAD.decode(&response.client_data[..]).unwrap();
77
78 parse_registration(challenge.app_id, client_data, registration_data)
79 }
80
81 fn registered_keys(&self, registrations: Vec<Registration>) -> Vec<RegisteredKey> {
82 let mut keys: Vec<RegisteredKey> = vec![];
83
84 for registration in registrations {
85 keys.push(get_registered_key(self.app_id.clone(), registration.key_handle));
86 }
87
88 keys
89 }
90
91 pub fn sign_request(&self, challenge: Challenge, registrations: Vec<Registration>) -> U2fSignRequest {
92 let mut keys: Vec<RegisteredKey> = vec![];
93
94 for registration in registrations {
95 keys.push(get_registered_key(self.app_id.clone(), registration.key_handle));
96 }
97
98 U2fSignRequest {
99 app_id: self.app_id.clone(),
100 challenge: URL_SAFE_NO_PAD.encode(challenge.challenge.as_bytes()),
101 registered_keys: keys,
102 }
103 }
104
105 pub fn sign_response(&self, challenge: Challenge, reg: Registration, sign_resp: SignResponse, counter: u32) -> Result<u32> {
106 if expiration(challenge.timestamp) > TimeDelta::seconds(300) {
107 return Err(U2fError::ChallengeExpired);
108 }
109
110 if sign_resp.key_handle != get_encoded(®.key_handle[..]) {
111 return Err(U2fError::WrongKeyHandler);
112 }
113
114 let client_data: Vec<u8> = URL_SAFE_NO_PAD
115 .decode(&sign_resp.client_data[..])
116 .map_err(|_e| U2fError::InvalidClientData)?;
117 let sign_data: Vec<u8> = URL_SAFE_NO_PAD
118 .decode(&sign_resp.signature_data[..])
119 .map_err(|_e| U2fError::InvalidSignatureData)?;
120
121 let public_key = reg.pub_key;
122
123 let auth = parse_sign_response(self.app_id.clone(), client_data.clone(), public_key, sign_data.clone());
124
125 match auth {
126 Ok(ref res) => {
127 if res.counter < counter {
130 Err(U2fError::CounterTooLow)
131 } else {
132 Ok(res.counter)
133 }
134 }
135 Err(e) => Err(e),
136 }
137 }
138}