|
| 1 | +use std::collections::HashMap; |
1 | 2 | use std::fmt::{Debug, Display}; |
| 3 | +use std::sync::Arc; |
2 | 4 |
|
3 | 5 | use crate::transport::error::Error; |
4 | 6 | use crate::transport::Device; |
| 7 | +use crate::webauthn::TransportError; |
5 | 8 | use crate::UxUpdate; |
6 | 9 |
|
7 | 10 | use async_trait::async_trait; |
| 11 | +use futures::lock::Mutex; |
8 | 12 | use tokio::sync::mpsc; |
9 | 13 |
|
10 | 14 | use super::channel::CableChannel; |
| 15 | +use super::tunnel::CableLinkingInfo; |
11 | 16 | use super::Cable; |
12 | 17 |
|
13 | 18 | #[async_trait] |
14 | | -pub trait CableKnownDeviceInfoStore: Debug + Send { |
15 | | - /// Called whenever a known device should be added. |
16 | | - async fn put_known_device(&mut self, device: &CableKnownDeviceInfo); |
| 19 | +pub trait CableKnownDeviceInfoStore: Debug + Send + Sync { |
| 20 | + /// Called whenever a known device should be added or updated. |
| 21 | + async fn put_known_device(&self, device_id: &CableKnownDeviceId, device: &CableKnownDeviceInfo); |
17 | 22 | /// Called whenever a known device becomes permanently unavailable. |
18 | | - async fn delete_known_device(&mut self, device_id: String); |
| 23 | + async fn delete_known_device(&self, device_id: &CableKnownDeviceId); |
19 | 24 | } |
20 | 25 |
|
21 | | -/// A no-op known-device store for ephemeral-only implementations. |
| 26 | +/// An in-memory store for testing purposes. |
22 | 27 | #[derive(Debug, Default, Clone)] |
23 | 28 | pub struct EphemeralDeviceInfoStore { |
24 | | - pub last_device_info: Option<CableKnownDeviceInfo>, |
| 29 | + pub known_devices: Arc<Mutex<HashMap<CableKnownDeviceId, CableKnownDeviceInfo>>>, |
| 30 | +} |
| 31 | + |
| 32 | +impl EphemeralDeviceInfoStore { |
| 33 | + pub fn new() -> Self { |
| 34 | + Self { |
| 35 | + known_devices: Arc::new(Mutex::new(HashMap::new())), |
| 36 | + } |
| 37 | + } |
25 | 38 | } |
26 | 39 |
|
27 | 40 | unsafe impl Send for EphemeralDeviceInfoStore {} |
28 | 41 |
|
29 | 42 | #[async_trait] |
30 | 43 | impl CableKnownDeviceInfoStore for EphemeralDeviceInfoStore { |
31 | | - async fn put_known_device(&mut self, device: &CableKnownDeviceInfo) { |
32 | | - self.last_device_info = Some(device.clone()) |
| 44 | + async fn put_known_device( |
| 45 | + &self, |
| 46 | + device_id: &CableKnownDeviceId, |
| 47 | + device: &CableKnownDeviceInfo, |
| 48 | + ) { |
| 49 | + let mut known_devices = self.known_devices.lock().await; |
| 50 | + known_devices.insert(device_id.clone(), device.clone()); |
33 | 51 | } |
34 | 52 |
|
35 | | - async fn delete_known_device(&mut self, device_id: String) { |
36 | | - if let Some(last_device_info) = &self.last_device_info { |
37 | | - if last_device_info.device_id == device_id { |
38 | | - self.last_device_info = None |
39 | | - } |
40 | | - } |
| 53 | + async fn delete_known_device(&self, device_id: &CableKnownDeviceId) { |
| 54 | + let mut known_devices = self.known_devices.lock().await; |
| 55 | + known_devices.remove(device_id); |
41 | 56 | } |
42 | 57 | } |
43 | 58 |
|
| 59 | +pub type CableKnownDeviceId = String; |
| 60 | + |
44 | 61 | #[derive(Debug, Clone)] |
45 | 62 | pub struct CableKnownDeviceInfo { |
46 | | - pub device_id: String, |
47 | 63 | pub contact_id: Vec<u8>, |
48 | 64 | pub link_id: [u8; 8], |
49 | 65 | pub link_secret: [u8; 32], |
50 | 66 | pub public_key: [u8; 65], |
51 | 67 | pub name: String, |
| 68 | + pub tunnel_domain: String, |
| 69 | +} |
| 70 | + |
| 71 | +impl From<&CableLinkingInfo> for CableKnownDeviceId { |
| 72 | + fn from(linking_info: &CableLinkingInfo) -> Self { |
| 73 | + hex::encode(&linking_info.authenticator_public_key) |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +impl CableKnownDeviceInfo { |
| 78 | + pub(crate) fn new(tunnel_domain: &str, linking_info: &CableLinkingInfo) -> Result<Self, Error> { |
| 79 | + let info = Self { |
| 80 | + contact_id: linking_info.contact_id.to_vec(), |
| 81 | + link_id: linking_info |
| 82 | + .link_id |
| 83 | + .clone() |
| 84 | + .try_into() |
| 85 | + .map_err(|_| Error::Transport(TransportError::InvalidFraming))?, |
| 86 | + link_secret: linking_info |
| 87 | + .link_secret |
| 88 | + .clone() |
| 89 | + .try_into() |
| 90 | + .map_err(|_| Error::Transport(TransportError::InvalidFraming))?, |
| 91 | + public_key: linking_info |
| 92 | + .authenticator_public_key |
| 93 | + .clone() |
| 94 | + .try_into() |
| 95 | + .map_err(|_| Error::Transport(TransportError::InvalidFraming))?, |
| 96 | + name: linking_info.authenticator_name.clone(), |
| 97 | + tunnel_domain: tunnel_domain.to_string(), |
| 98 | + }; |
| 99 | + Ok(info) |
| 100 | + } |
52 | 101 | } |
53 | 102 |
|
54 | 103 | #[derive(Debug)] |
55 | | -pub struct CableKnownDevice<'d> { |
| 104 | +pub struct CableKnownDevice { |
56 | 105 | pub device_info: CableKnownDeviceInfo, |
57 | | - _store: &'d mut Box<dyn CableKnownDeviceInfoStore>, |
| 106 | + _store: Arc<dyn CableKnownDeviceInfoStore>, |
58 | 107 | } |
59 | 108 |
|
60 | | -impl<'d> Display for CableKnownDevice<'d> { |
| 109 | +impl Display for CableKnownDevice { |
61 | 110 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
62 | 111 | write!( |
63 | 112 | f, |
64 | 113 | "{} ({})", |
65 | | - self.device_info.name, self.device_info.device_id |
| 114 | + &self.device_info.name, |
| 115 | + hex::encode(&self.device_info.public_key) |
66 | 116 | ) |
67 | 117 | } |
68 | 118 | } |
69 | 119 |
|
70 | | -unsafe impl<'d> Send for CableKnownDevice<'d> {} |
71 | | -unsafe impl<'d> Sync for CableKnownDevice<'d> {} |
| 120 | +unsafe impl Send for CableKnownDevice {} |
| 121 | +unsafe impl Sync for CableKnownDevice {} |
72 | 122 |
|
73 | 123 | #[async_trait] |
74 | | -impl<'d> Device<'d, Cable, CableChannel<'d>> for CableKnownDevice<'d> { |
| 124 | +impl<'d> Device<'d, Cable, CableChannel<'d>> for CableKnownDevice { |
75 | 125 | async fn channel(&'d mut self) -> Result<(CableChannel, mpsc::Receiver<UxUpdate>), Error> { |
76 | 126 | todo!() |
77 | 127 | } |
|
0 commit comments