Skip to content

Commit 24f909e

Browse files
committed
First attempt at decoupling Device/Transaction stuff from StateMachine
1 parent a250e33 commit 24f909e

File tree

2 files changed

+181
-143
lines changed

2 files changed

+181
-143
lines changed

src/manager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl U2FManager {
6060
callback,
6161
}) => {
6262
// This must not block, otherwise we can't cancel.
63-
sm.register(
63+
sm._register(
6464
flags,
6565
timeout,
6666
challenge,
@@ -80,7 +80,7 @@ impl U2FManager {
8080
callback,
8181
}) => {
8282
// This must not block, otherwise we can't cancel.
83-
sm.sign(
83+
sm._sign(
8484
flags,
8585
timeout,
8686
challenge,

src/statemachine.rs

Lines changed: 179 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,84 @@ impl StateMachine {
6666
Default::default()
6767
}
6868

69-
pub fn register(
69+
pub fn register<T>(
70+
dev: &mut T,
71+
flags: crate::RegisterFlags,
72+
challenge: &Vec<u8>,
73+
application: &crate::AppId,
74+
key_handles: &Vec<crate::KeyHandle>,
75+
status_mutex: &Mutex<Sender<crate::StatusUpdate>>,
76+
callback: &StateCallback<crate::Result<crate::RegisterResult>>,
77+
alive: &dyn Fn() -> bool,
78+
) where
79+
T: APDUDevice + U2FInfoQueryable,
80+
{
81+
// Try initializing it.
82+
if !dev.init_apdu().is_ok() {
83+
return;
84+
}
85+
86+
// We currently support none of the authenticator selection
87+
// criteria because we can't ask tokens whether they do support
88+
// those features. If flags are set, ignore all tokens for now.
89+
//
90+
// Technically, this is a ConstraintError because we shouldn't talk
91+
// to this authenticator in the first place. But the result is the
92+
// same anyway.
93+
if !flags.is_empty() {
94+
return;
95+
}
96+
97+
send_status(
98+
&status_mutex,
99+
crate::StatusUpdate::DeviceAvailable {
100+
dev_info: dev.get_device_info(),
101+
},
102+
);
103+
104+
// Iterate the exclude list and see if there are any matches.
105+
// If so, we'll keep polling the device anyway to test for user
106+
// consent, to be consistent with CTAP2 device behavior.
107+
let excluded = key_handles.iter().any(|key_handle| {
108+
is_valid_transport(key_handle.transports)
109+
&& apdu_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
110+
.unwrap_or(false) /* no match on failure */
111+
});
112+
113+
while alive() {
114+
if excluded {
115+
let blank = vec![0u8; PARAMETER_SIZE];
116+
if apdu_register(dev, &blank, &blank).is_ok() {
117+
callback.call(Err(errors::AuthenticatorError::U2FToken(
118+
errors::U2FTokenError::InvalidState,
119+
)));
120+
break;
121+
}
122+
} else if let Ok(bytes) = apdu_register(dev, &challenge, &application) {
123+
let dev_info = dev.get_device_info();
124+
send_status(
125+
&status_mutex,
126+
crate::StatusUpdate::Success {
127+
dev_info: dev.get_device_info(),
128+
},
129+
);
130+
callback.call(Ok((bytes, dev_info)));
131+
break;
132+
}
133+
134+
// Sleep a bit before trying again.
135+
thread::sleep(Duration::from_millis(100));
136+
}
137+
138+
send_status(
139+
&status_mutex,
140+
crate::StatusUpdate::DeviceUnavailable {
141+
dev_info: dev.get_device_info(),
142+
},
143+
);
144+
}
145+
146+
pub fn _register(
70147
&mut self,
71148
flags: crate::RegisterFlags,
72149
timeout: u64,
@@ -89,75 +166,119 @@ impl StateMachine {
89166
_ => return,
90167
};
91168

92-
// Try initializing it.
93-
if !dev.is_u2f() || !dev.init_apdu().is_ok() {
169+
if !dev.is_u2f() {
94170
return;
95171
}
96172

97-
// We currently support none of the authenticator selection
98-
// criteria because we can't ask tokens whether they do support
99-
// those features. If flags are set, ignore all tokens for now.
100-
//
101-
// Technically, this is a ConstraintError because we shouldn't talk
102-
// to this authenticator in the first place. But the result is the
103-
// same anyway.
104-
if !flags.is_empty() {
105-
return;
106-
}
173+
StateMachine::register(dev, flags, &challenge, &application, &key_handles, &status_mutex, &callback, alive);
174+
});
175+
176+
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));
177+
}
178+
179+
pub fn sign<T>(
180+
dev: &mut T,
181+
flags: crate::SignFlags,
182+
challenge: &Vec<u8>,
183+
app_ids: &Vec<crate::AppId>,
184+
key_handles: &Vec<crate::KeyHandle>,
185+
status_mutex: &Mutex<Sender<crate::StatusUpdate>>,
186+
callback: &StateCallback<crate::Result<crate::SignResult>>,
187+
alive: &dyn Fn() -> bool,
188+
) where
189+
T: APDUDevice + U2FInfoQueryable,
190+
{
191+
// Try initializing it.
192+
if !dev.init_apdu().is_ok() {
193+
return;
194+
}
195+
196+
// We currently don't support user verification because we can't
197+
// ask tokens whether they do support that. If the flag is set,
198+
// ignore all tokens for now.
199+
//
200+
// Technically, this is a ConstraintError because we shouldn't talk
201+
// to this authenticator in the first place. But the result is the
202+
// same anyway.
203+
if !flags.is_empty() {
204+
return;
205+
}
107206

108-
send_status(
109-
&status_mutex,
110-
crate::StatusUpdate::DeviceAvailable {
111-
dev_info: dev.get_device_info(),
112-
},
113-
);
114-
115-
// Iterate the exclude list and see if there are any matches.
116-
// If so, we'll keep polling the device anyway to test for user
117-
// consent, to be consistent with CTAP2 device behavior.
118-
let excluded = key_handles.iter().any(|key_handle| {
119-
is_valid_transport(key_handle.transports)
120-
&& apdu_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
121-
.unwrap_or(false) /* no match on failure */
207+
// For each appId, try all key handles. If there's at least one
208+
// valid key handle for an appId, we'll use that appId below.
209+
let (app_id, valid_handles) =
210+
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
211+
apdu_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
212+
.unwrap_or(false) /* no match on failure */
122213
});
123214

124-
while alive() {
125-
if excluded {
126-
let blank = vec![0u8; PARAMETER_SIZE];
127-
if apdu_register(dev, &blank, &blank).is_ok() {
128-
callback.call(Err(errors::AuthenticatorError::U2FToken(
129-
errors::U2FTokenError::InvalidState,
215+
// Aggregate distinct transports from all given credentials.
216+
let transports = key_handles
217+
.iter()
218+
.fold(crate::AuthenticatorTransports::empty(), |t, k| {
219+
t | k.transports
220+
});
221+
222+
// We currently only support USB. If the RP specifies transports
223+
// and doesn't include USB it's probably lying.
224+
if !is_valid_transport(transports) {
225+
return;
226+
}
227+
228+
send_status(
229+
&status_mutex,
230+
crate::StatusUpdate::DeviceAvailable {
231+
dev_info: dev.get_device_info(),
232+
},
233+
);
234+
235+
'outer: while alive() {
236+
// If the device matches none of the given key handles
237+
// then just make it blink with bogus data.
238+
if valid_handles.is_empty() {
239+
let blank = vec![0u8; PARAMETER_SIZE];
240+
if apdu_register(dev, &blank, &blank).is_ok() {
241+
callback.call(Err(errors::AuthenticatorError::U2FToken(
242+
errors::U2FTokenError::InvalidState,
243+
)));
244+
break;
245+
}
246+
} else {
247+
// Otherwise, try to sign.
248+
for key_handle in &valid_handles {
249+
if let Ok(bytes) = apdu_sign(dev, &challenge, app_id, &key_handle.credential)
250+
{
251+
let dev_info = dev.get_device_info();
252+
send_status(
253+
&status_mutex,
254+
crate::StatusUpdate::Success {
255+
dev_info: dev.get_device_info(),
256+
},
257+
);
258+
callback.call(Ok((
259+
app_id.clone(),
260+
key_handle.credential.clone(),
261+
bytes,
262+
dev_info,
130263
)));
131-
break;
264+
break 'outer;
132265
}
133-
} else if let Ok(bytes) = apdu_register(dev, &challenge, &application) {
134-
let dev_info = dev.get_device_info();
135-
send_status(
136-
&status_mutex,
137-
crate::StatusUpdate::Success {
138-
dev_info: dev.get_device_info(),
139-
},
140-
);
141-
callback.call(Ok((bytes, dev_info)));
142-
break;
143266
}
144-
145-
// Sleep a bit before trying again.
146-
thread::sleep(Duration::from_millis(100));
147267
}
148268

149-
send_status(
150-
&status_mutex,
151-
crate::StatusUpdate::DeviceUnavailable {
152-
dev_info: dev.get_device_info(),
153-
},
154-
);
155-
});
269+
// Sleep a bit before trying again.
270+
thread::sleep(Duration::from_millis(100));
271+
}
156272

157-
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));
273+
send_status(
274+
&status_mutex,
275+
crate::StatusUpdate::DeviceUnavailable {
276+
dev_info: dev.get_device_info(),
277+
},
278+
);
158279
}
159280

160-
pub fn sign(
281+
pub fn _sign(
161282
&mut self,
162283
flags: crate::SignFlags,
163284
timeout: u64,
@@ -181,94 +302,11 @@ impl StateMachine {
181302
_ => return,
182303
};
183304

184-
// Try initializing it.
185-
if !dev.is_u2f() || !dev.init_apdu().is_ok() {
305+
if !dev.is_u2f() {
186306
return;
187307
}
188308

189-
// We currently don't support user verification because we can't
190-
// ask tokens whether they do support that. If the flag is set,
191-
// ignore all tokens for now.
192-
//
193-
// Technically, this is a ConstraintError because we shouldn't talk
194-
// to this authenticator in the first place. But the result is the
195-
// same anyway.
196-
if !flags.is_empty() {
197-
return;
198-
}
199-
200-
// For each appId, try all key handles. If there's at least one
201-
// valid key handle for an appId, we'll use that appId below.
202-
let (app_id, valid_handles) =
203-
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
204-
apdu_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
205-
.unwrap_or(false) /* no match on failure */
206-
});
207-
208-
// Aggregate distinct transports from all given credentials.
209-
let transports = key_handles
210-
.iter()
211-
.fold(crate::AuthenticatorTransports::empty(), |t, k| {
212-
t | k.transports
213-
});
214-
215-
// We currently only support USB. If the RP specifies transports
216-
// and doesn't include USB it's probably lying.
217-
if !is_valid_transport(transports) {
218-
return;
219-
}
220-
221-
send_status(
222-
&status_mutex,
223-
crate::StatusUpdate::DeviceAvailable {
224-
dev_info: dev.get_device_info(),
225-
},
226-
);
227-
228-
'outer: while alive() {
229-
// If the device matches none of the given key handles
230-
// then just make it blink with bogus data.
231-
if valid_handles.is_empty() {
232-
let blank = vec![0u8; PARAMETER_SIZE];
233-
if apdu_register(dev, &blank, &blank).is_ok() {
234-
callback.call(Err(errors::AuthenticatorError::U2FToken(
235-
errors::U2FTokenError::InvalidState,
236-
)));
237-
break;
238-
}
239-
} else {
240-
// Otherwise, try to sign.
241-
for key_handle in &valid_handles {
242-
if let Ok(bytes) = apdu_sign(dev, &challenge, app_id, &key_handle.credential)
243-
{
244-
let dev_info = dev.get_device_info();
245-
send_status(
246-
&status_mutex,
247-
crate::StatusUpdate::Success {
248-
dev_info: dev.get_device_info(),
249-
},
250-
);
251-
callback.call(Ok((
252-
app_id.clone(),
253-
key_handle.credential.clone(),
254-
bytes,
255-
dev_info,
256-
)));
257-
break 'outer;
258-
}
259-
}
260-
}
261-
262-
// Sleep a bit before trying again.
263-
thread::sleep(Duration::from_millis(100));
264-
}
265-
266-
send_status(
267-
&status_mutex,
268-
crate::StatusUpdate::DeviceUnavailable {
269-
dev_info: dev.get_device_info(),
270-
},
271-
);
309+
StateMachine::sign(dev, flags, &challenge, &app_ids, &key_handles, &status_mutex, &callback, alive);
272310
});
273311

274312
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));

0 commit comments

Comments
 (0)