@@ -5,6 +5,7 @@ use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine};
55use futures_lite:: Stream ;
66use libwebauthn:: {
77 ops:: webauthn:: GetAssertionResponse ,
8+ proto:: CtapError ,
89 transport:: { hid:: HidDevice , Device } ,
910 webauthn:: { Error as WebAuthnError , WebAuthn } ,
1011 UxUpdate ,
@@ -17,7 +18,7 @@ use crate::{
1718 view_model:: Credential ,
1819} ;
1920
20- use super :: { AuthenticatorResponse , CredentialResponse } ;
21+ use super :: { AuthenticatorResponse , CredentialResponse , Error } ;
2122
2223pub ( crate ) trait UsbHandler {
2324 fn start (
@@ -33,7 +34,7 @@ impl InProcessUsbHandler {
3334 async fn process (
3435 tx : Sender < UsbStateInternal > ,
3536 cred_request : CredentialRequest ,
36- ) -> Result < ( ) , String > {
37+ ) -> Result < ( ) , Error > {
3738 let mut state = UsbStateInternal :: Idle ;
3839 let ( signal_tx, mut signal_rx) = mpsc:: channel ( 256 ) ;
3940 let ( cred_tx, mut cred_rx) = mpsc:: channel ( 1 ) ;
@@ -58,7 +59,7 @@ impl InProcessUsbHandler {
5859 Err ( err) => {
5960 failures += 1 ;
6061 if failures == 5 {
61- Err ( format ! ( "Failed to list USB authenticators: {:?}. Cancelling USB state updates." , err) )
62+ Err ( Error :: Internal ( format ! ( "Failed to list USB authenticators: {:?}. Cancelling USB state updates." , err) ) )
6263 } else {
6364 tracing:: warn!( "Failed to list USB authenticators: {:?}. Throttling USB state updates" , err) ;
6465 tokio:: time:: sleep ( Duration :: from_secs ( 1 ) ) . await ;
@@ -173,18 +174,16 @@ impl InProcessUsbHandler {
173174 }
174175 } ,
175176 Some ( Err ( err) ) => Err ( err. clone ( ) ) ,
176- None => Err ( "Channel disconnected" . to_string ( ) ) ,
177+ None => Err ( Error :: Internal ( "Channel disconnected" . to_string ( ) ) ) ,
177178 }
178179 }
179180 UsbStateInternal :: NeedsPin {
180181 attempts_left : Some ( attempts_left) ,
181182 ..
182- } if attempts_left <= 1 => Err ( "No more USB attempts left" . to_string ( ) ) ,
183+ } if attempts_left <= 1 => Err ( Error :: PinAttemptsExhausted ) ,
183184 UsbStateInternal :: NeedsUserVerification {
184185 attempts_left : Some ( attempts_left) ,
185- } if attempts_left <= 1 => {
186- Err ( "No more on-device user device attempts left" . to_string ( ) )
187- }
186+ } if attempts_left <= 1 => Err ( Error :: UserVerficiationAttemptsExhausted ) ,
188187 UsbStateInternal :: NeedsPin { .. }
189188 | UsbStateInternal :: NeedsUserVerification { .. }
190189 | UsbStateInternal :: NeedsUserPresence => match signal_rx. recv ( ) . await {
@@ -227,7 +226,7 @@ impl InProcessUsbHandler {
227226 }
228227 } ,
229228 } ,
230- None => Err ( "USB UV handler channel closed" . to_string ( ) ) ,
229+ None => Err ( Error :: Internal ( "USB UV handler channel closed" . to_string ( ) ) ) ,
231230 } ,
232231 UsbStateInternal :: Completed ( _) => Ok ( prev_usb_state) ,
233232 UsbStateInternal :: SelectCredential {
@@ -262,19 +261,21 @@ impl InProcessUsbHandler {
262261 ) ,
263262 ) ,
264263 ) ) ,
265- None => Err ( "Selected credential not found." . to_string ( ) ) ,
264+ None => Err ( Error :: NoCredentials ) ,
266265 }
267266 }
268267 None => {
269268 tracing:: debug!( "cred channel closed before receiving cred from client." ) ;
270- Err ( "Cred channel disconnected" . to_string ( ) )
269+ Err ( Error :: Internal (
270+ "Cred channel disconnected prematurely" . to_string ( ) ,
271+ ) )
271272 }
272273 } ,
273274 } ;
274275 state = next_usb_state?;
275- tx. send ( state. clone ( ) )
276- . await
277- . map_err ( |_| "Receiver channel closed" . to_string ( ) ) ?;
276+ tx. send ( state. clone ( ) ) . await . map_err ( |_| {
277+ Error :: Internal ( "USB state channel receiver closed prematurely" . to_string ( ) )
278+ } ) ?;
278279 if let UsbStateInternal :: Completed ( _) = state {
279280 break Ok ( ( ) ) ;
280281 }
@@ -285,7 +286,7 @@ impl InProcessUsbHandler {
285286async fn handle_events (
286287 cred_request : & CredentialRequest ,
287288 mut device : HidDevice ,
288- signal_tx : & Sender < Result < UsbUvMessage , String > > ,
289+ signal_tx : & Sender < Result < UsbUvMessage , Error > > ,
289290) {
290291 let device_debug = device. to_string ( ) ;
291292 match device. channel ( ) . await {
@@ -298,86 +299,54 @@ async fn handle_events(
298299 handle_usb_updates ( & signal_tx2, state_rx) . await ;
299300 debug ! ( "Reached end of USB update task" ) ;
300301 } ) ;
301- match cred_request {
302- CredentialRequest :: CreatePublicKeyCredentialRequest ( make_cred_request) => loop {
303- tracing:: debug!(
304- "Polling for credential from USB authenticator {}" ,
305- & device_debug
306- ) ;
307- match channel. webauthn_make_credential ( make_cred_request) . await {
308- Ok ( response) => {
309- tracing:: debug!( "Received attestation from USB authenticator" ) ;
310- notify_ceremony_completed (
311- signal_tx,
312- AuthenticatorResponse :: CredentialCreated ( response) ,
313- )
314- . await ;
315- break ;
316- }
317- Err ( WebAuthnError :: Ctap ( ctap_error) )
318- if ctap_error. is_retryable_user_error ( ) =>
319- {
320- warn ! ( "Retrying WebAuthn make credential operation" ) ;
321- continue ;
322- }
323- Err ( err) => {
324- tracing:: warn!(
325- "Failed to create credential with USB authenticator: {:?}" ,
326- err
327- ) ;
328- notify_ceremony_failed ( signal_tx, err. to_string ( ) ) . await ;
329- break ;
330- }
331- } ;
332- } ,
333- CredentialRequest :: GetPublicKeyCredentialRequest ( get_cred_request) => loop {
334- match channel. webauthn_get_assertion ( get_cred_request) . await {
335- Ok ( response) => {
336- tracing:: debug!( "Received assertion from USB authenticator" ) ;
337- notify_ceremony_completed (
338- signal_tx,
339- AuthenticatorResponse :: CredentialsAsserted ( response) ,
340- )
341- . await ;
342- break ;
343- }
344- Err ( WebAuthnError :: Ctap ( ctap_error) )
345- if ctap_error. is_retryable_user_error ( ) =>
346- {
347- tracing:: warn!( "Retrying WebAuthn get credential operation" ) ;
348- continue ;
349- }
350- Err ( err) => {
351- tracing:: warn!(
352- "Failed to get credential from USB authenticator: {:?}" ,
353- err
354- ) ;
355- notify_ceremony_failed ( signal_tx, err. to_string ( ) ) . await ;
356- break ;
357- }
358- } ;
359- } ,
360- } ;
302+ tracing:: debug!(
303+ "Polling for credential from USB authenticator {}" ,
304+ & device_debug
305+ ) ;
306+ let response: Result < UsbUvMessage , Error > = loop {
307+ let response = match cred_request {
308+ CredentialRequest :: CreatePublicKeyCredentialRequest ( make_cred_request) => {
309+ channel
310+ . webauthn_make_credential ( make_cred_request)
311+ . await
312+ . map ( |response| UsbUvMessage :: ReceivedCredentials ( response. into ( ) ) )
313+ }
314+ CredentialRequest :: GetPublicKeyCredentialRequest ( get_cred_request) => channel
315+ . webauthn_get_assertion ( get_cred_request)
316+ . await
317+ . map ( |response| UsbUvMessage :: ReceivedCredentials ( response. into ( ) ) ) ,
318+ } ;
319+ match response {
320+ Ok ( response) => {
321+ tracing:: debug!( "Received credential from USB authenticator" ) ;
322+ break Ok ( response) ;
323+ }
324+ Err ( WebAuthnError :: Ctap ( ctap_error) )
325+ if ctap_error. is_retryable_user_error ( ) =>
326+ {
327+ warn ! ( "Retrying WebAuthn credential operation" ) ;
328+ continue ;
329+ }
330+ Err ( err) => {
331+ tracing:: warn!(
332+ "Failed to make/get credential with USB authenticator: {:?}" ,
333+ err
334+ ) ;
335+ break Err ( err) ;
336+ }
337+ }
338+ }
339+ . map_err ( |err| match err {
340+ WebAuthnError :: Ctap ( CtapError :: NoCredentials ) => Error :: NoCredentials ,
341+ _ => Error :: AuthenticatorError ,
342+ } ) ;
343+ if let Err ( err) = signal_tx. send ( response) . await {
344+ tracing:: error!( "Failed to notify that ceremony completed: {:?}" , err) ;
345+ }
361346 }
362347 }
363348}
364349
365- async fn notify_ceremony_completed (
366- signal_tx : & Sender < Result < UsbUvMessage , String > > ,
367- response : AuthenticatorResponse ,
368- ) {
369- signal_tx
370- . send ( Ok ( UsbUvMessage :: ReceivedCredentials ( response) ) )
371- . await
372- . unwrap ( ) ;
373- }
374-
375- async fn notify_ceremony_failed ( signal_tx : & Sender < Result < UsbUvMessage , String > > , err : String ) {
376- if let Err ( tx_err) = signal_tx. send ( Err ( err) ) . await {
377- tracing:: error!( "Failed to notify that ceremony failed: {:?}" , tx_err) ;
378- }
379- }
380-
381350impl UsbHandler for InProcessUsbHandler {
382351 fn start (
383352 & self ,
@@ -386,6 +355,8 @@ impl UsbHandler for InProcessUsbHandler {
386355 let request = request. clone ( ) ;
387356 let ( tx, mut rx) = mpsc:: channel ( 32 ) ;
388357 tokio:: spawn ( async move {
358+ // TODO: instead of logging error here, push the errors into the
359+ // stream so credential service can handle/forward them to the UI
389360 if let Err ( err) = InProcessUsbHandler :: process ( tx, request) . await {
390361 tracing:: error!( "Error getting credential from USB: {:?}" , err) ;
391362 }
@@ -550,7 +521,7 @@ impl From<UsbStateInternal> for UsbState {
550521}
551522
552523async fn handle_usb_updates (
553- signal_tx : & WeakSender < Result < UsbUvMessage , String > > ,
524+ signal_tx : & WeakSender < Result < UsbUvMessage , Error > > ,
554525 mut state_rx : Receiver < UxUpdate > ,
555526) {
556527 while let Some ( msg) = state_rx. recv ( ) . await {
@@ -570,7 +541,8 @@ async fn handle_usb_updates(
570541 UxUpdate :: PinRequired ( pin_update) => {
571542 if pin_update. attempts_left . is_some_and ( |num| num <= 1 ) {
572543 // TODO: cancel authenticator operation
573- if let Err ( err) = signal_tx. send ( Err ( "No more PIN attempts allowed. Select a different authenticator or try again later." . to_string ( ) ) ) . await {
544+ // Err("No more PIN attempts allowed. Select a different authenticator or try again later.".to_string())
545+ if let Err ( err) = signal_tx. send ( Err ( Error :: PinAttemptsExhausted ) ) . await {
574546 tracing:: error!( "Authenticator cannot process anymore PIN requests, but we cannot relay the message to credential service: {:?}" , err) ;
575547 }
576548 continue ;
0 commit comments