Skip to content

Commit a4fd9a4

Browse files
refactor(error): per-transport Channel::Error types and generic WebAuthnError
1 parent dca2054 commit a4fd9a4

58 files changed

Lines changed: 1606 additions & 1287 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

libwebauthn-tests/tests/large_blob.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use libwebauthn::proto::ctap2::{
1313
Ctap2PublicKeyCredentialUserEntity,
1414
};
1515
use libwebauthn::transport::{Channel, ChannelSettings, Device};
16-
use libwebauthn::webauthn::{Error, PlatformError, WebAuthn};
16+
use libwebauthn::webauthn::{PlatformError, WebAuthn, WebAuthnError};
1717
use libwebauthn::UvUpdate;
1818
use libwebauthn_tests::virt::get_virtual_device;
1919
use rand::{thread_rng, Rng};
@@ -635,7 +635,10 @@ async fn test_webauthn_large_blob_write_requires_single_allow_credential() {
635635
.webauthn_get_assertion(&two)
636636
.await
637637
.expect_err("write with two allowCredentials must be rejected");
638-
assert_eq!(err, Error::Platform(PlatformError::NotSupported));
638+
assert!(matches!(
639+
err,
640+
WebAuthnError::Platform(PlatformError::NotSupported)
641+
));
639642

640643
let mut none = ga_request(
641644
&cred_a,
@@ -647,7 +650,10 @@ async fn test_webauthn_large_blob_write_requires_single_allow_credential() {
647650
.webauthn_get_assertion(&none)
648651
.await
649652
.expect_err("write with empty allowCredentials must be rejected");
650-
assert_eq!(err, Error::Platform(PlatformError::NotSupported));
653+
assert!(matches!(
654+
err,
655+
WebAuthnError::Platform(PlatformError::NotSupported)
656+
));
651657

652658
update_handle.await.unwrap();
653659
}

libwebauthn-tests/tests/preflight.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use libwebauthn::proto::ctap2::{
1111
};
1212
use libwebauthn::proto::CtapError;
1313
use libwebauthn::transport::hid::channel::HidChannel;
14+
use libwebauthn::transport::hid::HidError;
1415
use libwebauthn::transport::{Channel, ChannelSettings, Device};
15-
use libwebauthn::webauthn::{Error, WebAuthn};
16+
use libwebauthn::webauthn::{WebAuthn, WebAuthnError};
1617
use libwebauthn::UvUpdate;
1718
use libwebauthn_tests::virt::get_virtual_device;
1819
use rand::{thread_rng, Rng};
@@ -36,7 +37,7 @@ async fn make_credential_call(
3637
channel: &mut HidChannel<'_>,
3738
user_id: &[u8],
3839
exclude_list: Option<Vec<Ctap2PublicKeyCredentialDescriptor>>,
39-
) -> Result<(Ctap2PublicKeyCredentialDescriptor, [u8; 32]), Error> {
40+
) -> Result<(Ctap2PublicKeyCredentialDescriptor, [u8; 32]), WebAuthnError<HidError>> {
4041
make_credential_call_with_rp(channel, user_id, exclude_list, "example.org").await
4142
}
4243

@@ -45,7 +46,7 @@ async fn make_credential_call_with_rp(
4546
user_id: &[u8],
4647
exclude_list: Option<Vec<Ctap2PublicKeyCredentialDescriptor>>,
4748
rp_id: &str,
48-
) -> Result<(Ctap2PublicKeyCredentialDescriptor, [u8; 32]), Error> {
49+
) -> Result<(Ctap2PublicKeyCredentialDescriptor, [u8; 32]), WebAuthnError<HidError>> {
4950
let challenge: [u8; 32] = thread_rng().gen();
5051
let make_credentials_request = MakeCredentialRequest {
5152
origin: rp_id.to_owned(),
@@ -70,7 +71,7 @@ async fn make_credential_call_with_rp(
7071
async fn get_assertion_call(
7172
channel: &mut HidChannel<'_>,
7273
allow_list: Vec<Ctap2PublicKeyCredentialDescriptor>,
73-
) -> Result<GetAssertionResponse, Error> {
74+
) -> Result<GetAssertionResponse, WebAuthnError<HidError>> {
7475
let challenge: [u8; 32] = thread_rng().gen();
7576
let get_assertion = GetAssertionRequest {
7677
origin: "example.org".to_owned(),
@@ -175,7 +176,7 @@ async fn preflight_mixed_exclude_list() {
175176
let res = make_credential_call(&mut channel, &user_id, Some(exclude_list)).await;
176177
assert!(matches!(
177178
res,
178-
Err(Error::Ctap(CtapError::CredentialExcluded))
179+
Err(WebAuthnError::Ctap(CtapError::CredentialExcluded))
179180
));
180181

181182
expected_uv_updates(
@@ -207,7 +208,10 @@ async fn preflight_no_allow_list() {
207208

208209
let allow_list = Vec::new();
209210
let res = get_assertion_call(&mut channel, allow_list).await;
210-
assert!(matches!(res, Err(Error::Ctap(CtapError::NoCredentials))));
211+
assert!(matches!(
212+
res,
213+
Err(WebAuthnError::Ctap(CtapError::NoCredentials))
214+
));
211215

212216
expected_uv_updates(
213217
state_recv,
@@ -239,7 +243,10 @@ async fn preflight_nonsense_allow_list() {
239243
assert!(filtered_list.is_empty());
240244

241245
let res = get_assertion_call(&mut channel, allow_list).await;
242-
assert!(matches!(res, Err(Error::Ctap(CtapError::NoCredentials))));
246+
assert!(matches!(
247+
res,
248+
Err(WebAuthnError::Ctap(CtapError::NoCredentials))
249+
));
243250

244251
expected_uv_updates(
245252
state_recv,

libwebauthn-tests/tests/prf.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use libwebauthn::ops::webauthn::{
88
use libwebauthn::pin::PinManagement;
99
use libwebauthn::proto::ctap2::{Ctap2PinUvAuthProtocol, Ctap2PublicKeyCredentialDescriptor};
1010
use libwebauthn::transport::hid::channel::HidChannel;
11+
use libwebauthn::transport::hid::HidError;
1112
use libwebauthn::transport::{Channel, ChannelSettings, Ctap2AuthTokenStore, Device};
12-
use libwebauthn::webauthn::{Error as WebAuthnError, PlatformError, WebAuthn};
13+
use libwebauthn::webauthn::{PlatformError, WebAuthn, WebAuthnError};
1314
use libwebauthn::UvUpdate;
1415
use libwebauthn::{
1516
ops::webauthn::{MakeCredentialRequest, ResidentKeyRequirement, UserVerificationRequirement},
@@ -488,7 +489,7 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) {
488489
&challenge,
489490
prf,
490491
"Wrongly encoded credential_id",
491-
WebAuthnError::Platform(PlatformError::SyntaxError),
492+
PlatformError::SyntaxError,
492493
)
493494
.await;
494495

@@ -512,7 +513,7 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) {
512513
&challenge,
513514
prf,
514515
"Empty credential_id",
515-
WebAuthnError::Platform(PlatformError::SyntaxError),
516+
PlatformError::SyntaxError,
516517
)
517518
.await;
518519

@@ -536,7 +537,7 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) {
536537
&challenge,
537538
prf,
538539
"Empty allow_list, set eval_by_credential",
539-
WebAuthnError::Platform(PlatformError::NotSupported),
540+
PlatformError::NotSupported,
540541
)
541542
.await;
542543

@@ -619,7 +620,7 @@ async fn run_failed_test(
619620
challenge: &[u8; 32],
620621
prf: PrfInput,
621622
printoutput: &str,
622-
expected_error: WebAuthnError,
623+
expected_error: PlatformError,
623624
) {
624625
let get_assertion = GetAssertionRequest {
625626
relying_party_id: "example.org".to_owned(),
@@ -635,7 +636,7 @@ async fn run_failed_test(
635636
top_origin: None,
636637
};
637638

638-
let response: Result<(), WebAuthnError> = loop {
639+
let response: Result<(), WebAuthnError<HidError>> = loop {
639640
match channel.webauthn_get_assertion(&get_assertion).await {
640641
Ok(_) => panic!("Success, even though it should have errored out!"),
641642
Err(WebAuthnError::Ctap(ctap_error)) => {
@@ -649,7 +650,10 @@ async fn run_failed_test(
649650
};
650651
};
651652

652-
assert_eq!(response, Err(expected_error), "{printoutput}:");
653+
match response {
654+
Err(WebAuthnError::Platform(got)) => assert_eq!(got, expected_error, "{printoutput}:"),
655+
other => panic!("{printoutput}: expected {expected_error:?}, got {other:?}"),
656+
}
653657
println!("Success for test: {printoutput}")
654658
}
655659

libwebauthn/examples/common/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ macro_rules! retry_user_errors {
123123
loop {
124124
match $call.await {
125125
Ok(response) => break Ok(response),
126-
Err(libwebauthn::webauthn::Error::Ctap(ctap_error)) => {
126+
Err(libwebauthn::webauthn::WebAuthnError::Ctap(ctap_error)) => {
127127
if ctap_error.is_retryable_user_error() {
128128
println!("Oops, try again! Error: {}", ctap_error);
129129
continue;
130130
}
131-
break Err(libwebauthn::webauthn::Error::Ctap(ctap_error));
131+
break Err(libwebauthn::webauthn::WebAuthnError::Ctap(ctap_error));
132132
}
133133
Err(err) => break Err(err),
134134
}

libwebauthn/examples/features/webauthn_preflight_hid.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use libwebauthn::proto::ctap2::{
1414
};
1515
use libwebauthn::transport::hid::list_devices;
1616
use libwebauthn::transport::{Channel, ChannelSettings, Device};
17-
use libwebauthn::webauthn::{CtapError, Error as WebAuthnError, WebAuthn};
17+
use libwebauthn::webauthn::{CtapError, WebAuthn, WebAuthnError};
1818

1919
#[path = "../common/mod.rs"]
2020
mod common;
@@ -103,11 +103,11 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
103103
Ok(())
104104
}
105105

106-
async fn make_credential_call(
107-
channel: &mut impl Channel,
106+
async fn make_credential_call<C: Channel>(
107+
channel: &mut C,
108108
user_id: &[u8],
109109
exclude_list: Option<Vec<Ctap2PublicKeyCredentialDescriptor>>,
110-
) -> Result<Ctap2PublicKeyCredentialDescriptor, WebAuthnError> {
110+
) -> Result<Ctap2PublicKeyCredentialDescriptor, WebAuthnError<C::Error>> {
111111
let challenge: [u8; 32] = thread_rng().gen();
112112
let make_credentials_request = MakeCredentialRequest {
113113
challenge: Vec::from(challenge),
@@ -127,10 +127,10 @@ async fn make_credential_call(
127127
.map(|x| (&x.authenticator_data).try_into().unwrap())
128128
}
129129

130-
async fn get_assertion_call(
131-
channel: &mut impl Channel,
130+
async fn get_assertion_call<C: Channel>(
131+
channel: &mut C,
132132
allow_list: Vec<Ctap2PublicKeyCredentialDescriptor>,
133-
) -> Result<GetAssertionResponse, WebAuthnError> {
133+
) -> Result<GetAssertionResponse, WebAuthnError<C::Error>> {
134134
let challenge: [u8; 32] = thread_rng().gen();
135135
let get_assertion = GetAssertionRequest {
136136
relying_party_id: "example.org".to_owned(),

libwebauthn/examples/features/webauthn_prf_hid.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use libwebauthn::proto::ctap2::{
1717
};
1818
use libwebauthn::transport::hid::list_devices;
1919
use libwebauthn::transport::{Channel as _, ChannelSettings, Device};
20-
use libwebauthn::webauthn::{Error as WebAuthnError, PlatformError, WebAuthn};
20+
use libwebauthn::webauthn::{PlatformError, WebAuthn, WebAuthnError};
2121

2222
#[path = "../common/mod.rs"]
2323
mod common;
@@ -273,7 +273,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
273273
eval_by_credential,
274274
},
275275
"Wrongly encoded credential_id",
276-
WebAuthnError::Platform(PlatformError::SyntaxError),
276+
PlatformError::SyntaxError,
277277
)
278278
.await;
279279

@@ -295,7 +295,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
295295
eval_by_credential,
296296
},
297297
"Empty credential_id",
298-
WebAuthnError::Platform(PlatformError::SyntaxError),
298+
PlatformError::SyntaxError,
299299
)
300300
.await;
301301

@@ -317,7 +317,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
317317
eval_by_credential,
318318
},
319319
"Empty allow_list, set eval_by_credential",
320-
WebAuthnError::Platform(PlatformError::NotSupported),
320+
PlatformError::NotSupported,
321321
)
322322
.await;
323323
}
@@ -360,7 +360,7 @@ async fn run_failed_test(
360360
challenge: &[u8; 32],
361361
prf: PrfInput,
362362
printoutput: &str,
363-
expected_error: WebAuthnError,
363+
expected_error: PlatformError,
364364
) {
365365
let get_assertion = GetAssertionRequest {
366366
relying_party_id: "example.org".to_owned(),
@@ -376,9 +376,12 @@ async fn run_failed_test(
376376
timeout: TIMEOUT,
377377
};
378378

379-
let response = retry_user_errors!(channel.webauthn_get_assertion(&get_assertion))
380-
.map(|_| panic!("Success, even though it should have errored out!"));
379+
let response = retry_user_errors!(channel.webauthn_get_assertion(&get_assertion));
381380

382-
assert_eq!(response, Err(expected_error), "{printoutput}:");
381+
match response {
382+
Ok(_) => panic!("Success, even though it should have errored out!"),
383+
Err(WebAuthnError::Platform(got)) => assert_eq!(got, expected_error, "{printoutput}:"),
384+
Err(other) => panic!("{printoutput}: expected {expected_error:?}, got {other:?}"),
385+
}
383386
println!("Success for test: {printoutput}")
384387
}

libwebauthn/examples/management/bio_enrollment_hid.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use text_io::read;
77
use libwebauthn::management::BioEnrollment;
88
use libwebauthn::proto::ctap2::{Ctap2, Ctap2GetInfoResponse, Ctap2LastEnrollmentSampleStatus};
99
use libwebauthn::transport::hid::list_devices;
10+
use libwebauthn::transport::hid::HidError;
1011
use libwebauthn::transport::{Channel as _, ChannelSettings, Device};
11-
use libwebauthn::webauthn::Error as WebAuthnError;
12+
use libwebauthn::webauthn::WebAuthnError;
1213

1314
#[path = "../common/mod.rs"]
1415
mod common;
@@ -101,7 +102,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
101102
}
102103

103104
let idx = common::prompt_index(options.len());
104-
let result: Result<String, WebAuthnError> = match options[idx] {
105+
let result: Result<String, WebAuthnError<HidError>> = match options[idx] {
105106
Operation::GetModality => {
106107
retry_user_errors!(channel.get_bio_modality(TIMEOUT)).map(|x| format!("{x:?}"))
107108
}

libwebauthn/examples/management/cred_management_hid.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use libwebauthn::proto::ctap2::{
77
};
88
use libwebauthn::proto::CtapError;
99
use libwebauthn::transport::hid::list_devices;
10+
use libwebauthn::transport::hid::HidError;
1011
use libwebauthn::transport::{Channel as _, ChannelSettings, Device};
11-
use libwebauthn::webauthn::Error as WebAuthnError;
12+
use libwebauthn::webauthn::WebAuthnError;
1213
use std::io::{self, Write};
1314
use text_io::read;
1415

@@ -31,7 +32,7 @@ fn format_credential(cred: &Ctap2CredentialData) -> String {
3132

3233
async fn enumerate_rps<T: CredentialManagement>(
3334
channel: &mut T,
34-
) -> Result<Vec<Ctap2RPData>, WebAuthnError> {
35+
) -> Result<Vec<Ctap2RPData>, WebAuthnError<T::Error>> {
3536
let (rp, total_rps) = retry_user_errors!(channel.enumerate_rps_begin(TIMEOUT))?;
3637
let mut rps = vec![rp];
3738
for _ in 1..total_rps {
@@ -44,7 +45,7 @@ async fn enumerate_rps<T: CredentialManagement>(
4445
async fn enumerate_credentials_for_rp<T: CredentialManagement>(
4546
channel: &mut T,
4647
rp_id_hash: &[u8],
47-
) -> Result<Vec<Ctap2CredentialData>, WebAuthnError> {
48+
) -> Result<Vec<Ctap2CredentialData>, WebAuthnError<T::Error>> {
4849
let (credential, num_of_creds) =
4950
retry_user_errors!(channel.enumerate_credentials_begin(rp_id_hash, TIMEOUT))?;
5051
let mut credentials = vec![credential];
@@ -77,7 +78,7 @@ impl Display for Operation {
7778
}
7879

7980
#[tokio::main]
80-
pub async fn main() -> Result<(), WebAuthnError> {
81+
pub async fn main() -> Result<(), WebAuthnError<HidError>> {
8182
common::setup_logging();
8283

8384
let devices = list_devices().await.unwrap();
@@ -86,7 +87,10 @@ pub async fn main() -> Result<(), WebAuthnError> {
8687
for mut device in devices {
8788
println!("Selected HID authenticator: {}", &device);
8889
let mut channel = device.channel(ChannelSettings::default()).await?;
89-
channel.wink(TIMEOUT).await?;
90+
channel
91+
.wink(TIMEOUT)
92+
.await
93+
.map_err(WebAuthnError::Transport)?;
9094

9195
let state_recv = channel.get_ux_update_receiver();
9296
tokio::spawn(common::handle_uv_updates(state_recv));

libwebauthn/examples/management/persistent_cred_management_hid.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@ use libwebauthn::management::CredentialManagement;
1616
use libwebauthn::pin::persistent_token::{MemoryPersistentTokenStore, PersistentTokenStore};
1717
use libwebauthn::proto::ctap2::Ctap2;
1818
use libwebauthn::transport::hid::list_devices;
19+
use libwebauthn::transport::hid::HidError;
1920
use libwebauthn::transport::{Channel as _, ChannelSettings, Device};
20-
use libwebauthn::webauthn::Error as WebAuthnError;
21+
use libwebauthn::webauthn::WebAuthnError;
2122

2223
#[path = "../common/mod.rs"]
2324
mod common;
2425

2526
const TIMEOUT: Duration = Duration::from_secs(10);
2627

2728
#[tokio::main]
28-
pub async fn main() -> Result<(), WebAuthnError> {
29+
pub async fn main() -> Result<(), WebAuthnError<HidError>> {
2930
common::setup_logging();
3031

3132
// In production, use a securely stored implementation. See the module docs.
@@ -75,7 +76,9 @@ pub async fn main() -> Result<(), WebAuthnError> {
7576
Ok(())
7677
}
7778

78-
async fn print_metadata(channel: &mut impl CredentialManagement) -> Result<(), WebAuthnError> {
79+
async fn print_metadata<T: CredentialManagement>(
80+
channel: &mut T,
81+
) -> Result<(), WebAuthnError<T::Error>> {
7982
let metadata = channel.get_credential_metadata(TIMEOUT).await?;
8083
println!(
8184
"Discoverable credentials: {} (max remaining: {})",

0 commit comments

Comments
 (0)