blob: 6ca0611d3f9170262decffac4b69ca028e12d843 [file] [log] [blame]
Alistair Francis4ad920d2020-10-02 08:13:44 -07001//! This is a featured CTAP example
2//! WARNING! This currently uses unsound crypto operations
3//! This is only a demo and should not be used in real enviroments
Alistair Francis67f3e5f2020-09-11 10:03:35 -07004#![no_std]
5#![feature(alloc_error_handler)]
6
7extern crate alloc;
8
9use core::alloc::Layout;
10use core::cell::Cell;
Alistair Francised5f9cb2020-09-17 15:40:41 -070011use core::convert::TryInto;
Alistair Francisfd050a12020-09-17 15:30:13 -070012use core::fmt::{self, Debug, Error, Formatter};
Alistair Francis67f3e5f2020-09-11 10:03:35 -070013use core::time::Duration;
14use ctap2_authenticator::credentials::{RpCredential, UserCredential};
15use ctap2_authenticator::usbhid::{
16 CtapHidCapabilities, CtapHidPlatform, KeepaliveResponse, TransactionProcessor,
17};
18use ctap2_authenticator::Authenticator;
19use ctap2_authenticator::{
20 AuthenticatorPlatform, CredentialDescriptorList, CtapOptions, PublicKey, Signature,
21};
Alistair Francisbcb51412020-09-17 15:34:14 -070022use generic_array::GenericArray;
Alistair Francis67f3e5f2020-09-11 10:03:35 -070023use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer};
Alistair Francised5f9cb2020-09-17 15:40:41 -070024use libtock::hmac::{HmacDataBuffer, HmacDestBuffer, HmacDriverFactory, HmacKeyBuffer};
Alistair Francis67f3e5f2020-09-11 10:03:35 -070025use libtock::println;
26use libtock::result::TockResult;
27use libtock::syscalls;
Alistair Francisb541b342020-09-17 15:45:53 -070028use p256::ecdsa::{signature::Signer, SigningKey};
Alistair Francisbcb51412020-09-17 15:34:14 -070029use p256::elliptic_curve::ff::PrimeField;
Alistair Francised5f9cb2020-09-17 15:40:41 -070030use p256::{Scalar, SecretKey};
Alistair Francisbcb51412020-09-17 15:34:14 -070031use subtle::{Choice, ConditionallySelectable};
32
Alistair Francis6b2f10d2020-11-19 13:06:20 -080033libtock_core::stack_size! {0x4000}
34
Alistair Francisbcb51412020-09-17 15:34:14 -070035#[derive(Debug, Clone, PartialEq, Eq, Copy)]
36pub struct PrivateKey(Scalar);
37
38impl ConditionallySelectable for PrivateKey {
39 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
40 Self(ConditionallySelectable::conditional_select(
41 &a.0, &b.0, choice,
42 ))
43 }
44}
45
46impl Default for PrivateKey {
47 fn default() -> Self {
48 Self(Scalar::default())
49 }
50}
51
52impl PrivateKey {
53 pub fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
54 Scalar::from_repr(GenericArray::clone_from_slice(bytes)).map(|s| Self(s))
55 }
56}
Alistair Francis67f3e5f2020-09-11 10:03:35 -070057
58/// This is the provided implementation of `CtapHidPlatform` to be used in the `UsbContext`
59pub(crate) struct UsbKeyHidPlatform {}
60
61impl UsbKeyHidPlatform {
62 pub fn new() -> Self {
63 Self {}
64 }
65}
66
67impl CtapHidPlatform for UsbKeyHidPlatform {
68 // Should be kept in sync with the Crates version if possible
69 const MAJOR_VERSION: u8 = 0;
70 const MINOR_VERSION: u8 = 0;
71 const BUILD_VERSION: u8 = 0;
72 const CAPABILITIES: CtapHidCapabilities = CtapHidCapabilities {
73 wink: true,
74 cbor: true,
75 msg: false,
76 };
77
78 fn wink(&mut self) {}
79
80 fn cancel(&mut self) {
Alistair Francis3dd1a1b2020-09-17 15:29:15 -070081 println!("cancel");
Alistair Francis67f3e5f2020-09-11 10:03:35 -070082 unimplemented!()
83 }
84
85 fn keepalive_needed(&mut self) -> KeepaliveResponse {
Alistair Francis3dd1a1b2020-09-17 15:29:15 -070086 println!("keepalive_needed");
Alistair Francis67f3e5f2020-09-11 10:03:35 -070087 unimplemented!()
88 }
89
90 fn start_timer(&mut self) {
Alistair Francis3dd1a1b2020-09-17 15:29:15 -070091 println!("start_timer");
Alistair Francis67f3e5f2020-09-11 10:03:35 -070092 }
93
94 fn has_timed_out(&mut self) -> bool {
Alistair Francis3dd1a1b2020-09-17 15:29:15 -070095 println!("has_timed_out");
96 false
Alistair Francis67f3e5f2020-09-11 10:03:35 -070097 }
98}
99
100#[derive(Clone, Copy)]
101pub struct HmacKeyCredential(pub(crate) [u8; 40]);
102
Alistair Francis3dd1a1b2020-09-17 15:29:15 -0700103impl HmacKeyCredential {
104 fn new(mac: &[u8; 32], nonce: [u8; 8]) -> Self {
105 let mut new = [0; 40];
106 new[..32].copy_from_slice(&mac[..]);
107 new[32..].copy_from_slice(&nonce[..]);
108 Self(new)
109 }
110
111 fn get_mac(&self) -> [u8; 32] {
112 let mut mac = [0; 32];
113 mac.copy_from_slice(&self.0[..32]);
114 mac
115 }
116}
117
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700118impl AsRef<[u8]> for HmacKeyCredential {
119 fn as_ref(&self) -> &[u8] {
120 &self.0
121 }
122}
123
124impl Debug for HmacKeyCredential {
125 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
126 write!(
127 f,
128 "HmacKeyCredential {{ mac: {:?}, nonce: {:?} }}",
129 &self.0[..32],
130 &self.0[32..]
131 )
132 }
133}
134
135impl PartialEq for HmacKeyCredential {
136 fn eq(&self, other: &Self) -> bool {
137 self.0[..] == other.0[..]
138 }
139}
140
141impl Eq for HmacKeyCredential {}
142
143const ITERATOR_MAX_LENGTH: usize = 8;
144
145pub struct HmacCredentialQueue {
146 index: usize,
147 length: usize,
148 list: [Option<(HmacKeyCredential, u32)>; ITERATOR_MAX_LENGTH],
149}
150
151impl HmacCredentialQueue {
152 pub(crate) fn new() -> Self {
153 Self {
154 index: 0,
155 length: 0,
156 list: [None; ITERATOR_MAX_LENGTH],
157 }
158 }
159
Alistair Francis3dd1a1b2020-09-17 15:29:15 -0700160 pub(crate) fn push(&mut self, credential: HmacKeyCredential, counter: u32) -> Result<(), ()> {
161 if self.length < ITERATOR_MAX_LENGTH {
162 self.list[self.length] = Some((credential, counter));
163 self.length += 1;
164 Ok(())
165 } else {
166 Err(())
167 }
168 }
169
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700170 pub(crate) fn len(&self) -> usize {
171 self.length
172 }
173}
174
175impl Iterator for HmacCredentialQueue {
176 type Item = (HmacKeyCredential, u32);
177
178 fn next(&mut self) -> Option<Self::Item> {
179 if self.list[self.index].is_some() {
180 self.index += 1;
181 self.list[self.index - 1]
182 } else {
183 None
184 }
185 }
186}
187
Alistair Francisfd050a12020-09-17 15:30:13 -0700188pub(crate) struct CtapPlatform {
189 hmac: HmacDriverFactory,
190}
191
192impl CtapPlatform {
193 fn new(hmac: HmacDriverFactory) -> Self {
194 Self { hmac }
195 }
196}
197
198impl fmt::Debug for CtapPlatform {
199 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 Ok(())
201 }
202}
203
Alistair Francised5f9cb2020-09-17 15:40:41 -0700204impl CtapPlatform {
205 fn generate_key_seed(
206 &mut self,
207 rp_id: &str,
208 nonce: &[u8; 8],
209 ) -> [u8; libtock::hmac::DEST_BUFFER_SIZE] {
Alistair Francis3e75f952020-10-01 14:20:17 -0700210 let hmac_driver;
211 match self.hmac.init_driver() {
212 Err(_) => {
213 panic!("Hmac init error");
214 }
215 Ok(driver) => {
216 hmac_driver = driver;
217 }
218 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700219
220 let mut key_buffer = HmacKeyBuffer::default();
221 for (i, d) in rp_id.as_bytes().iter().enumerate() {
222 key_buffer[i] = *d;
223 }
Alistair Francis3e75f952020-10-01 14:20:17 -0700224 // We need to make sure this isn't dropped straight away
225 let key_buffer_ret = hmac_driver.init_key_buffer(&mut key_buffer);
226 if key_buffer_ret.is_err() {
227 panic!("Hmac key buffer init error");
228 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700229
230 let mut data_buffer = HmacDataBuffer::default();
231 for (i, d) in nonce.iter().enumerate() {
232 data_buffer[i] = *d;
233 }
Alistair Francis3e75f952020-10-01 14:20:17 -0700234 // We need to make sure this isn't dropped straight away
235 let data_buffer_ret = hmac_driver.init_data_buffer(&mut data_buffer);
236 if data_buffer_ret.is_err() {
237 panic!("Hmac data buffer init error");
238 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700239
Alistair Francis3e75f952020-10-01 14:20:17 -0700240 let mut dest_buffer = HmacDestBuffer::default();
241 let seed_buffer;
242 match hmac_driver.init_dest_buffer(&mut dest_buffer) {
243 Err(_) => {
244 panic!("Hmac subscribe error");
245 }
246 Ok(buffer) => {
247 seed_buffer = buffer;
248 }
249 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700250
251 let mut callback = |_result, _digest| {};
252
Alistair Francis3e75f952020-10-01 14:20:17 -0700253 // We need to make sure this isn't dropped straight away
254 let subscribe = hmac_driver.subscribe(&mut callback);
255 if subscribe.is_err() {
256 panic!("Hmac subscribe error");
257 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700258
Alistair Francis3e75f952020-10-01 14:20:17 -0700259 if hmac_driver.run().is_err() {
260 panic!("Hmac run error");
261 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700262
263 // Yield waiting for the HMAC callback
264 unsafe {
265 syscalls::raw::yieldk();
266 }
267
268 let mut temp_buffer = [0; libtock::hmac::DEST_BUFFER_SIZE];
269 seed_buffer.read_bytes(&mut temp_buffer[..]);
270 temp_buffer
271 }
Alistair Francis142290d2020-09-17 15:46:38 -0700272
273 /// Generates a Credential from a CredentialId and checks that the Id is valid
274 fn checked_generate_hmac_cred(
275 &mut self,
276 rp_id: &str,
277 credential_id: &[u8],
278 ) -> Option<HmacKeyCredential> {
279 if credential_id.len() != 40 {
280 return None;
281 }
282
283 let received_mac = &credential_id[..32];
284
285 let seed_buffer = self.generate_key_seed(rp_id, &credential_id[32..40].try_into().unwrap());
286
287 let credential =
288 HmacKeyCredential::new(&seed_buffer, credential_id[32..].try_into().unwrap());
289
290 let generated_mac = &credential.0[..32];
291
292 if generated_mac == received_mac {
293 Some(credential)
294 } else {
295 None
296 }
297 }
Alistair Francised5f9cb2020-09-17 15:40:41 -0700298}
299
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700300impl AuthenticatorPlatform for CtapPlatform {
301 const AAGUID: [u8; 16] = [
302 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
303 0x7d,
304 ];
305 const CERTIFICATE: Option<&'static [u8]> = None;
306 const MAX_MSG_LENGTH: u16 = 7609;
307 const SUPPORTED_OPTIONS: CtapOptions = CtapOptions {
308 plat: None,
309 rk: Some(true),
310 client_pin: None,
311 up: Some(true),
312 uv: Some(false),
313 };
314 type CredentialId = HmacKeyCredential;
315 type PublicKeyBuffer = [u8; 32];
316 type SignatureBuffer = [u8; 32];
317 type CredentialIterator = HmacCredentialQueue;
318
319 fn reset(&mut self) -> Result<(), ()> {
Alistair Francis8f6d2402020-09-17 15:47:03 -0700320 println!("reset");
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700321 unimplemented!()
322 }
323
324 fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool {
Alistair Francis8f6d2402020-09-17 15:47:03 -0700325 println!("check_exclude_list");
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700326 unimplemented!()
327 }
328
329 fn locate_credentials(
330 &mut self,
Alistair Francis142290d2020-09-17 15:46:38 -0700331 rp_id: &str,
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700332 list: Option<CredentialDescriptorList>,
333 ) -> (u16, Self::CredentialIterator) {
334 match list {
335 None => (0, HmacCredentialQueue::new()),
Alistair Francis142290d2020-09-17 15:46:38 -0700336 Some(list) => {
337 let mut result = HmacCredentialQueue::new();
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700338
Alistair Francis142290d2020-09-17 15:46:38 -0700339 for descriptor in list {
340 match self.checked_generate_hmac_cred(rp_id, descriptor.get_id()) {
341 None => (),
342 Some(credential) => {
343 match result.push(credential, 0) {
344 Ok(()) => (),
345 // NOTE: We just truncate if we don't have enough space to store all
346 // fitting credentials
347 Err(()) => break,
348 }
349 }
350 }
351 }
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700352
353 (result.len() as u16, result)
354 }
355 }
356 }
357
358 fn create_credential(
359 &mut self,
Alistair Francised5f9cb2020-09-17 15:40:41 -0700360 rp: RpCredential<&[u8], &str>,
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700361 _user: UserCredential<&[u8], &str>,
362 ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) {
Alistair Francised5f9cb2020-09-17 15:40:41 -0700363 println!("create_credential");
364 let rp_id = core::str::from_utf8(rp.rp_id()).unwrap();
365 // This nonce is static!!!!
366 // This is really bad cryptowise, let's print a warning
367 // TODO: Convert this to generate a nonce from Tock's TRNG
368 println!("WARNING!!! The nonce is static, this key is insecure");
369 println!("WARNING!!! Do not use this anywhere important");
370 let nonce = [0; 8];
371
372 let seed_buffer = self.generate_key_seed(rp_id, &nonce);
373
374 let credential = HmacKeyCredential::new(&seed_buffer, nonce.try_into().unwrap());
375
376 let secret_key = SecretKey::from_bytes(&seed_buffer[0..32]).unwrap();
377
378 let pub_key = p256::EncodedPoint::from_secret_key(&secret_key, false);
379
380 let x: [u8; 32] = pub_key.as_bytes()[1..33].try_into().unwrap();
381 let y: [u8; 32] = pub_key.as_bytes()[33..].try_into().unwrap();
382
383 let ret_key = PublicKey::nistp256(x, y);
384
385 (credential, ret_key, 0)
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700386 }
387
388 fn attest(
389 &mut self,
Alistair Francisb541b342020-09-17 15:45:53 -0700390 id: &Self::CredentialId,
391 data: &[u8],
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700392 ) -> Option<Signature<Self::SignatureBuffer>> {
Alistair Francisb541b342020-09-17 15:45:53 -0700393 // TODO: Should attest be different then sign?
394 let attest = {
395 let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();
396
397 let signer = SigningKey::from(&secret_key);
398
399 let sig = signer.sign(data);
400
401 let mut r: [u8; 32] = [0; 32];
402 r.clone_from_slice(&sig.r().as_ref().to_bytes());
403 let mut s: [u8; 32] = [0; 32];
404 s.clone_from_slice(&sig.s().as_ref().to_bytes());
405
406 Signature::nistp256(r, s)
407 };
408
409 Some(attest)
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700410 }
411
412 fn sign(
413 &mut self,
Alistair Francisb541b342020-09-17 15:45:53 -0700414 id: &Self::CredentialId,
415 data: &[u8],
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700416 ) -> Option<Signature<Self::SignatureBuffer>> {
Alistair Francisb541b342020-09-17 15:45:53 -0700417 let attest = {
418 let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();
419 let signer = SigningKey::from(&secret_key);
420 let sig = signer.sign(data);
421
422 let mut r: [u8; 32] = [0; 32];
423 r.clone_from_slice(&sig.r().as_ref().to_bytes());
424 let mut s: [u8; 32] = [0; 32];
425 s.clone_from_slice(&sig.s().as_ref().to_bytes());
426
427 Signature::nistp256(r, s)
428 };
429
430 Some(attest)
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700431 }
432
433 fn start_timeout(&mut self) {
Alistair Francis8f6d2402020-09-17 15:47:03 -0700434 println!("start_timeout");
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700435 unimplemented!()
436 }
437
438 fn has_timed_out(&mut self, _time: Duration) -> bool {
Alistair Francis8f6d2402020-09-17 15:47:03 -0700439 println!("has_timed_out");
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700440 unimplemented!()
441 }
442}
443
444#[alloc_error_handler]
445unsafe fn alloc_error_handler(_: Layout) -> ! {
446 println!("alloc_error_handler called");
447 loop {
448 syscalls::raw::yieldk();
449 }
450}
451
452#[libtock::main]
453async fn main() -> TockResult<()> {
454 let mut tp: Cell<TransactionProcessor<&'static mut [u8], UsbKeyHidPlatform>>;
455 let mut temp_buffer = [0; libtock::ctap::RECV_BUFFER_SIZE];
456 let mut drivers = libtock::retrieve_drivers()?;
457 drivers.console.create_console();
458
459 println!("Starting CTAP feature example");
460
461 let ctap_driver = drivers.ctap.init_driver()?;
462
463 let mut recv_buffer = CtapRecvBuffer::default();
464 let recv_buffer = ctap_driver.init_recv_buffer(&mut recv_buffer)?;
465
466 let mut send_buffer = CtapSendBuffer::default();
467 let mut send_buffer = ctap_driver.init_send_buffer(&mut send_buffer)?;
468
469 static mut BUFFER: &'static mut [u8] = &mut [0; 2048];
470 unsafe {
471 tp = Cell::new(TransactionProcessor::new(BUFFER, UsbKeyHidPlatform::new()));
472 }
473
Alistair Francisfd050a12020-09-17 15:30:13 -0700474 let mut authenticator = Authenticator::create(CtapPlatform::new(drivers.hmac));
Alistair Francis67f3e5f2020-09-11 10:03:35 -0700475
476 let mut callback = |sent, _| {
477 let mut looping = true;
478 recv_buffer.read_bytes(&mut temp_buffer[..]);
479
480 let temp_tp = tp.get_mut();
481 let mut poll_msg = if sent == 0 { Some(&temp_buffer) } else { None };
482
483 while looping {
484 looping = false;
485 let (maybe_request, maybe_data) = temp_tp.poll(poll_msg);
486
487 if let Some(request) = maybe_request {
488 let response = authenticator.process(request);
489 temp_tp.response(response);
490 looping = true;
491 }
492
493 if let Some(mut data) = maybe_data {
494 send_buffer.write_bytes(&mut data[0..]);
495 let _ = ctap_driver.send_data();
496 }
497
498 poll_msg = None;
499 }
500 let _ = ctap_driver.allow_receive();
501 };
502
503 let _subscription = ctap_driver.subscribe(&mut callback)?;
504 ctap_driver.allow_receive()?;
505
506 loop {
507 unsafe { syscalls::raw::yieldk() };
508 }
509}