1pub mod api;
5mod attestation_cert;
6mod ctap;
7pub mod error;
8mod implementation;
9pub mod messages;
10mod nav_thread;
11mod u2f;
12
13use ctap::{PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity};
14use error::FidoError;
15
16security::use_api!();
17
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
19pub struct RegisteredKeyU2f {
20 pub application_parameter: [u8; 32],
21 pub signature_counter: u32,
22 pub registered_timestamp: u32,
23}
24
25#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
26pub struct RegisteredKeyCtap {
27 pub rp: PublicKeyCredentialRpEntity,
28 pub user: PublicKeyCredentialUserEntity,
29 pub signature_counter: u32,
30 pub registered_timestamp: u32,
31}
32
33#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
34pub enum RegisteredKey {
35 U2f(RegisteredKeyU2f),
36 Ctap(RegisteredKeyCtap),
37}
38
39crypto::use_api!();
40
41impl RegisteredKey {
42 pub(crate) fn signature_counter(&self) -> u32 {
43 match self {
44 RegisteredKey::U2f(key) => key.signature_counter,
45 RegisteredKey::Ctap(key) => key.signature_counter,
46 }
47 }
48
49 pub(crate) fn inc_signature_counter(&mut self) -> u32 {
50 match self {
51 RegisteredKey::U2f(key) => {
52 key.signature_counter += 1;
53 key.signature_counter
54 }
55 RegisteredKey::Ctap(key) => {
56 key.signature_counter += 1;
57 key.signature_counter
58 }
59 }
60 }
61}
62
63#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
64pub struct SecurityKey {
65 pub registered_keys: Vec<RegisteredKey>,
66 pub live: bool,
67 #[serde(default)]
68 pub label: String,
69 #[serde(default)]
70 pub color: u8,
71 #[serde(default)]
72 pub icon: String,
73 #[serde(default)]
74 pub archived: bool,
75 #[serde(default)]
76 pub date: u64,
77}
78
79#[derive(
82 Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, serde::Serialize, serde::Deserialize,
83)]
84pub struct SecurityKeyView {
85 pub index: usize,
86 pub label: String,
87 pub color: u8,
88 pub icon: String,
89 pub archived: bool,
90 pub live: bool,
91 pub date: u64,
92 pub registered_count: usize,
93}
94
95impl SecurityKey {
96 pub fn to_view(&self, index: usize) -> SecurityKeyView {
98 SecurityKeyView {
99 index,
100 label: self.label.clone(),
101 color: self.color,
102 icon: self.icon.clone(),
103 archived: self.archived,
104 live: self.live,
105 date: self.date,
106 registered_count: self.registered_keys.len(),
107 }
108 }
109
110 pub fn set_archived(&mut self, archived: bool) {
112 self.archived = archived;
113 if archived {
114 self.live = false;
115 } else {
116 self.live = true;
117 }
118 }
119}
120
121impl SecurityKey {
122 fn registered_key(&self, index: usize) -> Result<&RegisteredKey, FidoError> {
123 self.registered_keys.get(index).ok_or(FidoError::InvalidIndex)
124 }
125
126 fn registered_key_mut(&mut self, index: usize) -> Result<&mut RegisteredKey, FidoError> {
127 self.registered_keys.get_mut(index).ok_or(FidoError::InvalidIndex)
128 }
129
130 fn registered_key_indexes(&self, u2f: bool, tag: &[u8]) -> Vec<usize> {
131 self.registered_keys
132 .iter()
133 .enumerate()
134 .filter_map(|(index, key)| match (u2f, key) {
135 (true, RegisteredKey::U2f(key)) if key.application_parameter == tag => Some(index),
136 (false, RegisteredKey::Ctap(key)) if key.rp.id.as_bytes() == tag => Some(index),
137 _ => None,
138 })
139 .collect()
140 }
141}
142
143pub fn listen() {
144 let (security, app_seed) = implementation::wait();
145 server::listen(implementation::FidoServer::new(security, app_seed).unwrap())
146}
147
148#[cfg(test)]
149mod tests {
150 use chrono::Utc;
151
152 use super::*;
153
154 #[test]
155 fn inc_signature_counter() {
156 let mut key = RegisteredKey::U2f(RegisteredKeyU2f {
157 application_parameter: [1; 32],
158 signature_counter: 0,
159 registered_timestamp: Utc::now().timestamp() as u32,
160 });
161 assert_eq!(key.signature_counter(), 0);
162 assert_eq!(key.inc_signature_counter(), 1);
163 assert_eq!(key.signature_counter(), 1);
164 }
165
166 #[test]
167 fn registered_key_indexes() {
168 let security_key = SecurityKey {
169 registered_keys: vec![
170 RegisteredKey::Ctap(RegisteredKeyCtap {
171 rp: PublicKeyCredentialRpEntity { id: "test.com".to_string(), name: None },
172 user: PublicKeyCredentialUserEntity {
173 id: vec![12],
174 name: None,
175 display_name: None,
176 icon: None,
177 },
178 signature_counter: 0,
179 registered_timestamp: Utc::now().timestamp() as u32,
180 }),
181 RegisteredKey::Ctap(RegisteredKeyCtap {
182 rp: PublicKeyCredentialRpEntity { id: "test2.com".to_string(), name: None },
183 user: PublicKeyCredentialUserEntity {
184 id: vec![13],
185 name: None,
186 display_name: None,
187 icon: None,
188 },
189 signature_counter: 1,
190 registered_timestamp: Utc::now().timestamp() as u32,
191 }),
192 RegisteredKey::Ctap(RegisteredKeyCtap {
193 rp: PublicKeyCredentialRpEntity { id: "test.com".to_string(), name: None },
194 user: PublicKeyCredentialUserEntity {
195 id: vec![14],
196 name: None,
197 display_name: None,
198 icon: None,
199 },
200 signature_counter: 0,
201 registered_timestamp: Utc::now().timestamp() as u32,
202 }),
203 RegisteredKey::U2f(RegisteredKeyU2f {
204 application_parameter: [1; 32],
205 signature_counter: 0,
206 registered_timestamp: Utc::now().timestamp() as u32,
207 }),
208 RegisteredKey::U2f(RegisteredKeyU2f {
209 application_parameter: [2; 32],
210 signature_counter: 0,
211 registered_timestamp: Utc::now().timestamp() as u32,
212 }),
213 RegisteredKey::U2f(RegisteredKeyU2f {
214 application_parameter: [1; 32],
215 signature_counter: 0,
216 registered_timestamp: Utc::now().timestamp() as u32,
217 }),
218 ],
219 live: false,
220 ..Default::default()
221 };
222 let registered_key_indexes_ctap = security_key.registered_key_indexes(false, "test.com".as_bytes());
223 println!("{:?}", registered_key_indexes_ctap);
224 assert_eq!(registered_key_indexes_ctap, vec![0, 2]);
225 let registered_key_indexes_u2f = security_key.registered_key_indexes(true, &[1; 32]);
226 println!("{:?}", registered_key_indexes_u2f);
227 assert_eq!(registered_key_indexes_u2f, vec![3, 5]);
228 }
229}