Skip to content

Commit a0025ce

Browse files
committed
First attempt at decoupling Device/Transaction stuff from StateMachine
1 parent c48efdd commit a0025ce

File tree

2 files changed

+178
-140
lines changed

2 files changed

+178
-140
lines changed

src/manager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl U2FManager {
5959
callback,
6060
}) => {
6161
// This must not block, otherwise we can't cancel.
62-
sm.register(
62+
sm._register(
6363
flags,
6464
timeout,
6565
challenge,
@@ -79,7 +79,7 @@ impl U2FManager {
7979
callback,
8080
}) => {
8181
// This must not block, otherwise we can't cancel.
82-
sm.sign(
82+
sm._sign(
8383
flags,
8484
timeout,
8585
challenge,

src/statemachine.rs

Lines changed: 176 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,82 @@ 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<Result<crate::RegisterResult, crate::Error>>,
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(crate::Error::InvalidState));
118+
break;
119+
}
120+
} else if let Ok(bytes) = apdu_register(dev, &challenge, &application) {
121+
let dev_info = dev.get_device_info();
122+
send_status(
123+
&status_mutex,
124+
crate::StatusUpdate::Success {
125+
dev_info: dev.get_device_info(),
126+
},
127+
);
128+
callback.call(Ok((bytes, dev_info)));
129+
break;
130+
}
131+
132+
// Sleep a bit before trying again.
133+
thread::sleep(Duration::from_millis(100));
134+
}
135+
136+
send_status(
137+
&status_mutex,
138+
crate::StatusUpdate::DeviceUnavailable {
139+
dev_info: dev.get_device_info(),
140+
},
141+
);
142+
}
143+
144+
pub fn _register(
70145
&mut self,
71146
flags: crate::RegisterFlags,
72147
timeout: u64,
@@ -89,73 +164,117 @@ impl StateMachine {
89164
_ => return,
90165
};
91166

92-
// Try initializing it.
93-
if !dev.is_u2f() || !dev.init_apdu().is_ok() {
167+
if !dev.is_u2f() {
94168
return;
95169
}
96170

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-
}
171+
StateMachine::register(dev, flags, &challenge, &application, &key_handles, &status_mutex, &callback, alive);
172+
});
173+
174+
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));
175+
}
107176

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 */
177+
pub fn sign<T>(
178+
dev: &mut T,
179+
flags: crate::SignFlags,
180+
challenge: &Vec<u8>,
181+
app_ids: &Vec<crate::AppId>,
182+
key_handles: &Vec<crate::KeyHandle>,
183+
status_mutex: &Mutex<Sender<crate::StatusUpdate>>,
184+
callback: &StateCallback<Result<crate::SignResult, crate::Error>>,
185+
alive: &dyn Fn() -> bool,
186+
) where
187+
T: APDUDevice + U2FInfoQueryable,
188+
{
189+
// Try initializing it.
190+
if !dev.init_apdu().is_ok() {
191+
return;
192+
}
193+
194+
// We currently don't support user verification because we can't
195+
// ask tokens whether they do support that. If the flag is set,
196+
// ignore all tokens for now.
197+
//
198+
// Technically, this is a ConstraintError because we shouldn't talk
199+
// to this authenticator in the first place. But the result is the
200+
// same anyway.
201+
if !flags.is_empty() {
202+
return;
203+
}
204+
205+
// For each appId, try all key handles. If there's at least one
206+
// valid key handle for an appId, we'll use that appId below.
207+
let (app_id, valid_handles) =
208+
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
209+
apdu_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
210+
.unwrap_or(false) /* no match on failure */
122211
});
123212

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

147-
send_status(
148-
&status_mutex,
149-
crate::StatusUpdate::DeviceUnavailable {
150-
dev_info: dev.get_device_info(),
151-
},
152-
);
153-
});
265+
// Sleep a bit before trying again.
266+
thread::sleep(Duration::from_millis(100));
267+
}
154268

155-
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));
269+
send_status(
270+
&status_mutex,
271+
crate::StatusUpdate::DeviceUnavailable {
272+
dev_info: dev.get_device_info(),
273+
},
274+
);
156275
}
157276

158-
pub fn sign(
277+
pub fn _sign(
159278
&mut self,
160279
flags: crate::SignFlags,
161280
timeout: u64,
@@ -179,92 +298,11 @@ impl StateMachine {
179298
_ => return,
180299
};
181300

182-
// Try initializing it.
183-
if !dev.is_u2f() || !dev.init_apdu().is_ok() {
184-
return;
185-
}
186-
187-
// We currently don't support user verification because we can't
188-
// ask tokens whether they do support that. If the flag is set,
189-
// ignore all tokens for now.
190-
//
191-
// Technically, this is a ConstraintError because we shouldn't talk
192-
// to this authenticator in the first place. But the result is the
193-
// same anyway.
194-
if !flags.is_empty() {
195-
return;
196-
}
197-
198-
// For each appId, try all key handles. If there's at least one
199-
// valid key handle for an appId, we'll use that appId below.
200-
let (app_id, valid_handles) =
201-
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
202-
apdu_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
203-
.unwrap_or(false) /* no match on failure */
204-
});
205-
206-
// Aggregate distinct transports from all given credentials.
207-
let transports = key_handles
208-
.iter()
209-
.fold(crate::AuthenticatorTransports::empty(), |t, k| {
210-
t | k.transports
211-
});
212-
213-
// We currently only support USB. If the RP specifies transports
214-
// and doesn't include USB it's probably lying.
215-
if !is_valid_transport(transports) {
301+
if !dev.is_u2f() {
216302
return;
217303
}
218304

219-
send_status(
220-
&status_mutex,
221-
crate::StatusUpdate::DeviceAvailable {
222-
dev_info: dev.get_device_info(),
223-
},
224-
);
225-
226-
'outer: while alive() {
227-
// If the device matches none of the given key handles
228-
// then just make it blink with bogus data.
229-
if valid_handles.is_empty() {
230-
let blank = vec![0u8; PARAMETER_SIZE];
231-
if apdu_register(dev, &blank, &blank).is_ok() {
232-
callback.call(Err(crate::Error::InvalidState));
233-
break;
234-
}
235-
} else {
236-
// Otherwise, try to sign.
237-
for key_handle in &valid_handles {
238-
if let Ok(bytes) = apdu_sign(dev, &challenge, app_id, &key_handle.credential)
239-
{
240-
let dev_info = dev.get_device_info();
241-
send_status(
242-
&status_mutex,
243-
crate::StatusUpdate::Success {
244-
dev_info: dev.get_device_info(),
245-
},
246-
);
247-
callback.call(Ok((
248-
app_id.clone(),
249-
key_handle.credential.clone(),
250-
bytes,
251-
dev_info,
252-
)));
253-
break 'outer;
254-
}
255-
}
256-
}
257-
258-
// Sleep a bit before trying again.
259-
thread::sleep(Duration::from_millis(100));
260-
}
261-
262-
send_status(
263-
&status_mutex,
264-
crate::StatusUpdate::DeviceUnavailable {
265-
dev_info: dev.get_device_info(),
266-
},
267-
);
305+
StateMachine::sign(dev, flags, &challenge, &app_ids, &key_handles, &status_mutex, &callback, alive);
268306
});
269307

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

0 commit comments

Comments
 (0)