Skip to content

Commit 2c0cf07

Browse files
committed
Don't assume AppId is hashed
This allows alternative token implementations to use CTAP2 based API, that expects relying party is hashed by the platform, not by the application.
1 parent 660a701 commit 2c0cf07

File tree

9 files changed

+56
-30
lines changed

9 files changed

+56
-30
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ log = "0.4"
4545
libc = "0.2"
4646
runloop = "0.1.0"
4747
bitflags = "1.0"
48+
sha2 = "^0.8.2"
4849
tokio = { version = "0.2", optional = true, features = ["macros"] }
4950
warp = { version = "0.2.4", optional = true }
5051
serde = { version = "1.0", optional = true, features = ["derive"] }
@@ -53,7 +54,6 @@ bytes = { version = "0.5", optional = true, features = ["serde"] }
5354
base64 = { version = "^0.10", optional = true }
5455

5556
[dev-dependencies]
56-
sha2 = "^0.8.2"
5757
base64 = "^0.10"
5858
env_logger = "^0.6"
5959
getopts = "^0.2"

examples/main.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
use authenticator::{
6-
authenticatorservice::AuthenticatorService, statecallback::StateCallback,
6+
authenticatorservice::AuthenticatorService, statecallback::StateCallback, AppId,
77
AuthenticatorTransports, KeyHandle, RegisterFlags, SignFlags, StatusUpdate,
88
};
99
use getopts::Options;
@@ -96,9 +96,8 @@ fn main() {
9696
challenge.input(challenge_str.as_bytes());
9797
let chall_bytes = challenge.result().to_vec();
9898

99-
let mut application = Sha256::default();
100-
application.input(b"http://demo.yubico.com");
101-
let app_bytes = application.result().to_vec();
99+
let app_bytes = b"http://demo.yubico.com".to_vec();
100+
let app_id: AppId = app_bytes.into();
102101

103102
let flags = RegisterFlags::empty();
104103

@@ -131,7 +130,7 @@ fn main() {
131130
flags,
132131
timeout_ms,
133132
chall_bytes.clone(),
134-
app_bytes.clone(),
133+
app_id.clone(),
135134
vec![],
136135
status_tx.clone(),
137136
callback,
@@ -163,7 +162,7 @@ fn main() {
163162
flags,
164163
timeout_ms,
165164
chall_bytes,
166-
vec![app_bytes],
165+
vec![app_id.clone()],
167166
vec![key_handle],
168167
status_tx,
169168
callback,

src/authenticatorservice.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl AuthenticatorService {
105105
status: Sender<crate::StatusUpdate>,
106106
callback: StateCallback<crate::Result<crate::RegisterResult>>,
107107
) -> crate::Result<()> {
108-
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
108+
if challenge.len() != PARAMETER_SIZE {
109109
return Err(AuthenticatorError::InvalidRelyingPartyInput);
110110
}
111111

@@ -167,12 +167,6 @@ impl AuthenticatorService {
167167
return Err(AuthenticatorError::InvalidRelyingPartyInput);
168168
}
169169

170-
for app_id in &app_ids {
171-
if app_id.len() != PARAMETER_SIZE {
172-
return Err(AuthenticatorError::InvalidRelyingPartyInput);
173-
}
174-
}
175-
176170
for key_handle in &key_handles {
177171
if key_handle.credential.len() > 256 {
178172
return Err(AuthenticatorError::InvalidRelyingPartyInput);

src/capi.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub unsafe extern "C" fn rust_u2f_app_ids_add(
8282
id_ptr: *const u8,
8383
id_len: usize,
8484
) {
85-
(*ids).push(from_raw(id_ptr, id_len));
85+
(*ids).push(from_raw(id_ptr, id_len).into());
8686
}
8787

8888
/// # Safety
@@ -232,7 +232,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_register(
232232

233233
let flags = crate::RegisterFlags::from_bits_truncate(flags);
234234
let challenge = from_raw(challenge_ptr, challenge_len);
235-
let application = from_raw(application_ptr, application_len);
235+
let application = from_raw(application_ptr, application_len).into();
236236
let key_handles = (*khs).clone();
237237

238238
let (status_tx, status_rx) = channel::<crate::StatusUpdate>();
@@ -333,7 +333,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
333333
let mut bufs = HashMap::new();
334334
bufs.insert(RESBUF_ID_KEYHANDLE, key_handle);
335335
bufs.insert(RESBUF_ID_SIGNATURE, signature);
336-
bufs.insert(RESBUF_ID_APPID, app_id);
336+
bufs.insert(RESBUF_ID_APPID, app_id.to_u2f());
337337
bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name);
338338
bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name);
339339
bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]);

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ pub struct KeyHandle {
104104
pub transports: AuthenticatorTransports,
105105
}
106106

107-
pub type AppId = Vec<u8>;
107+
#[derive(Clone, Debug)]
108+
pub struct AppId(Vec<u8>);
108109
pub type RegisterResult = (Vec<u8>, u2ftypes::U2FDeviceInfo);
109110
pub type SignResult = (AppId, Vec<u8>, Vec<u8>, u2ftypes::U2FDeviceInfo);
110111

src/manager.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ impl AuthenticatorTransport for U2FManager {
121121
status: Sender<crate::StatusUpdate>,
122122
callback: StateCallback<crate::Result<crate::RegisterResult>>,
123123
) -> crate::Result<()> {
124-
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
124+
if challenge.len() != PARAMETER_SIZE {
125125
return Err(AuthenticatorError::InvalidRelyingPartyInput);
126126
}
127127

@@ -161,12 +161,6 @@ impl AuthenticatorTransport for U2FManager {
161161
return Err(AuthenticatorError::InvalidRelyingPartyInput);
162162
}
163163

164-
for app_id in &app_ids {
165-
if app_id.len() != PARAMETER_SIZE {
166-
return Err(AuthenticatorError::InvalidRelyingPartyInput);
167-
}
168-
}
169-
170164
for key_handle in &key_handles {
171165
if key_handle.credential.len() > 256 {
172166
return Err(AuthenticatorError::InvalidRelyingPartyInput);

src/statemachine.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,14 @@ impl StateMachine {
112112
},
113113
);
114114

115+
let app_id_hash = application.to_u2f();
116+
115117
// Iterate the exclude list and see if there are any matches.
116118
// If so, we'll keep polling the device anyway to test for user
117119
// consent, to be consistent with CTAP2 device behavior.
118120
let excluded = key_handles.iter().any(|key_handle| {
119121
is_valid_transport(key_handle.transports)
120-
&& u2f_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
122+
&& u2f_is_keyhandle_valid(dev, &challenge, &app_id_hash, &key_handle.credential)
121123
.unwrap_or(false) /* no match on failure */
122124
});
123125

@@ -130,7 +132,7 @@ impl StateMachine {
130132
)));
131133
break;
132134
}
133-
} else if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
135+
} else if let Ok(bytes) = u2f_register(dev, &challenge, &app_id_hash) {
134136
let dev_info = dev.get_device_info();
135137
send_status(
136138
&status_mutex,
@@ -201,7 +203,9 @@ impl StateMachine {
201203
// valid key handle for an appId, we'll use that appId below.
202204
let (app_id, valid_handles) =
203205
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
204-
u2f_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
206+
let app_id: crate::AppId = app_id.clone().into();
207+
let app_id_hash = app_id.to_u2f();
208+
u2f_is_keyhandle_valid(dev, &challenge, &app_id_hash, &key_handle.credential)
205209
.unwrap_or(false) /* no match on failure */
206210
});
207211

@@ -225,6 +229,8 @@ impl StateMachine {
225229
},
226230
);
227231

232+
let app_id_hash = app_id.to_u2f();
233+
228234
'outer: while alive() {
229235
// If the device matches none of the given key handles
230236
// then just make it blink with bogus data.
@@ -239,7 +245,8 @@ impl StateMachine {
239245
} else {
240246
// Otherwise, try to sign.
241247
for key_handle in &valid_handles {
242-
if let Ok(bytes) = u2f_sign(dev, &challenge, app_id, &key_handle.credential)
248+
if let Ok(bytes) =
249+
u2f_sign(dev, &challenge, &app_id_hash, &key_handle.credential)
243250
{
244251
let dev_info = dev.get_device_info();
245252
send_status(

src/util.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
extern crate libc;
66

7+
use sha2::Digest;
78
use std::io;
9+
use std::ops::Deref;
10+
11+
use crate::AppId;
812

913
macro_rules! try_or {
1014
($val:expr, $or:expr) => {
@@ -65,3 +69,25 @@ pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
6569
pub fn io_err(msg: &str) -> io::Error {
6670
io::Error::new(io::ErrorKind::Other, msg)
6771
}
72+
73+
impl AppId {
74+
pub fn to_u2f(&self) -> Vec<u8> {
75+
let mut sha256 = sha2::Sha256::default();
76+
sha256.input(&self.0);
77+
sha256.result().to_vec()
78+
}
79+
}
80+
81+
impl Deref for AppId {
82+
type Target = Vec<u8>;
83+
84+
fn deref(&self) -> &Vec<u8> {
85+
&self.0
86+
}
87+
}
88+
89+
impl From<Vec<u8>> for AppId {
90+
fn from(v: Vec<u8>) -> Self {
91+
Self(v)
92+
}
93+
}

src/virtualdevices/software_u2f.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ impl SoftwareU2FToken {
3434
_app_ids: Vec<crate::AppId>,
3535
_key_handles: Vec<crate::KeyHandle>,
3636
) -> crate::Result<crate::SignResult> {
37-
Ok((vec![0u8; 0], vec![0u8; 0], vec![0u8; 0], self.dev_info()))
37+
Ok((
38+
vec![0u8; 0].into(),
39+
vec![0u8; 0],
40+
vec![0u8; 0],
41+
self.dev_info(),
42+
))
3843
}
3944

4045
pub fn dev_info(&self) -> crate::u2ftypes::U2FDeviceInfo {

0 commit comments

Comments
 (0)