Skip to content

Commit dd073b8

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

File tree

5 files changed

+259
-55
lines changed

5 files changed

+259
-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: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,127 @@
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;
913

1014
use super::channel::CableChannel;
15+
use super::tunnel::CableLinkingInfo;
1116
use super::Cable;
1217

1318
#[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);
1722
/// 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);
1924
}
2025

21-
/// A no-op known-device store for ephemeral-only implementations.
26+
/// An in-memory store for testing purposes.
2227
#[derive(Debug, Default, Clone)]
2328
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+
}
2538
}
2639

2740
unsafe impl Send for EphemeralDeviceInfoStore {}
2841

2942
#[async_trait]
3043
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());
3351
}
3452

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);
4156
}
4257
}
4358

59+
pub type CableKnownDeviceId = String;
60+
4461
#[derive(Debug, Clone)]
4562
pub struct CableKnownDeviceInfo {
46-
pub device_id: String,
4763
pub contact_id: Vec<u8>,
4864
pub link_id: [u8; 8],
4965
pub link_secret: [u8; 32],
5066
pub public_key: [u8; 65],
5167
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+
}
52101
}
53102

54103
#[derive(Debug)]
55-
pub struct CableKnownDevice<'d> {
104+
pub struct CableKnownDevice {
56105
pub device_info: CableKnownDeviceInfo,
57-
_store: &'d mut Box<dyn CableKnownDeviceInfoStore>,
106+
_store: Arc<dyn CableKnownDeviceInfoStore>,
58107
}
59108

60-
impl<'d> Display for CableKnownDevice<'d> {
109+
impl Display for CableKnownDevice {
61110
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62111
write!(
63112
f,
64113
"{} ({})",
65-
self.device_info.name, self.device_info.device_id
114+
&self.device_info.name,
115+
hex::encode(&self.device_info.public_key)
66116
)
67117
}
68118
}
69119

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 {}
72122

73123
#[async_trait]
74-
impl<'d> Device<'d, Cable, CableChannel<'d>> for CableKnownDevice<'d> {
124+
impl<'d> Device<'d, Cable, CableChannel<'d>> for CableKnownDevice {
75125
async fn channel(&'d mut self) -> Result<(CableChannel, mpsc::Receiver<UxUpdate>), Error> {
76126
todo!()
77127
}

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)