fido_authenticator/
ctap1.rs1use ctap_types::{
4 ctap1::{authenticate, register, Authenticator, ControlByte, Error, Result},
5 heapless_bytes::Bytes,
6};
7use serde_bytes::ByteArray;
8
9use trussed_core::{
10 syscall,
11 types::{KeySerialization, Location, Mechanism, SignatureSerialization},
12};
13
14use crate::{
15 constants,
16 credential::{self, Credential, Key, StrippedCredential},
17 SigningAlgorithm, TrussedRequirements, UserPresence,
18};
19
20type Commitment = Bytes<324>;
21
22impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenticator<UP, T> {
29 fn register(&mut self, reg: ®ister::Request) -> Result<register::Response> {
38 self.up
39 .user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
40 .map_err(|_| Error::ConditionsOfUseNotSatisfied)?;
41
42 let private_key = syscall!(self.trussed.generate_p256_private_key(Location::Volatile)).key;
44 let public_key = syscall!(self
45 .trussed
46 .derive_p256_public_key(private_key, Location::Volatile))
47 .key;
48
49 let serialized_cose_public_key = syscall!(self
50 .trussed
51 .serialize_p256_key(public_key, KeySerialization::EcdhEsHkdf256))
52 .serialized_key;
53 syscall!(self.trussed.delete(public_key));
54 let cose_key: cosey::EcdhEsHkdf256PublicKey =
55 cbor_smol::cbor_deserialize(&serialized_cose_public_key).unwrap();
56
57 let wrapping_key = self
58 .state
59 .persistent
60 .key_wrapping_key(&mut self.trussed)
61 .map_err(|_| Error::UnspecifiedCheckingError)?;
62 let wrapped_key =
65 syscall!(self
66 .trussed
67 .wrap_key_chacha8poly1305(wrapping_key, private_key, &[], None))
68 .wrapped_key;
69 syscall!(self.trussed.delete(private_key));
72
73 let key = Key::WrappedKey(
74 wrapped_key
75 .to_bytes()
76 .map_err(|_| Error::UnspecifiedCheckingError)?,
77 );
78 let nonce = ByteArray::new(self.nonce());
79
80 let credential = StrippedCredential {
81 ctap: credential::CtapVersion::U2fV2,
82 creation_time: self
83 .state
84 .persistent
85 .timestamp(&mut self.trussed)
86 .map_err(|_| Error::NotEnoughMemory)?,
87 use_counter: true,
88 algorithm: SigningAlgorithm::P256 as i32,
89 key,
90 nonce,
91 hmac_secret: None,
92 cred_protect: None,
93 large_blob_key: None,
94 third_party_payment: None,
95 };
96
97 let kek = self
101 .state
102 .persistent
103 .key_encryption_key(&mut self.trussed)
104 .map_err(|_| Error::NotEnoughMemory)?;
105 let credential_id = credential
106 .id(&mut self.trussed, kek, reg.app_id)
107 .map_err(|_| Error::NotEnoughMemory)?;
108
109 let mut commitment = Commitment::new();
110
111 commitment.push(0).unwrap(); commitment.extend_from_slice(reg.app_id).unwrap();
113 commitment.extend_from_slice(reg.challenge).unwrap();
114
115 commitment.extend_from_slice(&credential_id.0).unwrap();
116
117 commitment.push(0x04).unwrap(); commitment.extend_from_slice(&cose_key.x).unwrap();
119 commitment.extend_from_slice(&cose_key.y).unwrap();
120
121 let attestation = self.state.identity.attestation(&mut self.trussed);
122
123 let (signature, cert) = match attestation {
124 (Some((key, cert)), _aaguid) => {
125 info!("aaguid: {}", hex_str!(&_aaguid));
126 (
127 syscall!(self.trussed.sign(
128 Mechanism::P256,
129 key,
130 &commitment,
131 SignatureSerialization::Asn1Der
132 ))
133 .signature
134 .to_bytes()
135 .unwrap(),
136 cert,
137 )
138 }
139 _ => {
140 info!("Not provisioned with attestation key!");
141 return Err(Error::KeyReferenceNotFound);
142 }
143 };
144
145 Ok(register::Response::new(
146 0x05,
147 &cose_key,
148 credential_id.0,
149 signature,
150 cert,
151 ))
152 }
153
154 fn authenticate(&mut self, auth: &authenticate::Request) -> Result<authenticate::Response> {
155 let cred = Credential::try_from_bytes(self, auth.app_id, auth.key_handle);
156
157 let user_presence_byte = match auth.control_byte {
158 ControlByte::CheckOnly => {
159 return if cred.is_ok() {
163 Err(Error::ConditionsOfUseNotSatisfied)
164 } else {
165 Err(Error::IncorrectDataParameter)
166 };
167 }
168 ControlByte::EnforceUserPresenceAndSign => {
169 if !self.skip_up_check() {
170 self.up
171 .user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT)
172 .map_err(|_| Error::ConditionsOfUseNotSatisfied)?;
173 }
174 0x01
175 }
176 ControlByte::DontEnforceUserPresenceAndSign => 0x00,
177 };
178
179 let cred = cred.map_err(|_| Error::IncorrectDataParameter)?;
180
181 let key = match cred.key() {
182 Key::WrappedKey(bytes) => {
183 let wrapping_key = self
184 .state
185 .persistent
186 .key_wrapping_key(&mut self.trussed)
187 .map_err(|_| Error::IncorrectDataParameter)?;
188 let key_result = syscall!(self.trussed.unwrap_key_chacha8poly1305(
189 wrapping_key,
190 bytes,
191 &[],
192 Location::Volatile,
193 ))
194 .key;
195 match key_result {
196 Some(key) => {
197 info!("loaded u2f key!");
198 key
199 }
200 None => {
201 info!("issue with unwrapping credential id key");
202 return Err(Error::IncorrectDataParameter);
203 }
204 }
205 }
206 _ => return Err(Error::IncorrectDataParameter),
207 };
208
209 if cred.algorithm() != -7 {
210 info!("Unexpected mechanism for u2f");
211 return Err(Error::IncorrectDataParameter);
212 }
213
214 let sig_count = self
215 .state
216 .persistent
217 .timestamp(&mut self.trussed)
218 .map_err(|_| Error::UnspecifiedNonpersistentExecutionError)?;
219
220 let mut commitment = Commitment::new();
221
222 commitment.extend_from_slice(auth.app_id).unwrap();
223 commitment.push(user_presence_byte).unwrap();
224 commitment
225 .extend_from_slice(&sig_count.to_be_bytes())
226 .unwrap();
227 commitment.extend_from_slice(auth.challenge).unwrap();
228
229 let signature = syscall!(self.trussed.sign(
230 Mechanism::P256,
231 key,
232 &commitment,
233 SignatureSerialization::Asn1Der
234 ))
235 .signature
236 .to_bytes()
237 .unwrap();
238
239 Ok(authenticate::Response {
240 user_presence: user_presence_byte,
241 count: sig_count,
242 signature,
243 })
244 }
245}