Skip to content

Commit 640a344

Browse files
Hybrid transport: Add state-assisted transactions (WiP)
1 parent dc23dae commit 640a344

File tree

5 files changed

+262
-55
lines changed

5 files changed

+262
-55
lines changed

libwebauthn/examples/webauthn_cable.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::error::Error;
22
use std::io::{self, Write};
3+
use std::sync::Arc;
34
use std::time::Duration;
45

56
use libwebauthn::pin::PinRequestReason;
@@ -78,12 +79,14 @@ async fn handle_updates(mut state_recv: Receiver<UxUpdate>) {
7879
pub async fn main() -> Result<(), Box<dyn Error>> {
7980
setup_logging();
8081

81-
let _device_info_store: Box<dyn CableKnownDeviceInfoStore> =
82-
Box::new(EphemeralDeviceInfoStore::default());
82+
let device_info_store: Arc<dyn CableKnownDeviceInfoStore> =
83+
Arc::new(EphemeralDeviceInfoStore::default());
8384

8485
// Create QR code
85-
let mut device: CableQrCodeDevice<'_> =
86-
CableQrCodeDevice::new_transient(QrCodeOperationHint::MakeCredential);
86+
let mut device: CableQrCodeDevice = CableQrCodeDevice::new_persistent(
87+
QrCodeOperationHint::MakeCredential,
88+
device_info_store.clone(),
89+
);
8790

8891
println!("Created QR code, awaiting for advertisement.");
8992
let qr_code = QrCode::new(device.qr_code.to_string()).unwrap();
@@ -148,8 +151,10 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
148151
};
149152

150153
// Create QR code
151-
let mut device: CableQrCodeDevice<'_> =
152-
CableQrCodeDevice::new_transient(QrCodeOperationHint::GetAssertionRequest);
154+
let mut device: CableQrCodeDevice = CableQrCodeDevice::new_persistent(
155+
QrCodeOperationHint::GetAssertionRequest,
156+
device_info_store.clone(),
157+
);
153158

154159
println!("Created QR code, awaiting for advertisement.");
155160
let qr_code = QrCode::new(device.qr_code.to_string()).unwrap();

libwebauthn/src/transport/cable/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use super::qr_code_device::CableQrCodeDevice;
2222

2323
#[derive(Debug)]
2424
pub enum CableChannelDevice<'d> {
25-
QrCode(&'d CableQrCodeDevice<'d>),
26-
Known(&'d CableKnownDevice<'d>),
25+
QrCode(&'d CableQrCodeDevice),
26+
Known(&'d CableKnownDevice),
2727
}
2828

2929
#[derive(Debug)]

libwebauthn/src/transport/cable/known_devices.rs

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,131 @@
1+
use std::collections::HashMap;
12
use std::fmt::{Debug, Display};
3+
use std::sync::Arc;
24

35
use crate::transport::error::Error;
46
use crate::transport::Device;
7+
use crate::webauthn::TransportError;
58
use crate::UxUpdate;
69

710
use async_trait::async_trait;
11+
use futures::lock::Mutex;
812
use tokio::sync::mpsc;
13+
use tracing::{debug, trace};
914

1015
use super::channel::CableChannel;
16+
use super::tunnel::CableLinkingInfo;
1117
use super::Cable;
1218

1319
#[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);
20+
pub trait CableKnownDeviceInfoStore: Debug + Send + Sync {
21+
/// Called whenever a known device should be added or updated.
22+
async fn put_known_device(&self, device_id: &CableKnownDeviceId, device: &CableKnownDeviceInfo);
1723
/// Called whenever a known device becomes permanently unavailable.
18-
async fn delete_known_device(&mut self, device_id: String);
24+
async fn delete_known_device(&self, device_id: &CableKnownDeviceId);
1925
}
2026

21-
/// A no-op known-device store for ephemeral-only implementations.
27+
/// An in-memory store for testing purposes.
2228
#[derive(Debug, Default, Clone)]
2329
pub struct EphemeralDeviceInfoStore {
24-
pub last_device_info: Option<CableKnownDeviceInfo>,
30+
pub known_devices: Arc<Mutex<HashMap<CableKnownDeviceId, CableKnownDeviceInfo>>>,
31+
}
32+
33+
impl EphemeralDeviceInfoStore {
34+
pub fn new() -> Self {
35+
Self {
36+
known_devices: Arc::new(Mutex::new(HashMap::new())),
37+
}
38+
}
2539
}
2640

2741
unsafe impl Send for EphemeralDeviceInfoStore {}
2842

2943
#[async_trait]
3044
impl CableKnownDeviceInfoStore for EphemeralDeviceInfoStore {
31-
async fn put_known_device(&mut self, device: &CableKnownDeviceInfo) {
32-
self.last_device_info = Some(device.clone())
45+
async fn put_known_device(
46+
&self,
47+
device_id: &CableKnownDeviceId,
48+
device: &CableKnownDeviceInfo,
49+
) {
50+
debug!(?device_id, "Inserting or updating known device");
51+
trace!(?device);
52+
let mut known_devices = self.known_devices.lock().await;
53+
known_devices.insert(device_id.clone(), device.clone());
3354
}
3455

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-
}
56+
async fn delete_known_device(&self, device_id: &CableKnownDeviceId) {
57+
debug!(?device_id, "Deleting known device");
58+
let mut known_devices = self.known_devices.lock().await;
59+
known_devices.remove(device_id);
4160
}
4261
}
4362

63+
pub type CableKnownDeviceId = String;
64+
4465
#[derive(Debug, Clone)]
4566
pub struct CableKnownDeviceInfo {
46-
pub device_id: String,
4767
pub contact_id: Vec<u8>,
4868
pub link_id: [u8; 8],
4969
pub link_secret: [u8; 32],
5070
pub public_key: [u8; 65],
5171
pub name: String,
72+
pub tunnel_domain: String,
73+
}
74+
75+
impl From<&CableLinkingInfo> for CableKnownDeviceId {
76+
fn from(linking_info: &CableLinkingInfo) -> Self {
77+
hex::encode(&linking_info.authenticator_public_key)
78+
}
79+
}
80+
81+
impl CableKnownDeviceInfo {
82+
pub(crate) fn new(tunnel_domain: &str, linking_info: &CableLinkingInfo) -> Result<Self, Error> {
83+
let info = Self {
84+
contact_id: linking_info.contact_id.to_vec(),
85+
link_id: linking_info
86+
.link_id
87+
.clone()
88+
.try_into()
89+
.map_err(|_| Error::Transport(TransportError::InvalidFraming))?,
90+
link_secret: linking_info
91+
.link_secret
92+
.clone()
93+
.try_into()
94+
.map_err(|_| Error::Transport(TransportError::InvalidFraming))?,
95+
public_key: linking_info
96+
.authenticator_public_key
97+
.clone()
98+
.try_into()
99+
.map_err(|_| Error::Transport(TransportError::InvalidFraming))?,
100+
name: linking_info.authenticator_name.clone(),
101+
tunnel_domain: tunnel_domain.to_string(),
102+
};
103+
Ok(info)
104+
}
52105
}
53106

54107
#[derive(Debug)]
55-
pub struct CableKnownDevice<'d> {
108+
pub struct CableKnownDevice {
56109
pub device_info: CableKnownDeviceInfo,
57-
_store: &'d mut Box<dyn CableKnownDeviceInfoStore>,
110+
_store: Arc<dyn CableKnownDeviceInfoStore>,
58111
}
59112

60-
impl<'d> Display for CableKnownDevice<'d> {
113+
impl Display for CableKnownDevice {
61114
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62115
write!(
63116
f,
64117
"{} ({})",
65-
self.device_info.name, self.device_info.device_id
118+
&self.device_info.name,
119+
hex::encode(&self.device_info.public_key)
66120
)
67121
}
68122
}
69123

70-
unsafe impl<'d> Send for CableKnownDevice<'d> {}
71-
unsafe impl<'d> Sync for CableKnownDevice<'d> {}
124+
unsafe impl Send for CableKnownDevice {}
125+
unsafe impl Sync for CableKnownDevice {}
72126

73127
#[async_trait]
74-
impl<'d> Device<'d, Cable, CableChannel<'d>> for CableKnownDevice<'d> {
128+
impl<'d> Device<'d, Cable, CableChannel<'d>> for CableKnownDevice {
75129
async fn channel(&'d mut self) -> Result<(CableChannel, mpsc::Receiver<UxUpdate>), Error> {
76130
todo!()
77131
}

libwebauthn/src/transport/cable/qr_code_device.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::{Debug, Display};
22
use std::pin::pin;
3+
use std::sync::Arc;
34
use std::time::SystemTime;
45

56
use async_trait::async_trait;
@@ -81,16 +82,16 @@ impl ToString for CableQrCode {
8182

8283
/// Represents a new device which will connect by scanning a QR code.
8384
/// This could be a new device, or an ephmemeral device whose details were not stored.
84-
pub struct CableQrCodeDevice<'d> {
85+
pub struct CableQrCodeDevice {
8586
/// The QR code to be scanned by the new authenticator.
8687
pub qr_code: CableQrCode,
8788
/// An ephemeral private, corresponding to the public key within the QR code.
8889
pub private_key: NonZeroScalar,
8990
/// An optional reference to the store. This may be None, if no persistence is desired.
90-
store: Option<&'d mut Box<dyn CableKnownDeviceInfoStore>>,
91+
pub(crate) store: Option<Arc<dyn CableKnownDeviceInfoStore>>,
9192
}
9293

93-
impl Debug for CableQrCodeDevice<'_> {
94+
impl Debug for CableQrCodeDevice {
9495
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9596
f.debug_struct("CableQrCodeDevice")
9697
.field("qr_code", &self.qr_code)
@@ -125,20 +126,20 @@ impl From<&[u8]> for DecryptedAdvert {
125126
}
126127
}
127128

128-
impl<'d> CableQrCodeDevice<'d> {
129+
impl CableQrCodeDevice {
129130
/// Generates a QR code, linking the provided known-device store. A device scanning
130131
/// this QR code may be persisted to the store after a successful connection.
131132
pub fn new_persistent(
132133
hint: QrCodeOperationHint,
133-
store: &'d mut Box<dyn CableKnownDeviceInfoStore>,
134+
store: Arc<dyn CableKnownDeviceInfoStore>,
134135
) -> Self {
135136
Self::new(hint, true, Some(store))
136137
}
137138

138139
fn new(
139140
hint: QrCodeOperationHint,
140141
state_assisted: bool,
141-
store: Option<&'d mut Box<dyn CableKnownDeviceInfoStore>>,
142+
store: Option<Arc<dyn CableKnownDeviceInfoStore>>,
142143
) -> Self {
143144
let private_key_scalar = NonZeroScalar::random(&mut OsRng);
144145
let private_key = SecretKey::from_bytes(&private_key_scalar.to_bytes()).unwrap();
@@ -176,7 +177,7 @@ impl<'d> CableQrCodeDevice<'d> {
176177
}
177178
}
178179

179-
impl CableQrCodeDevice<'_> {
180+
impl CableQrCodeDevice {
180181
/// Generates a QR code, without any known-device store. A device scanning this QR code
181182
/// will not be persisted.
182183
pub fn new_transient(hint: QrCodeOperationHint) -> Self {
@@ -231,18 +232,18 @@ impl CableQrCodeDevice<'_> {
231232
}
232233
}
233234

234-
unsafe impl Send for CableQrCodeDevice<'_> {}
235+
unsafe impl Send for CableQrCodeDevice {}
235236

236-
unsafe impl Sync for CableQrCodeDevice<'_> {}
237+
unsafe impl Sync for CableQrCodeDevice {}
237238

238-
impl Display for CableQrCodeDevice<'_> {
239+
impl Display for CableQrCodeDevice {
239240
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240241
write!(f, "CableQrCodeDevice")
241242
}
242243
}
243244

244245
#[async_trait]
245-
impl<'d> Device<'d, Cable, CableChannel<'d>> for CableQrCodeDevice<'_> {
246+
impl<'d> Device<'d, Cable, CableChannel<'d>> for CableQrCodeDevice {
246247
async fn channel(&'d mut self) -> Result<(CableChannel<'d>, mpsc::Receiver<UxUpdate>), Error> {
247248
let (_device, advert) = self.await_advertisement().await?;
248249

0 commit comments

Comments
 (0)