-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathmodel.rs
More file actions
336 lines (282 loc) · 9.38 KB
/
Copy pathmodel.rs
File metadata and controls
336 lines (282 loc) · 9.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
use std::fmt::Display;
use serde::{Deserialize, Serialize};
use zvariant::{SerializeDict, Type};
pub use libwebauthn::ops::webauthn::{
Assertion, GetAssertionRequest, MakeCredentialRequest, MakeCredentialResponse,
};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Credential {
pub id: String,
pub name: String,
pub username: Option<String>,
}
#[derive(Clone, Debug)]
pub enum CredentialRequest {
CreatePublicKeyCredentialRequest(MakeCredentialRequest),
GetPublicKeyCredentialRequest(GetAssertionRequest),
}
#[derive(Clone, Debug)]
pub enum CredentialResponse {
CreatePublicKeyCredentialResponse(Box<MakeCredentialResponseInternal>),
GetPublicKeyCredentialResponse(Box<GetAssertionResponseInternal>),
}
impl CredentialResponse {
pub fn from_make_credential(
response: &MakeCredentialResponse,
transports: &[&str],
modality: &str,
) -> CredentialResponse {
CredentialResponse::CreatePublicKeyCredentialResponse(Box::new(
MakeCredentialResponseInternal::new(
response.clone(),
transports.iter().map(|s| s.to_string()).collect(),
modality.to_string(),
),
))
}
pub fn from_get_assertion(assertion: &Assertion, modality: &str) -> CredentialResponse {
CredentialResponse::GetPublicKeyCredentialResponse(Box::new(
GetAssertionResponseInternal::new(assertion.clone(), modality.to_string()),
))
}
}
#[derive(Clone, Debug)]
pub struct MakeCredentialResponseInternal {
pub ctap: MakeCredentialResponse,
pub transport: Vec<String>,
pub attachment_modality: String,
}
impl MakeCredentialResponseInternal {
pub fn new(
response: MakeCredentialResponse,
transport: Vec<String>,
attachment_modality: String,
) -> Self {
Self {
ctap: response,
transport,
attachment_modality,
}
}
}
#[derive(Clone, Debug)]
pub struct GetAssertionResponseInternal {
pub ctap: Assertion,
pub attachment_modality: String,
}
impl GetAssertionResponseInternal {
pub fn new(ctap: Assertion, attachment_modality: String) -> Self {
Self {
ctap,
attachment_modality,
}
}
}
#[derive(SerializeDict, Type)]
#[zvariant(signature = "dict", rename_all = "camelCase")]
pub struct GetClientCapabilitiesResponse {
pub conditional_create: bool,
pub conditional_get: bool,
pub hybrid_transport: bool,
pub passkey_platform_authenticator: bool,
pub user_verifying_platform_authenticator: bool,
pub related_origins: bool,
pub signal_all_accepted_credentials: bool,
pub signal_current_user_details: bool,
pub signal_unknown_credential: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum CredentialType {
Passkey,
// Password,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Device {
pub id: String,
pub transport: Transport,
}
#[derive(Debug, Serialize, Deserialize, Type)]
pub enum Operation {
Create,
Get,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Transport {
Ble,
HybridLinked,
HybridQr,
Internal,
Nfc,
Usb,
}
impl TryInto<Transport> for String {
type Error = String;
fn try_into(self) -> Result<Transport, String> {
let value: &str = self.as_ref();
value.try_into()
}
}
impl TryInto<Transport> for &str {
type Error = String;
fn try_into(self) -> Result<Transport, String> {
match self {
"BLE" => Ok(Transport::Ble),
"HybridLinked" => Ok(Transport::HybridLinked),
"HybridQr" => Ok(Transport::HybridQr),
"Internal" => Ok(Transport::Internal),
"NFC" => Ok(Transport::Nfc),
"USB" => Ok(Transport::Usb),
_ => Err(format!("Unrecognized transport: {}", self.to_owned())),
}
}
}
impl From<Transport> for String {
fn from(val: Transport) -> Self {
val.as_str().to_string()
}
}
impl Transport {
pub fn as_str(&self) -> &'static str {
match self {
Transport::Ble => "BLE",
Transport::HybridLinked => "HybridLinked",
Transport::HybridQr => "HybridQr",
Transport::Internal => "Internal",
Transport::Nfc => "NFC",
Transport::Usb => "USB",
}
}
}
#[derive(Serialize, Deserialize)]
pub enum ViewUpdate {
SetTitle(String),
SetDevices(Vec<Device>),
SetCredentials(Vec<Credential>),
WaitingForDevice(Device),
SelectingDevice,
UsbNeedsPin { attempts_left: Option<u32> },
UsbNeedsUserVerification { attempts_left: Option<u32> },
UsbNeedsUserPresence,
HybridNeedsQrCode(String),
HybridConnecting,
HybridConnected,
Completed,
Cancelled,
Failed(String),
}
#[derive(Clone, Debug, Default)]
pub enum HybridState {
/// Default state, not listening for hybrid transport.
#[default]
Idle,
/// QR code flow is starting, awaiting QR code scan and BLE advert from phone.
Started(String),
/// BLE advert received, connecting to caBLE tunnel with shared secret.
Connecting,
/// Connected to device via caBLE tunnel.
Connected,
/// Credential received over tunnel.
Completed,
// This isn't actually sent from the server.
UserCancelled,
/// Failed to receive a credential
Failed,
}
/// Used to share public state between credential service and UI.
#[derive(Clone, Debug, Default)]
pub enum UsbState {
/// Not polling for FIDO USB device.
#[default]
Idle,
/// Awaiting FIDO USB device to be plugged in.
Waiting,
// When we encounter multiple devices, we let all of them blink and continue
// with the one that was tapped.
SelectingDevice,
/// USB device connected, prompt user to tap
Connected,
/// The device needs the PIN to be entered.
NeedsPin {
attempts_left: Option<u32>,
},
/// The device needs on-device user verification.
NeedsUserVerification {
attempts_left: Option<u32>,
},
/// The device needs evidence of user presence (e.g. touch) to release the credential.
NeedsUserPresence,
// TODO: implement cancellation
// This isn't actually sent from the server.
//UserCancelled,
/// Multiple credentials have been found and the user has to select which to use
SelectCredential {
/// List of user-identities to decide which to use.
creds: Vec<Credential>,
},
/// USB tapped, received credential
Completed,
/// Interaction with the authenticator failed.
Failed(Error),
}
#[derive(Debug)]
pub enum BackgroundEvent {
UsbStateChanged(UsbState),
HybridQrStateChanged(HybridState),
}
#[derive(Debug, Clone)]
pub enum Error {
/// Some unknown error with the authenticator occurred.
AuthenticatorError,
/// No matching credentials were found on the device.
NoCredentials,
/// Too many incorrect PIN attempts, and authenticator must be removed and
/// reinserted to continue any more PIN attempts.
///
/// Note that this is different than exhausting the PIN count that fully
/// locks out the device.
PinAttemptsExhausted,
// TODO: We may want to hide the details on this variant from the public API.
/// Something went wrong with the credential service itself, not the authenticator.
Internal(String),
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AuthenticatorError => f.write_str("AuthenticatorError"),
Self::NoCredentials => f.write_str("NoCredentials"),
Self::PinAttemptsExhausted => f.write_str("PinAttemptsExhausted"),
Self::Internal(s) => write!(f, "InternalError: {s}"),
}
}
}
pub enum WebAuthnError {
/// The ceremony was cancelled by an AbortController. See § 5.6 Abort
/// Operations with AbortSignal and § 1.3.4 Aborting Authentication
/// Operations.
AbortError,
/// Either `residentKey` was set to required and no available authenticator
/// supported resident keys, or `userVerification` was set to required and no
/// available authenticator could perform user verification.
ConstraintError,
/// The authenticator used in the ceremony recognized an entry in
/// `excludeCredentials` after the user consented to registering a credential.
InvalidStateError,
/// No entry in `pubKeyCredParams` had a type property of `public-key`, or the
/// authenticator did not support any of the signature algorithms specified
/// in `pubKeyCredParams`.
NotSupportedError,
/// The effective domain was not a valid domain, or `rp.id` was not equal to
/// or a registrable domain suffix of the effective domain. In the latter
/// case, the client does not support related origin requests or the related
/// origins validation procedure failed.
SecurityError,
/// A catch-all error covering a wide range of possible reasons, including
/// common ones like the user canceling out of the ceremony. Some of these
/// causes are documented throughout this spec, while others are
/// client-specific.
NotAllowedError,
/// The options argument was not a valid `CredentialCreationOptions` value, or
/// the value of `user.id` was empty or was longer than 64 bytes.
TypeError,
}