зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1804972 - upgrade to authenticator 0.4.0-alpha.7. r=supply-chain-reviewers,dveditz
Differential Revision: https://phabricator.services.mozilla.com/D168157
This commit is contained in:
Родитель
9f7926a306
Коммит
1c2bfe5620
|
@ -378,9 +378,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "authenticator"
|
||||
version = "0.4.0-alpha.6"
|
||||
version = "0.4.0-alpha.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0bad0926d74984fd6932d928c65efbc0f14d37873d0316a664621898bfcd80c"
|
||||
checksum = "178ca28a8c8b156a4f7aa70e789d87a598fa71a2a66094ed92dee513b3cdf868"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
|
|
|
@ -149,7 +149,7 @@ notes = "I maintain this crate and have reviewed every line."
|
|||
[[audits.authenticator]]
|
||||
who = "John M. Schanck <jschanck@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.4.0-alpha.6"
|
||||
version = "0.4.0-alpha.7"
|
||||
notes = "Maintained by the CryptoEng team at Mozilla."
|
||||
|
||||
[[audits.autocfg]]
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -39,7 +39,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "authenticator"
|
||||
version = "0.4.0-alpha.6"
|
||||
version = "0.4.0-alpha.7"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"base64",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "authenticator"
|
||||
version = "0.4.0-alpha.6"
|
||||
version = "0.4.0-alpha.7"
|
||||
authors = [
|
||||
"J.C. Jones <jc@mozilla.com>",
|
||||
"Tim Taubert <ttaubert@mozilla.com>",
|
||||
|
|
|
@ -10,9 +10,9 @@ fn main() {}
|
|||
#[cfg(all(target_os = "linux", feature = "binding-recompile"))]
|
||||
fn main() {
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("src/linux/hidwrapper.h")
|
||||
.whitelist_var("_HIDIOCGRDESCSIZE")
|
||||
.whitelist_var("_HIDIOCGRDESC")
|
||||
.header("src/transport/linux/hidwrapper.h")
|
||||
.allowlist_var("_HIDIOCGRDESCSIZE")
|
||||
.allowlist_var("_HIDIOCGRDESC")
|
||||
.generate()
|
||||
.expect("Unable to get hidraw bindings");
|
||||
|
||||
|
@ -51,6 +51,12 @@ fn main() {
|
|||
panic!("architecture not supported");
|
||||
};
|
||||
bindings
|
||||
.write_to_file(out_path.join("src").join("linux").join(name))
|
||||
.write_to_file(
|
||||
out_path
|
||||
.join("src")
|
||||
.join("transport")
|
||||
.join("linux")
|
||||
.join(name),
|
||||
)
|
||||
.expect("Couldn't write hidraw bindings");
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ fn main() {
|
|||
|
||||
println!("Register result: {}", base64::encode(®ister_data));
|
||||
println!("Device info: {}", &device_info);
|
||||
println!("");
|
||||
println!();
|
||||
println!("*********************************************************************");
|
||||
println!("Asking a security key to sign now, with the data from the register...");
|
||||
println!("*********************************************************************");
|
||||
|
@ -193,8 +193,8 @@ fn main() {
|
|||
if let SignResult::CTAP1(_, handle_used, sign_data, device_info) =
|
||||
sign_result.expect("Sign failed")
|
||||
{
|
||||
println!("Sign result: {}", base64::encode(&sign_data));
|
||||
println!("Key handle used: {}", base64::encode(&handle_used));
|
||||
println!("Sign result: {}", base64::encode(sign_data));
|
||||
println!("Key handle used: {}", base64::encode(handle_used));
|
||||
println!("Device info: {}", &device_info);
|
||||
println!("Done.");
|
||||
} else {
|
||||
|
|
|
@ -110,7 +110,7 @@ fn main() {
|
|||
PinError::InvalidPin(attempts) => {
|
||||
println!(
|
||||
"Wrong PIN! {}",
|
||||
attempts.map_or(format!("Try again."), |a| format!(
|
||||
attempts.map_or("Try again.".to_string(), |a| format!(
|
||||
"You have {} attempts left.",
|
||||
a
|
||||
))
|
||||
|
@ -152,7 +152,7 @@ fn main() {
|
|||
icon: None,
|
||||
},
|
||||
origin: origin.clone(),
|
||||
user: user.clone(),
|
||||
user,
|
||||
pub_cred_params: vec![
|
||||
PublicKeyCredentialParameters {
|
||||
alg: COSEAlgorithm::ES256,
|
||||
|
@ -194,7 +194,7 @@ fn main() {
|
|||
|
||||
if let Err(e) = manager.register(
|
||||
timeout_ms,
|
||||
ctap_args.clone().into(),
|
||||
ctap_args.into(),
|
||||
status_tx.clone(),
|
||||
callback,
|
||||
) {
|
||||
|
@ -219,7 +219,7 @@ fn main() {
|
|||
println!("Register result: {:?}", &attestation_object);
|
||||
println!("Collected client data: {:?}", &client_data);
|
||||
|
||||
println!("");
|
||||
println!();
|
||||
println!("*********************************************************************");
|
||||
println!("Asking a security key to sign now, with the data from the register...");
|
||||
println!("*********************************************************************");
|
||||
|
@ -227,7 +227,7 @@ fn main() {
|
|||
let allow_list;
|
||||
if let Some(cred_data) = attestation_object.auth_data.credential_data {
|
||||
allow_list = vec![PublicKeyCredentialDescriptor {
|
||||
id: cred_data.credential_id.clone(),
|
||||
id: cred_data.credential_id,
|
||||
transports: vec![Transport::USB],
|
||||
}];
|
||||
} else {
|
||||
|
@ -266,8 +266,8 @@ fn main() {
|
|||
|
||||
if let Err(e) = manager.sign(
|
||||
timeout_ms,
|
||||
ctap_args.clone().into(),
|
||||
status_tx.clone(),
|
||||
ctap_args.into(),
|
||||
status_tx,
|
||||
callback,
|
||||
) {
|
||||
panic!("Couldn't sign: {:?}", e);
|
||||
|
|
|
@ -30,7 +30,7 @@ fn print_usage(program: &str, opts: Options) {
|
|||
}
|
||||
|
||||
fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms: u64) {
|
||||
println!("");
|
||||
println!();
|
||||
println!("*********************************************************************");
|
||||
println!(
|
||||
"Asking a security key to register now with user: {}",
|
||||
|
@ -81,7 +81,7 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms:
|
|||
PinError::InvalidPin(attempts) => {
|
||||
println!(
|
||||
"Wrong PIN! {}",
|
||||
attempts.map_or(format!("Try again."), |a| format!(
|
||||
attempts.map_or("Try again.".to_string(), |a| format!(
|
||||
"You have {} attempts left.",
|
||||
a
|
||||
))
|
||||
|
@ -116,14 +116,14 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms:
|
|||
};
|
||||
let origin = "https://example.com".to_string();
|
||||
let ctap_args = RegisterArgsCtap2 {
|
||||
challenge: chall_bytes.clone(),
|
||||
challenge: chall_bytes,
|
||||
relying_party: RelyingParty {
|
||||
id: "example.com".to_string(),
|
||||
name: None,
|
||||
icon: None,
|
||||
},
|
||||
origin: origin.clone(),
|
||||
user: user.clone(),
|
||||
origin,
|
||||
user,
|
||||
pub_cred_params: vec![
|
||||
PublicKeyCredentialParameters {
|
||||
alg: COSEAlgorithm::ES256,
|
||||
|
@ -154,8 +154,8 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms:
|
|||
|
||||
if let Err(e) = manager.register(
|
||||
timeout_ms,
|
||||
ctap_args.clone().into(),
|
||||
status_tx.clone(),
|
||||
ctap_args.into(),
|
||||
status_tx,
|
||||
callback,
|
||||
) {
|
||||
panic!("Couldn't register: {:?}", e);
|
||||
|
@ -224,11 +224,11 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
for username in vec!["A. User", "A. Nother", "Dr. Who"] {
|
||||
for username in &["A. User", "A. Nother", "Dr. Who"] {
|
||||
register_user(&mut manager, username, timeout_ms)
|
||||
}
|
||||
|
||||
println!("");
|
||||
println!();
|
||||
println!("*********************************************************************");
|
||||
println!("Asking a security key to sign now, with the data from the register...");
|
||||
println!("*********************************************************************");
|
||||
|
@ -270,7 +270,7 @@ fn main() {
|
|||
PinError::InvalidPin(attempts) => {
|
||||
println!(
|
||||
"Wrong PIN! {}",
|
||||
attempts.map_or(format!("Try again."), |a| format!(
|
||||
attempts.map_or("Try again.".to_string(), |a| format!(
|
||||
"You have {} attempts left.",
|
||||
a
|
||||
))
|
||||
|
@ -319,8 +319,8 @@ fn main() {
|
|||
|
||||
if let Err(e) = manager.sign(
|
||||
timeout_ms,
|
||||
ctap_args.clone().into(),
|
||||
status_tx.clone(),
|
||||
ctap_args.into(),
|
||||
status_tx,
|
||||
callback,
|
||||
) {
|
||||
panic!("Couldn't sign: {:?}", e);
|
||||
|
|
|
@ -189,8 +189,8 @@ fn main() {
|
|||
if let SignResult::CTAP1(_, handle_used, sign_data, device_info) =
|
||||
sign_result.expect("Sign failed")
|
||||
{
|
||||
println!("Sign result: {}", base64::encode(&sign_data));
|
||||
println!("Key handle used: {}", base64::encode(&handle_used));
|
||||
println!("Sign result: {}", base64::encode(sign_data));
|
||||
println!("Key handle used: {}", base64::encode(handle_used));
|
||||
println!("Device info: {}", &device_info);
|
||||
println!("Done.");
|
||||
} else {
|
||||
|
|
|
@ -86,12 +86,12 @@ fn main() {
|
|||
|
||||
let (status_tx, status_rx) = channel::<StatusUpdate>();
|
||||
let (reset_tx, reset_rx) = channel();
|
||||
let rs_tx = reset_tx.clone();
|
||||
let rs_tx = reset_tx;
|
||||
let callback = StateCallback::new(Box::new(move |rv| {
|
||||
let _ = rs_tx.send(rv);
|
||||
}));
|
||||
|
||||
if let Err(e) = manager.reset(timeout_ms, status_tx.clone(), callback) {
|
||||
if let Err(e) = manager.reset(timeout_ms, status_tx, callback) {
|
||||
panic!("Couldn't register: {:?}", e);
|
||||
};
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ fn main() {
|
|||
PinError::InvalidPin(attempts) => {
|
||||
println!(
|
||||
"Wrong PIN! {}",
|
||||
attempts.map_or(format!("Try again."), |a| format!(
|
||||
attempts.map_or("Try again.".to_string(), |a| format!(
|
||||
"You have {} attempts left.",
|
||||
a
|
||||
))
|
||||
|
@ -123,12 +123,12 @@ fn main() {
|
|||
});
|
||||
|
||||
let (reset_tx, reset_rx) = channel();
|
||||
let rs_tx = reset_tx.clone();
|
||||
let rs_tx = reset_tx;
|
||||
let callback = StateCallback::new(Box::new(move |rv| {
|
||||
let _ = rs_tx.send(rv);
|
||||
}));
|
||||
|
||||
if let Err(e) = manager.set_pin(timeout_ms, Pin::new(&new_pin), status_tx.clone(), callback) {
|
||||
if let Err(e) = manager.set_pin(timeout_ms, Pin::new(&new_pin), status_tx, callback) {
|
||||
panic!("Couldn't call set_pin: {:?}", e);
|
||||
};
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ fn main() {
|
|||
PinError::InvalidPin(attempts) => {
|
||||
println!(
|
||||
"Wrong PIN! {}",
|
||||
attempts.map_or(format!("Try again."), |a| format!(
|
||||
attempts.map_or("Try again.".to_string(), |a| format!(
|
||||
"You have {} attempts left.",
|
||||
a
|
||||
))
|
||||
|
@ -141,14 +141,14 @@ fn main() {
|
|||
};
|
||||
let origin = "https://example.com".to_string();
|
||||
let mut ctap_args = RegisterArgsCtap2 {
|
||||
challenge: chall_bytes.clone(),
|
||||
challenge: chall_bytes,
|
||||
relying_party: RelyingParty {
|
||||
id: "example.com".to_string(),
|
||||
name: None,
|
||||
icon: None,
|
||||
},
|
||||
origin: origin.clone(),
|
||||
user: user.clone(),
|
||||
origin,
|
||||
user,
|
||||
pub_cred_params: vec![
|
||||
PublicKeyCredentialParameters {
|
||||
alg: COSEAlgorithm::ES256,
|
||||
|
|
|
@ -484,9 +484,8 @@ impl AuthenticatorService {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
AuthenticatorService, AuthenticatorTransport, CtapVersion, Pin,
|
||||
PublicKeyCredentialDescriptor, RegisterArgs, RegisterArgsCtap1, RegisterArgsCtap2,
|
||||
SignArgs, SignArgsCtap1, SignArgsCtap2, User,
|
||||
AuthenticatorService, AuthenticatorTransport, CtapVersion, Pin, RegisterArgs,
|
||||
RegisterArgsCtap1, RegisterArgsCtap2, SignArgs, SignArgsCtap1, SignArgsCtap2, User,
|
||||
};
|
||||
use crate::consts::Capability;
|
||||
use crate::consts::PARAMETER_SIZE;
|
||||
|
@ -932,9 +931,9 @@ mod tests {
|
|||
.is_ok());
|
||||
callback.wait();
|
||||
|
||||
assert_eq!(was_cancelled_one.load(Ordering::SeqCst), false);
|
||||
assert_eq!(was_cancelled_two.load(Ordering::SeqCst), true);
|
||||
assert_eq!(was_cancelled_three.load(Ordering::SeqCst), true);
|
||||
assert!(!was_cancelled_one.load(Ordering::SeqCst));
|
||||
assert!(was_cancelled_two.load(Ordering::SeqCst));
|
||||
assert!(was_cancelled_three.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -972,9 +971,9 @@ mod tests {
|
|||
.is_ok());
|
||||
callback.wait();
|
||||
|
||||
assert_eq!(was_cancelled_one.load(Ordering::SeqCst), false);
|
||||
assert_eq!(was_cancelled_two.load(Ordering::SeqCst), true);
|
||||
assert_eq!(was_cancelled_three.load(Ordering::SeqCst), true);
|
||||
assert!(!was_cancelled_one.load(Ordering::SeqCst));
|
||||
assert!(was_cancelled_two.load(Ordering::SeqCst));
|
||||
assert!(was_cancelled_three.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1012,9 +1011,8 @@ mod tests {
|
|||
|
||||
let one = was_cancelled_one.load(Ordering::SeqCst);
|
||||
let two = was_cancelled_two.load(Ordering::SeqCst);
|
||||
assert_eq!(
|
||||
assert!(
|
||||
one ^ two,
|
||||
true,
|
||||
"asserting that one={} xor two={} is true",
|
||||
one,
|
||||
two
|
||||
|
|
|
@ -72,7 +72,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_free(mgr: *mut AuthenticatorService) {
|
|||
/// The handle returned by this method must be freed by the caller.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_app_ids_new() -> *mut U2FAppIds {
|
||||
Box::into_raw(Box::new(vec![]))
|
||||
Box::into_raw(Box::default())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
|
@ -103,7 +103,7 @@ pub unsafe extern "C" fn rust_u2f_app_ids_free(ids: *mut U2FAppIds) {
|
|||
/// The handle returned by this method must be freed by the caller.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_khs_new() -> *mut U2FKeyHandles {
|
||||
Box::into_raw(Box::new(vec![]))
|
||||
Box::into_raw(Box::default())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
|
|
|
@ -66,9 +66,9 @@ pub enum HIDCmd {
|
|||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl Into<u8> for HIDCmd {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
impl From<HIDCmd> for u8 {
|
||||
fn from(v: HIDCmd) -> u8 {
|
||||
match v {
|
||||
HIDCmd::Ping => CTAPHID_PING,
|
||||
HIDCmd::Msg => CTAPHID_MSG,
|
||||
HIDCmd::Lock => CTAPHID_LOCK,
|
||||
|
|
|
@ -6,7 +6,7 @@ This is a dummy implementation for CI, to avoid having to install NSS or openSSL
|
|||
|
||||
pub type Result<T> = std::result::Result<T, BackendError>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub enum BackendError {}
|
||||
|
||||
pub(crate) fn serialize_key(_curve: ECDSACurve, key: &[u8]) -> Result<(ByteBuf, ByteBuf)> {
|
||||
|
|
|
@ -496,7 +496,8 @@ impl<'de> Deserialize<'de> for COSEKey {
|
|||
// key_type = Some(map.next_value()?);
|
||||
}
|
||||
-1 => {
|
||||
let key_type = key_type.ok_or(SerdeError::missing_field("key_type"))?;
|
||||
let key_type =
|
||||
key_type.ok_or_else(|| SerdeError::missing_field("key_type"))?;
|
||||
if key_type == COSEKeyTypeId::RSA {
|
||||
if y.is_some() {
|
||||
return Err(SerdeError::duplicate_field("y"));
|
||||
|
@ -551,22 +552,22 @@ impl<'de> Deserialize<'de> for COSEKey {
|
|||
};
|
||||
}
|
||||
|
||||
let key_type = key_type.ok_or(SerdeError::missing_field("key_type"))?;
|
||||
let x = x.ok_or(SerdeError::missing_field("x"))?;
|
||||
let alg = alg.ok_or(SerdeError::missing_field("alg"))?;
|
||||
let key_type = key_type.ok_or_else(|| SerdeError::missing_field("key_type"))?;
|
||||
let x = x.ok_or_else(|| SerdeError::missing_field("x"))?;
|
||||
let alg = alg.ok_or_else(|| SerdeError::missing_field("alg"))?;
|
||||
|
||||
let res = match key_type {
|
||||
COSEKeyTypeId::OKP => {
|
||||
let curve = curve.ok_or(SerdeError::missing_field("curve"))?;
|
||||
let curve = curve.ok_or_else(|| SerdeError::missing_field("curve"))?;
|
||||
COSEKeyType::OKP(COSEOKPKey { curve, x })
|
||||
}
|
||||
COSEKeyTypeId::EC2 => {
|
||||
let curve = curve.ok_or(SerdeError::missing_field("curve"))?;
|
||||
let y = y.ok_or(SerdeError::missing_field("y"))?;
|
||||
let curve = curve.ok_or_else(|| SerdeError::missing_field("curve"))?;
|
||||
let y = y.ok_or_else(|| SerdeError::missing_field("y"))?;
|
||||
COSEKeyType::EC2(COSEEC2Key { curve, x, y })
|
||||
}
|
||||
COSEKeyTypeId::RSA => {
|
||||
let e = y.ok_or(SerdeError::missing_field("y"))?;
|
||||
let e = y.ok_or_else(|| SerdeError::missing_field("y"))?;
|
||||
COSEKeyType::RSA(COSERSAKey { e, n: x })
|
||||
}
|
||||
COSEKeyTypeId::Symmetric => COSEKeyType::Symmetric(COSESymmetricKey { key: x }),
|
||||
|
@ -852,15 +853,15 @@ mod test {
|
|||
test_encapsulate(&peer_key, COSEAlgorithm::ES256, &my_pub_key_data, &EC_PRIV).unwrap();
|
||||
assert_eq!(shared_secret.shared_secret, SHARED);
|
||||
|
||||
let token_enc = encrypt(&shared_secret.shared_secret(), &TOKEN).unwrap();
|
||||
let token_enc = encrypt(shared_secret.shared_secret(), &TOKEN).unwrap();
|
||||
assert_eq!(token_enc, TOKEN_ENC);
|
||||
|
||||
let token = decrypt(&shared_secret.shared_secret(), &TOKEN_ENC).unwrap();
|
||||
let token = decrypt(shared_secret.shared_secret(), &TOKEN_ENC).unwrap();
|
||||
assert_eq!(token, TOKEN);
|
||||
|
||||
let pin = Pin::new("1234");
|
||||
let pin_hash_enc =
|
||||
encrypt(&shared_secret.shared_secret(), pin.for_pin_token().as_ref()).unwrap();
|
||||
encrypt(shared_secret.shared_secret(), pin.for_pin_token().as_ref()).unwrap();
|
||||
assert_eq!(pin_hash_enc, PIN_HASH_ENC);
|
||||
}
|
||||
|
||||
|
|
|
@ -235,7 +235,7 @@ pub(crate) fn encapsulate(peer_cose_key: &COSEKey) -> Result<ECDHSecret> {
|
|||
}
|
||||
|
||||
fn nss_public_key_from_der_spki(spki: &[u8]) -> Result<PublicKey> {
|
||||
let mut spki_item = SECItemBorrowed::wrap(&spki);
|
||||
let mut spki_item = SECItemBorrowed::wrap(spki);
|
||||
let spki_item_ptr: *mut SECItem = spki_item.as_mut();
|
||||
let nss_spki = unsafe {
|
||||
SubjectPublicKeyInfo::from_ptr(SECKEY_DecodeDERSubjectPublicKeyInfo(spki_item_ptr))?
|
||||
|
@ -452,7 +452,7 @@ pub(crate) fn test_encapsulate(
|
|||
nss_gk_api::init();
|
||||
|
||||
let peer_cose_key = COSEKey {
|
||||
alg: alg,
|
||||
alg,
|
||||
key: COSEKeyType::EC2(peer_coseec2_key.clone()),
|
||||
};
|
||||
let spki = der_spki_from_cose(&peer_cose_key)?;
|
||||
|
@ -464,7 +464,7 @@ pub(crate) fn test_encapsulate(
|
|||
let pkcs8_private_key_info_version = &[0x02, 0x01, 0x00];
|
||||
let rfc5915_ec_private_key_version = &[0x02, 0x01, 0x01];
|
||||
|
||||
let (curve_oid, seq_len, alg_len, attr_len, ecpriv_len, param_len, spk_len) =
|
||||
let (_curve_oid, seq_len, alg_len, attr_len, ecpriv_len, param_len, spk_len) =
|
||||
match peer_coseec2_key.curve {
|
||||
ECDSACurve::SECP256R1 => (
|
||||
DER_OID_P256_BYTES,
|
||||
|
@ -508,7 +508,7 @@ pub(crate) fn test_encapsulate(
|
|||
pkcs8_priv.push(0x03);
|
||||
pkcs8_priv.extend_from_slice(spk_len);
|
||||
pkcs8_priv.push(0x0);
|
||||
pkcs8_priv.extend_from_slice(&my_pub_key);
|
||||
pkcs8_priv.extend_from_slice(my_pub_key);
|
||||
|
||||
// Now we can import the private key.
|
||||
let slot = Slot::internal()?;
|
||||
|
|
|
@ -125,7 +125,7 @@ pub(crate) fn encapsulate(key: &COSEKey) -> Result<ECDHSecret> {
|
|||
let group = EcGroup::from_curve_name(curve_name)?;
|
||||
let my_key = EcKey::generate(&group)?;
|
||||
|
||||
encapsulate_helper(&ec2key, key.alg, group, my_key)
|
||||
encapsulate_helper(ec2key, key.alg, group, my_key)
|
||||
} else {
|
||||
Err(BackendError::UnsupportedKeyType)
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ pub(crate) fn encapsulate_helper(
|
|||
let my_public_key = COSEKey {
|
||||
alg,
|
||||
key: COSEKeyType::EC2(COSEEC2Key {
|
||||
curve: key.curve.clone(),
|
||||
curve: key.curve,
|
||||
x: x.to_vec(),
|
||||
y: y.to_vec(),
|
||||
}),
|
||||
|
@ -274,7 +274,7 @@ pub(crate) fn verify(
|
|||
data: &[u8],
|
||||
) -> Result<bool> {
|
||||
let _alg = to_openssl_name(sig_alg)?; // TODO(MS): Actually use this to determine the right MessageDigest below
|
||||
let pkey = X509::from_der(&pub_key)?;
|
||||
let pkey = X509::from_der(pub_key)?;
|
||||
let pubkey = pkey.public_key()?;
|
||||
let mut verifier = Verifier::new(MessageDigest::sha256(), &pubkey)?;
|
||||
verifier.update(data)?;
|
||||
|
|
|
@ -139,6 +139,25 @@ bool rust_ctap2_register_result_attestation_copy(
|
|||
const rust_ctap2_register_result* res,
|
||||
uint8_t *dst
|
||||
);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is used to get the length, prior to calling
|
||||
/// rust_ctap2_register_result_credential_id_copy()
|
||||
bool rust_ctap2_register_result_credential_id_len(
|
||||
const rust_ctap2_register_result *res,
|
||||
size_t *len
|
||||
);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This method does not ensure anything about dst before copying, so
|
||||
/// ensure it is long enough (using rust_ctap2_register_result_credential_id_len)
|
||||
bool rust_ctap2_register_result_credential_id_copy(
|
||||
const rust_ctap2_register_result* res,
|
||||
uint8_t *dst
|
||||
);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is used to get the length, prior to calling
|
||||
|
|
|
@ -36,7 +36,7 @@ impl Serialize for HmacSecretResponse {
|
|||
{
|
||||
match self {
|
||||
HmacSecretResponse::Confirmed(x) => serializer.serialize_bool(*x),
|
||||
HmacSecretResponse::Secret(x) => serializer.serialize_bytes(&x),
|
||||
HmacSecretResponse::Secret(x) => serializer.serialize_bytes(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ pub struct Extension {
|
|||
pub hmac_secret: Option<HmacSecretResponse>,
|
||||
}
|
||||
|
||||
fn parse_extensions<'a>(input: &'a [u8]) -> IResult<&'a [u8], Extension, NomError<&'a [u8]>> {
|
||||
fn parse_extensions(input: &[u8]) -> IResult<&[u8], Extension, NomError<&[u8]>> {
|
||||
serde_to_nom(input)
|
||||
}
|
||||
|
||||
|
@ -88,10 +88,12 @@ fn parse_extensions<'a>(input: &'a [u8]) -> IResult<&'a [u8], Extension, NomErro
|
|||
pub struct AAGuid(pub [u8; 16]);
|
||||
|
||||
impl AAGuid {
|
||||
pub fn from(src: &[u8]) -> Result<AAGuid, ()> {
|
||||
pub fn from(src: &[u8]) -> Result<AAGuid, AuthenticatorError> {
|
||||
let mut payload = [0u8; 16];
|
||||
if src.len() != payload.len() {
|
||||
Err(())
|
||||
Err(AuthenticatorError::InternalError(String::from(
|
||||
"Failed to parse AAGuid",
|
||||
)))
|
||||
} else {
|
||||
payload.copy_from_slice(src);
|
||||
Ok(AAGuid(payload))
|
||||
|
@ -175,9 +177,9 @@ where
|
|||
// .map_err(|_| NomErr::Error(Context::Code(input, ErrorKind::Custom(42))))
|
||||
}
|
||||
|
||||
fn parse_attested_cred_data<'a>(
|
||||
input: &'a [u8],
|
||||
) -> IResult<&'a [u8], AttestedCredentialData, NomError<&'a [u8]>> {
|
||||
fn parse_attested_cred_data(
|
||||
input: &[u8],
|
||||
) -> IResult<&[u8], AttestedCredentialData, NomError<&[u8]>> {
|
||||
let (rest, aaguid_res) = map(take(16u8), AAGuid::from)(input)?;
|
||||
// // We can unwrap here, since we _know_ the input will be 16 bytes error out before calling from()
|
||||
let aaguid = aaguid_res.unwrap();
|
||||
|
@ -189,7 +191,7 @@ fn parse_attested_cred_data<'a>(
|
|||
(AttestedCredentialData {
|
||||
aaguid,
|
||||
credential_id,
|
||||
credential_public_key: credential_public_key,
|
||||
credential_public_key,
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
@ -212,7 +214,7 @@ pub struct AuthenticatorData {
|
|||
pub extensions: Extension,
|
||||
}
|
||||
|
||||
fn parse_ad<'a>(input: &'a [u8]) -> IResult<&'a [u8], AuthenticatorData, NomError<&'a [u8]>> {
|
||||
fn parse_ad(input: &[u8]) -> IResult<&[u8], AuthenticatorData, NomError<&[u8]>> {
|
||||
let (rest, rp_id_hash_res) = map(take(32u8), RpIdHash::from)(input)?;
|
||||
// We can unwrap here, since we _know_ the input to from() will be 32 bytes or error out before calling from()
|
||||
let rp_id_hash = rp_id_hash_res.unwrap();
|
||||
|
@ -284,15 +286,15 @@ impl<'de> Deserialize<'de> for AuthenticatorData {
|
|||
impl AuthenticatorData {
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>, AuthenticatorError> {
|
||||
let mut data = Vec::new();
|
||||
data.extend(&self.rp_id_hash.0);
|
||||
data.extend(&[self.flags.bits()]);
|
||||
data.extend(&self.counter.to_be_bytes());
|
||||
data.extend(self.rp_id_hash.0);
|
||||
data.extend([self.flags.bits()]);
|
||||
data.extend(self.counter.to_be_bytes());
|
||||
|
||||
// TODO(baloo): need to yield credential_data and extensions, but that dependends on flags,
|
||||
// should we consider another type system?
|
||||
if let Some(cred) = &self.credential_data {
|
||||
data.extend(&cred.aaguid.0);
|
||||
data.extend(&(cred.credential_id.len() as u16).to_be_bytes());
|
||||
data.extend(cred.aaguid.0);
|
||||
data.extend((cred.credential_id.len() as u16).to_be_bytes());
|
||||
data.extend(&cred.credential_id);
|
||||
data.extend(
|
||||
&serde_cbor::to_vec(&cred.credential_public_key)
|
||||
|
@ -524,7 +526,7 @@ impl Serialize for AttestationObject {
|
|||
let auth_data = self
|
||||
.auth_data
|
||||
.to_vec()
|
||||
.map(|v| serde_cbor::Value::Bytes(v))
|
||||
.map(serde_cbor::Value::Bytes)
|
||||
.map_err(|_| SerError::custom("Failed to serialize auth_data"))?;
|
||||
|
||||
map.serialize_entry(&"authData", &auth_data)?;
|
||||
|
|
|
@ -107,12 +107,7 @@ impl<'de> Deserialize<'de> for TokenBinding {
|
|||
}
|
||||
}
|
||||
"supported" => Ok(TokenBinding::Supported),
|
||||
k => {
|
||||
return Err(M::Error::custom(format!(
|
||||
"unexpected status key: {:?}",
|
||||
k
|
||||
)));
|
||||
}
|
||||
k => Err(M::Error::custom(format!("unexpected status key: {:?}", k))),
|
||||
}
|
||||
} else {
|
||||
Err(SerdeError::missing_field("status"))
|
||||
|
@ -144,8 +139,8 @@ impl Serialize for WebauthnType {
|
|||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
WebauthnType::Create => serializer.serialize_str(&"webauthn.create"),
|
||||
WebauthnType::Get => serializer.serialize_str(&"webauthn.get"),
|
||||
WebauthnType::Create => serializer.serialize_str("webauthn.create"),
|
||||
WebauthnType::Get => serializer.serialize_str("webauthn.get"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +180,7 @@ pub struct Challenge(pub String);
|
|||
|
||||
impl Challenge {
|
||||
pub fn new(input: Vec<u8>) -> Self {
|
||||
let value = base64::encode_config(&input, base64::URL_SAFE_NO_PAD);
|
||||
let value = base64::encode_config(input, base64::URL_SAFE_NO_PAD);
|
||||
Challenge(value)
|
||||
}
|
||||
}
|
||||
|
@ -341,7 +336,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_collected_client_data_parsing() {
|
||||
let original_str = "{\"type\":\"webauthn.create\",\"challenge\":\"AAECAw\",\"origin\":\"example.com\",\"crossOrigin\":false,\"tokenBinding\":{\"status\":\"present\",\"id\":\"AAECAw\"}}";
|
||||
let parsed: CollectedClientData = serde_json::from_str(&original_str).unwrap();
|
||||
let parsed: CollectedClientData = serde_json::from_str(original_str).unwrap();
|
||||
let expected = CollectedClientData {
|
||||
webauthn_type: WebauthnType::Create,
|
||||
challenge: Challenge::new(vec![0x00, 0x01, 0x02, 0x03]),
|
||||
|
@ -359,7 +354,7 @@ mod test {
|
|||
fn test_collected_client_data_defaults() {
|
||||
let cross_origin_str = "{\"type\":\"webauthn.create\",\"challenge\":\"AAECAw\",\"origin\":\"example.com\",\"crossOrigin\":false,\"tokenBinding\":{\"status\":\"present\",\"id\":\"AAECAw\"}}";
|
||||
let no_cross_origin_str = "{\"type\":\"webauthn.create\",\"challenge\":\"AAECAw\",\"origin\":\"example.com\",\"tokenBinding\":{\"status\":\"present\",\"id\":\"AAECAw\"}}";
|
||||
let parsed: CollectedClientData = serde_json::from_str(&no_cross_origin_str).unwrap();
|
||||
let parsed: CollectedClientData = serde_json::from_str(no_cross_origin_str).unwrap();
|
||||
let expected = CollectedClientData {
|
||||
webauthn_type: WebauthnType::Create,
|
||||
challenge: Challenge::new(vec![0x00, 0x01, 0x02, 0x03]),
|
||||
|
@ -387,13 +382,11 @@ mod test {
|
|||
assert_eq!(
|
||||
c.hash(),
|
||||
// echo -n '{"type":"webauthn.create","challenge":"AAECAw","origin":"example.com","crossOrigin":false,"tokenBinding":{"status":"present","id":"AAECAw"}}' | sha256sum -t
|
||||
ClientDataHash {
|
||||
0: [
|
||||
0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11,
|
||||
0x32, 0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f,
|
||||
0x10, 0x87, 0x54, 0xc3, 0x2d, 0x80
|
||||
]
|
||||
}
|
||||
ClientDataHash([
|
||||
0x75, 0x35, 0x35, 0x7d, 0x49, 0x6e, 0x33, 0xc8, 0x18, 0x7f, 0xea, 0x8d, 0x11, 0x32,
|
||||
0x64, 0xaa, 0xa4, 0x52, 0x3e, 0x13, 0x40, 0x14, 0x9f, 0xbe, 0x00, 0x3f, 0x10, 0x87,
|
||||
0x54, 0xc3, 0x2d, 0x80
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,7 +291,7 @@ impl<'sc, 'pin> ClientPINSubCommand for GetPinToken<'sc, 'pin> {
|
|||
let input = self.pin.for_pin_token();
|
||||
trace!("pin_hash = {:#04X?}", &input.as_ref());
|
||||
let pin_hash_enc = encrypt(self.shared_secret.shared_secret(), input.as_ref())
|
||||
.map_err(|e| CryptoError::Backend(e))?;
|
||||
.map_err(CryptoError::Backend)?;
|
||||
trace!("pin_hash_enc = {:#04X?}", &pin_hash_enc);
|
||||
|
||||
Ok(ClientPIN {
|
||||
|
@ -315,7 +315,7 @@ impl<'sc, 'pin> ClientPINSubCommand for GetPinToken<'sc, 'pin> {
|
|||
self.shared_secret.shared_secret(),
|
||||
encrypted_pin_token.as_ref(),
|
||||
)
|
||||
.map_err(|e| CryptoError::Backend(e))?;
|
||||
.map_err(CryptoError::Backend)?;
|
||||
let pin_token = PinToken(pin_token);
|
||||
Ok(pin_token)
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ impl<'sc, 'pin> ClientPINSubCommand for GetPinUvAuthTokenUsingPinWithPermissions
|
|||
fn as_client_pin(&self) -> Result<ClientPIN, CommandError> {
|
||||
let input = self.pin.for_pin_token();
|
||||
let pin_hash_enc = encrypt(self.shared_secret.shared_secret(), input.as_ref())
|
||||
.map_err(|e| CryptoError::Backend(e))?;
|
||||
.map_err(CryptoError::Backend)?;
|
||||
|
||||
Ok(ClientPIN {
|
||||
pin_protocol: Some(self.pin_protocol),
|
||||
|
@ -387,7 +387,7 @@ impl<'sc, 'pin> ClientPINSubCommand for GetPinUvAuthTokenUsingPinWithPermissions
|
|||
self.shared_secret.shared_secret(),
|
||||
encrypted_pin_token.as_ref(),
|
||||
)
|
||||
.map_err(|e| CryptoError::Backend(e))?;
|
||||
.map_err(CryptoError::Backend)?;
|
||||
let pin_token = PinToken(pin_token);
|
||||
Ok(pin_token)
|
||||
}
|
||||
|
@ -475,8 +475,7 @@ impl<'sc, 'pin> ClientPINSubCommand for SetNewPin<'sc, 'pin> {
|
|||
|
||||
let shared_secret = self.shared_secret.shared_secret();
|
||||
// AES256-CBC(sharedSecret, IV=0, newPin)
|
||||
let new_pin_enc =
|
||||
encrypt(shared_secret, input.as_ref()).map_err(|e| CryptoError::Backend(e))?;
|
||||
let new_pin_enc = encrypt(shared_secret, input.as_ref()).map_err(CryptoError::Backend)?;
|
||||
|
||||
// LEFT(HMAC-SHA-265(sharedSecret, newPinEnc), 16)
|
||||
let pin_auth = PinToken(shared_secret.to_vec())
|
||||
|
@ -554,13 +553,12 @@ impl<'sc, 'pin> ClientPINSubCommand for ChangeExistingPin<'sc, 'pin> {
|
|||
|
||||
let shared_secret = self.shared_secret.shared_secret();
|
||||
// AES256-CBC(sharedSecret, IV=0, newPin)
|
||||
let new_pin_enc =
|
||||
encrypt(shared_secret, input.as_ref()).map_err(|e| CryptoError::Backend(e))?;
|
||||
let new_pin_enc = encrypt(shared_secret, input.as_ref()).map_err(CryptoError::Backend)?;
|
||||
|
||||
// AES256-CBC(sharedSecret, IV=0, LEFT(SHA-256(oldPin), 16))
|
||||
let input = self.current_pin.for_pin_token();
|
||||
let pin_hash_enc = encrypt(self.shared_secret.shared_secret(), input.as_ref())
|
||||
.map_err(|e| CryptoError::Backend(e))?;
|
||||
.map_err(CryptoError::Backend)?;
|
||||
|
||||
// LEFT(HMAC-SHA-265(sharedSecret, newPinEnc), 16)
|
||||
let pin_auth = PinToken(shared_secret.to_vec())
|
||||
|
@ -628,9 +626,8 @@ where
|
|||
let status: StatusCode = input[0].into();
|
||||
debug!("response status code: {:?}", status);
|
||||
if status.is_ok() {
|
||||
let res = <T as ClientPINSubCommand>::parse_response_payload(self, &input[1..])
|
||||
.map_err(HIDError::Command);
|
||||
res
|
||||
<T as ClientPINSubCommand>::parse_response_payload(self, &input[1..])
|
||||
.map_err(HIDError::Command)
|
||||
} else {
|
||||
let add_data = if input.len() > 1 {
|
||||
let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
|
||||
|
@ -722,7 +719,7 @@ impl Pin {
|
|||
|
||||
pub fn for_pin_token(&self) -> PinAuth {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&self.0.as_bytes());
|
||||
hasher.update(self.0.as_bytes());
|
||||
|
||||
let mut output = [0u8; 16];
|
||||
let len = output.len();
|
||||
|
|
|
@ -32,7 +32,7 @@ use std::convert::TryInto;
|
|||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum GetAssertionResult {
|
||||
CTAP1(Vec<u8>),
|
||||
CTAP2(AssertionObject, CollectedClientDataWrapper),
|
||||
|
@ -109,15 +109,13 @@ impl HmacSecretExtension {
|
|||
}
|
||||
None => encrypt(secret.shared_secret(), &self.salt1[..32]),
|
||||
}
|
||||
.map_err(|e| CryptoError::Backend(e))?;
|
||||
.map_err(CryptoError::Backend)?;
|
||||
let salt_auth_full =
|
||||
authenticate(secret.shared_secret(), &salt_enc).map_err(|e| CryptoError::Backend(e))?;
|
||||
authenticate(secret.shared_secret(), &salt_enc).map_err(CryptoError::Backend)?;
|
||||
let salt_auth = salt_auth_full
|
||||
.windows(16)
|
||||
.next()
|
||||
.ok_or(AuthenticatorError::InternalError(String::from(
|
||||
"salt_auth too short",
|
||||
)))?
|
||||
.ok_or_else(|| AuthenticatorError::InternalError(String::from("salt_auth too short")))?
|
||||
.try_into()
|
||||
.map_err(|_| {
|
||||
AuthenticatorError::InternalError(String::from(
|
||||
|
@ -318,8 +316,9 @@ pub(crate) struct CheckKeyHandle<'assertion> {
|
|||
|
||||
impl<'assertion> RequestCtap1 for CheckKeyHandle<'assertion> {
|
||||
type Output = ();
|
||||
type AdditionalInfo = ();
|
||||
|
||||
fn ctap1_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
|
||||
fn ctap1_format<Dev>(&self, _dev: &mut Dev) -> Result<(Vec<u8>, Self::AdditionalInfo), HIDError>
|
||||
where
|
||||
Dev: U2FDevice + io::Read + io::Write + fmt::Debug,
|
||||
{
|
||||
|
@ -334,13 +333,14 @@ impl<'assertion> RequestCtap1 for CheckKeyHandle<'assertion> {
|
|||
auth_data.extend_from_slice(self.key_handle);
|
||||
let cmd = U2F_AUTHENTICATE;
|
||||
let apdu = CTAP1RequestAPDU::serialize(cmd, flags, &auth_data)?;
|
||||
Ok(apdu)
|
||||
Ok((apdu, ()))
|
||||
}
|
||||
|
||||
fn handle_response_ctap1(
|
||||
&self,
|
||||
status: Result<(), ApduErrorStatus>,
|
||||
_input: &[u8],
|
||||
_add_info: &Self::AdditionalInfo,
|
||||
) -> Result<Self::Output, Retryable<HIDError>> {
|
||||
// From the U2F-spec: https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#registration-request-message---u2f_register
|
||||
// if the control byte is set to 0x07 by the FIDO Client, the U2F token is supposed to
|
||||
|
@ -359,8 +359,9 @@ impl<'assertion> RequestCtap1 for CheckKeyHandle<'assertion> {
|
|||
|
||||
impl RequestCtap1 for GetAssertion {
|
||||
type Output = GetAssertionResult;
|
||||
type AdditionalInfo = PublicKeyCredentialDescriptor;
|
||||
|
||||
fn ctap1_format<Dev>(&self, dev: &mut Dev) -> Result<Vec<u8>, HIDError>
|
||||
fn ctap1_format<Dev>(&self, dev: &mut Dev) -> Result<(Vec<u8>, Self::AdditionalInfo), HIDError>
|
||||
where
|
||||
Dev: io::Read + io::Write + fmt::Debug + FidoDevice,
|
||||
{
|
||||
|
@ -379,11 +380,14 @@ impl RequestCtap1 for GetAssertion {
|
|||
};
|
||||
let res = dev.send_ctap1(&check_command);
|
||||
match res {
|
||||
Ok(_) => Some(allowed_handle.id.clone()),
|
||||
Ok(_) => Some(allowed_handle.clone()),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.ok_or(HIDError::DeviceNotSupported)?;
|
||||
.ok_or(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::NoCredentials,
|
||||
None,
|
||||
)))?;
|
||||
|
||||
debug!("sending key_handle = {:?}", key_handle);
|
||||
|
||||
|
@ -393,7 +397,7 @@ impl RequestCtap1 for GetAssertion {
|
|||
0
|
||||
};
|
||||
let mut auth_data =
|
||||
Vec::with_capacity(2 * PARAMETER_SIZE + 1 /* key_handle_len */ + key_handle.len());
|
||||
Vec::with_capacity(2 * PARAMETER_SIZE + 1 /* key_handle_len */ + key_handle.id.len());
|
||||
|
||||
if self.is_ctap2_request() {
|
||||
auth_data.extend_from_slice(self.client_data_hash().as_ref());
|
||||
|
@ -406,18 +410,19 @@ impl RequestCtap1 for GetAssertion {
|
|||
auth_data.extend_from_slice(&decoded);
|
||||
}
|
||||
auth_data.extend_from_slice(self.rp.hash().as_ref());
|
||||
auth_data.extend_from_slice(&[key_handle.len() as u8]);
|
||||
auth_data.extend_from_slice(key_handle.as_ref());
|
||||
auth_data.extend_from_slice(&[key_handle.id.len() as u8]);
|
||||
auth_data.extend_from_slice(key_handle.id.as_ref());
|
||||
|
||||
let cmd = U2F_AUTHENTICATE;
|
||||
let apdu = CTAP1RequestAPDU::serialize(cmd, flags, &auth_data)?;
|
||||
Ok(apdu)
|
||||
Ok((apdu, key_handle))
|
||||
}
|
||||
|
||||
fn handle_response_ctap1(
|
||||
&self,
|
||||
status: Result<(), ApduErrorStatus>,
|
||||
input: &[u8],
|
||||
add_info: &PublicKeyCredentialDescriptor,
|
||||
) -> Result<Self::Output, Retryable<HIDError>> {
|
||||
if Err(ApduErrorStatus::ConditionsNotSatisfied) == status {
|
||||
return Err(Retryable::Retry);
|
||||
|
@ -453,7 +458,7 @@ impl RequestCtap1 for GetAssertion {
|
|||
extensions: Default::default(),
|
||||
};
|
||||
let assertion = Assertion {
|
||||
credentials: None,
|
||||
credentials: Some(add_info.clone()),
|
||||
signature,
|
||||
user: None,
|
||||
auth_data,
|
||||
|
@ -532,7 +537,7 @@ impl RequestCtap2 for GetAssertion {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Assertion {
|
||||
pub credentials: Option<PublicKeyCredentialDescriptor>, /* Was optional in CTAP2.0, is
|
||||
* mandatory in CTAP2.1 */
|
||||
|
@ -553,7 +558,7 @@ impl From<GetAssertionResponse> for Assertion {
|
|||
}
|
||||
|
||||
// TODO(baloo): Move this to src/ctap2/mod.rs?
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct AssertionObject(pub Vec<Assertion>);
|
||||
|
||||
impl AssertionObject {
|
||||
|
@ -561,7 +566,7 @@ impl AssertionObject {
|
|||
if let Some(first) = self.0.first() {
|
||||
let mut res = Vec::new();
|
||||
res.push(first.auth_data.flags.bits());
|
||||
res.extend(&first.auth_data.counter.to_be_bytes());
|
||||
res.extend(first.auth_data.counter.to_be_bytes());
|
||||
res.extend(&first.signature);
|
||||
res
|
||||
// first.signature.clone()
|
||||
|
@ -660,7 +665,10 @@ impl<'de> Deserialize<'de> for GetAssertionResponse {
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::{Assertion, GetAssertion, GetAssertionOptions, GetAssertionResult, HIDError};
|
||||
use super::{
|
||||
Assertion, CommandError, GetAssertion, GetAssertionOptions, GetAssertionResult, HIDError,
|
||||
StatusCode,
|
||||
};
|
||||
use crate::consts::{
|
||||
HIDCmd, SW_CONDITIONS_NOT_SATISFIED, SW_NO_ERROR, U2F_CHECK_IS_REGISTERED,
|
||||
U2F_REQUEST_USER_PRESENCE,
|
||||
|
@ -741,7 +749,7 @@ pub mod test {
|
|||
device.add_write(&msg, 0);
|
||||
|
||||
msg = cid.to_vec();
|
||||
msg.extend(&[0x0]); //SEQ
|
||||
msg.extend([0x0]); //SEQ
|
||||
msg.extend(vec![
|
||||
0x65, // e (continuation of type)
|
||||
0x6a, // text(10)
|
||||
|
@ -754,7 +762,7 @@ pub mod test {
|
|||
device.add_write(&msg, 0);
|
||||
|
||||
msg = cid.to_vec();
|
||||
msg.extend(&[0x1]); //SEQ
|
||||
msg.extend([0x1]); //SEQ
|
||||
msg.extend(&assertion.allow_list[0].id[42..]);
|
||||
msg.extend(vec![
|
||||
0x5, // options
|
||||
|
@ -767,31 +775,31 @@ pub mod test {
|
|||
|
||||
// fido response
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Cbor.into(), 0x1, 0x5c]); // cmd + bcnt
|
||||
msg.extend([HIDCmd::Cbor.into(), 0x1, 0x5c]); // cmd + bcnt
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[..57]);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[0x0]); // SEQ
|
||||
msg.extend([0x0]); // SEQ
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[57..116]);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[0x1]); // SEQ
|
||||
msg.extend([0x1]); // SEQ
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[116..175]);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[0x2]); // SEQ
|
||||
msg.extend([0x2]); // SEQ
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[175..234]);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[0x3]); // SEQ
|
||||
msg.extend([0x3]); // SEQ
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[234..293]);
|
||||
device.add_read(&msg, 0);
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[0x4]); // SEQ
|
||||
msg.extend([0x4]); // SEQ
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP2[293..]);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
|
@ -853,12 +861,12 @@ pub mod test {
|
|||
fn fill_device_ctap1(device: &mut Device, cid: [u8; 4], flags: u8, answer_status: [u8; 2]) {
|
||||
// ctap2 request
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Msg.into(), 0x00, 0x8A]); // cmd + bcnt
|
||||
msg.extend(&[0x00, 0x2]); // U2F_AUTHENTICATE
|
||||
msg.extend(&[flags]);
|
||||
msg.extend(&[0x00, 0x00, 0x00]);
|
||||
msg.extend(&[0x81]); // Data len - 7
|
||||
msg.extend(&CLIENT_DATA_HASH);
|
||||
msg.extend([HIDCmd::Msg.into(), 0x00, 0x8A]); // cmd + bcnt
|
||||
msg.extend([0x00, 0x2]); // U2F_AUTHENTICATE
|
||||
msg.extend([flags]);
|
||||
msg.extend([0x00, 0x00, 0x00]);
|
||||
msg.extend([0x81]); // Data len - 7
|
||||
msg.extend(CLIENT_DATA_HASH);
|
||||
msg.extend(&RELYING_PARTY_HASH[..18]);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
|
@ -866,7 +874,7 @@ pub mod test {
|
|||
let mut msg = cid.to_vec();
|
||||
msg.extend(vec![0x00]); // SEQ
|
||||
msg.extend(&RELYING_PARTY_HASH[18..]);
|
||||
msg.extend(&[KEY_HANDLE.len() as u8]);
|
||||
msg.extend([KEY_HANDLE.len() as u8]);
|
||||
msg.extend(&KEY_HANDLE[..44]);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
|
@ -877,14 +885,14 @@ pub mod test {
|
|||
|
||||
// fido response
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x4D]); // cmd + bcnt
|
||||
msg.extend([HIDCmd::Msg.into(), 0x0, 0x4D]); // cmd + bcnt
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP1[0..57]);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[0x0]); // SEQ
|
||||
msg.extend([0x0]); // SEQ
|
||||
msg.extend(&GET_ASSERTION_SAMPLE_RESPONSE_CTAP1[57..]);
|
||||
msg.extend(&answer_status);
|
||||
msg.extend(answer_status);
|
||||
device.add_read(&msg, 0);
|
||||
}
|
||||
|
||||
|
@ -897,6 +905,16 @@ pub mod test {
|
|||
cross_origin: false,
|
||||
token_binding: Some(TokenBinding::Present(String::from("AAECAw"))),
|
||||
};
|
||||
let allowed_key = PublicKeyCredentialDescriptor {
|
||||
id: vec![
|
||||
0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35, 0xEF,
|
||||
0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85,
|
||||
0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78,
|
||||
0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC,
|
||||
0xD4, 0x15, 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
|
||||
],
|
||||
transports: vec![Transport::USB],
|
||||
};
|
||||
let assertion = GetAssertion::new(
|
||||
client_data.clone(),
|
||||
RelyingPartyWrapper::Data(RelyingParty {
|
||||
|
@ -904,16 +922,7 @@ pub mod test {
|
|||
name: Some(String::from("Acme")),
|
||||
icon: None,
|
||||
}),
|
||||
vec![PublicKeyCredentialDescriptor {
|
||||
id: vec![
|
||||
0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35,
|
||||
0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, 0x71, 0x7D,
|
||||
0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5,
|
||||
0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5,
|
||||
0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
|
||||
],
|
||||
transports: vec![Transport::USB],
|
||||
}],
|
||||
vec![allowed_key.clone()],
|
||||
GetAssertionOptions {
|
||||
user_presence: Some(true),
|
||||
user_verification: None,
|
||||
|
@ -936,9 +945,10 @@ pub mod test {
|
|||
U2F_CHECK_IS_REGISTERED,
|
||||
SW_CONDITIONS_NOT_SATISFIED,
|
||||
);
|
||||
let ctap1_request = assertion.ctap1_format(&mut device).unwrap();
|
||||
let (ctap1_request, key_handle) = assertion.ctap1_format(&mut device).unwrap();
|
||||
// Check if the request is going to be correct
|
||||
assert_eq!(ctap1_request, GET_ASSERTION_SAMPLE_REQUEST_CTAP1);
|
||||
assert_eq!(key_handle, allowed_key);
|
||||
|
||||
// Now do it again, but parse the actual response
|
||||
fill_device_ctap1(
|
||||
|
@ -961,7 +971,7 @@ pub mod test {
|
|||
};
|
||||
|
||||
let expected_assertion = Assertion {
|
||||
credentials: None,
|
||||
credentials: Some(allowed_key),
|
||||
signature: vec![
|
||||
0x30, 0x44, 0x02, 0x20, 0x7B, 0xDE, 0x0A, 0x52, 0xAC, 0x1F, 0x4C, 0x8B, 0x27, 0xE0,
|
||||
0x03, 0xA3, 0x70, 0xCD, 0x66, 0xA4, 0xC7, 0x11, 0x8D, 0xD2, 0x2D, 0x54, 0x47, 0x83,
|
||||
|
@ -1024,15 +1034,25 @@ pub mod test {
|
|||
|
||||
assert_matches!(
|
||||
assertion.ctap1_format(&mut device),
|
||||
Err(HIDError::DeviceNotSupported)
|
||||
Err(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::NoCredentials,
|
||||
..
|
||||
)))
|
||||
);
|
||||
|
||||
assertion.allow_list = vec![too_long_key_handle.clone(); 5];
|
||||
// Test also multiple too long keys and an empty allow list
|
||||
for allow_list in [vec![], vec![too_long_key_handle.clone(); 5]] {
|
||||
assertion.allow_list = allow_list;
|
||||
|
||||
assert_matches!(
|
||||
assertion.ctap1_format(&mut device),
|
||||
Err(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::NoCredentials,
|
||||
..
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
assert_matches!(
|
||||
assertion.ctap1_format(&mut device),
|
||||
Err(HIDError::DeviceNotSupported)
|
||||
);
|
||||
let ok_key_handle = PublicKeyCredentialDescriptor {
|
||||
id: vec![
|
||||
0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, 0x35, 0xEF,
|
||||
|
@ -1047,8 +1067,8 @@ pub mod test {
|
|||
too_long_key_handle.clone(),
|
||||
too_long_key_handle.clone(),
|
||||
too_long_key_handle.clone(),
|
||||
ok_key_handle,
|
||||
too_long_key_handle.clone(),
|
||||
ok_key_handle.clone(),
|
||||
too_long_key_handle,
|
||||
];
|
||||
|
||||
// ctap1 request
|
||||
|
@ -1058,9 +1078,10 @@ pub mod test {
|
|||
U2F_CHECK_IS_REGISTERED,
|
||||
SW_CONDITIONS_NOT_SATISFIED,
|
||||
);
|
||||
let ctap1_request = assertion.ctap1_format(&mut device).unwrap();
|
||||
let (ctap1_request, key_handle) = assertion.ctap1_format(&mut device).unwrap();
|
||||
// Check if the request is going to be correct
|
||||
assert_eq!(ctap1_request, GET_ASSERTION_SAMPLE_REQUEST_CTAP1);
|
||||
assert_eq!(key_handle, ok_key_handle);
|
||||
|
||||
// Now do it again, but parse the actual response
|
||||
fill_device_ctap1(
|
||||
|
@ -1083,7 +1104,7 @@ pub mod test {
|
|||
};
|
||||
|
||||
let expected_assertion = Assertion {
|
||||
credentials: None,
|
||||
credentials: Some(ok_key_handle),
|
||||
signature: vec![
|
||||
0x30, 0x44, 0x02, 0x20, 0x7B, 0xDE, 0x0A, 0x52, 0xAC, 0x1F, 0x4C, 0x8B, 0x27, 0xE0,
|
||||
0x03, 0xA3, 0x70, 0xCD, 0x66, 0xA4, 0xC7, 0x11, 0x8D, 0xD2, 0x2D, 0x54, 0x47, 0x83,
|
||||
|
|
|
@ -11,15 +11,9 @@ use serde_cbor::{de::from_slice, Value};
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct GetInfo {}
|
||||
|
||||
impl Default for GetInfo {
|
||||
fn default() -> GetInfo {
|
||||
GetInfo {}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestCtap2 for GetInfo {
|
||||
type Output = AuthenticatorInfo;
|
||||
|
||||
|
|
|
@ -9,24 +9,20 @@ pub enum U2FInfo {
|
|||
U2F_V2,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
// TODO(baloo): if one does not issue U2F_VERSION before makecredentials or getassertion, token
|
||||
// will return error (ConditionsNotSatified), test this in unit tests
|
||||
pub struct GetVersion {}
|
||||
|
||||
impl Default for GetVersion {
|
||||
fn default() -> GetVersion {
|
||||
GetVersion {}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestCtap1 for GetVersion {
|
||||
type Output = U2FInfo;
|
||||
type AdditionalInfo = ();
|
||||
|
||||
fn handle_response_ctap1(
|
||||
&self,
|
||||
_status: Result<(), ApduErrorStatus>,
|
||||
input: &[u8],
|
||||
_add_info: &(),
|
||||
) -> Result<Self::Output, Retryable<HIDError>> {
|
||||
if input.is_empty() {
|
||||
return Err(Retryable::Error(HIDError::Command(
|
||||
|
@ -42,7 +38,7 @@ impl RequestCtap1 for GetVersion {
|
|||
}
|
||||
}
|
||||
|
||||
fn ctap1_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
|
||||
fn ctap1_format<Dev>(&self, _dev: &mut Dev) -> Result<(Vec<u8>, ()), HIDError>
|
||||
where
|
||||
Dev: U2FDevice,
|
||||
{
|
||||
|
@ -50,7 +46,7 @@ impl RequestCtap1 for GetVersion {
|
|||
|
||||
let cmd = U2F_VERSION;
|
||||
let data = CTAP1RequestAPDU::serialize(cmd, flags, &[])?;
|
||||
Ok(data)
|
||||
Ok((data, ()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +69,7 @@ pub mod tests {
|
|||
|
||||
// init packet
|
||||
let mut msg = CID_BROADCAST.to_vec();
|
||||
msg.extend(&[HIDCmd::Init.into(), 0x00, 0x08]); // cmd + bcnt
|
||||
msg.extend([HIDCmd::Init.into(), 0x00, 0x08]); // cmd + bcnt
|
||||
msg.extend_from_slice(&nonce);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
|
@ -87,20 +83,20 @@ pub mod tests {
|
|||
msg.extend_from_slice(&cid); // new channel id
|
||||
|
||||
// We are not setting CBOR, to signal that the device does not support CTAP1
|
||||
msg.extend(&[0x02, 0x04, 0x01, 0x08, 0x01]); // versions + flags (wink)
|
||||
msg.extend([0x02, 0x04, 0x01, 0x08, 0x01]); // versions + flags (wink)
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
// ctap1 U2F_VERSION request
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x7]); // cmd + bcnt
|
||||
msg.extend(&[0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0]);
|
||||
msg.extend([HIDCmd::Msg.into(), 0x0, 0x7]); // cmd + bcnt
|
||||
msg.extend([0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0]);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
// fido response
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x08]); // cmd + bcnt
|
||||
msg.extend(&[0x55, 0x32, 0x46, 0x5f, 0x56, 0x32]); // 'U2F_V2'
|
||||
msg.extend(&SW_NO_ERROR);
|
||||
msg.extend([HIDCmd::Msg.into(), 0x0, 0x08]); // cmd + bcnt
|
||||
msg.extend([0x55, 0x32, 0x46, 0x5f, 0x56, 0x32]); // 'U2F_V2'
|
||||
msg.extend(SW_NO_ERROR);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
device
|
||||
|
|
|
@ -47,7 +47,7 @@ pub enum MakeCredentialsResult {
|
|||
CTAP2(AttestationObject, CollectedClientDataWrapper),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize)]
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize)]
|
||||
#[cfg_attr(test, derive(Deserialize))]
|
||||
pub struct MakeCredentialsOptions {
|
||||
#[serde(rename = "rk", skip_serializing_if = "Option::is_none")]
|
||||
|
@ -58,15 +58,6 @@ pub struct MakeCredentialsOptions {
|
|||
// Commands need a version-flag to know what to de/serialize and what to ignore.
|
||||
}
|
||||
|
||||
impl Default for MakeCredentialsOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
resident_key: None,
|
||||
user_verification: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MakeCredentialsOptions {
|
||||
pub(crate) fn has_some(&self) -> bool {
|
||||
self.resident_key.is_some() || self.user_verification.is_some()
|
||||
|
@ -252,8 +243,9 @@ impl Request<MakeCredentialsResult> for MakeCredentials {
|
|||
|
||||
impl RequestCtap1 for MakeCredentials {
|
||||
type Output = MakeCredentialsResult;
|
||||
type AdditionalInfo = ();
|
||||
|
||||
fn ctap1_format<Dev>(&self, dev: &mut Dev) -> Result<Vec<u8>, HIDError>
|
||||
fn ctap1_format<Dev>(&self, dev: &mut Dev) -> Result<(Vec<u8>, ()), HIDError>
|
||||
where
|
||||
Dev: io::Read + io::Write + fmt::Debug + FidoDevice,
|
||||
{
|
||||
|
@ -280,7 +272,7 @@ impl RequestCtap1 for MakeCredentials {
|
|||
let res = dev.send_ctap1(&check_command);
|
||||
res.is_ok()
|
||||
})
|
||||
.any(|x| x == true);
|
||||
.any(|x| x);
|
||||
|
||||
if is_already_registered {
|
||||
// Now we need to send a dummy registration request, to make the token blink
|
||||
|
@ -315,13 +307,14 @@ impl RequestCtap1 for MakeCredentials {
|
|||
let cmd = U2F_REGISTER;
|
||||
let apdu = CTAP1RequestAPDU::serialize(cmd, flags, ®ister_data)?;
|
||||
|
||||
Ok(apdu)
|
||||
Ok((apdu, ()))
|
||||
}
|
||||
|
||||
fn handle_response_ctap1(
|
||||
&self,
|
||||
status: Result<(), ApduErrorStatus>,
|
||||
input: &[u8],
|
||||
_add_info: &(),
|
||||
) -> Result<Self::Output, Retryable<HIDError>> {
|
||||
if Err(ApduErrorStatus::ConditionsNotSatisfied) == status {
|
||||
return Err(Retryable::Retry);
|
||||
|
@ -683,7 +676,7 @@ pub mod test {
|
|||
.expect("Failed to create MakeCredentials");
|
||||
|
||||
let mut device = Device::new("commands/make_credentials").unwrap(); // not really used (all functions ignore it)
|
||||
let req_serialized = req
|
||||
let (req_serialized, _) = req
|
||||
.ctap1_format(&mut device)
|
||||
.expect("Failed to serialize MakeCredentials request");
|
||||
assert_eq!(
|
||||
|
@ -692,7 +685,7 @@ pub mod test {
|
|||
req_serialized, MAKE_CREDENTIALS_SAMPLE_REQUEST_CTAP1
|
||||
);
|
||||
let (attestation_object, _collected_client_data) = match req
|
||||
.handle_response_ctap1(Ok(()), &MAKE_CREDENTIALS_SAMPLE_RESPONSE_CTAP1)
|
||||
.handle_response_ctap1(Ok(()), &MAKE_CREDENTIALS_SAMPLE_RESPONSE_CTAP1, &())
|
||||
.expect("Failed to handle CTAP1 response")
|
||||
{
|
||||
MakeCredentialsResult::CTAP2(attestation_object, _collected_client_data) => {
|
||||
|
|
|
@ -55,11 +55,13 @@ impl<T> From<T> for Retryable<T> {
|
|||
|
||||
pub trait RequestCtap1: fmt::Debug {
|
||||
type Output;
|
||||
// E.g.: For GetAssertion, which key-handle is currently being tested
|
||||
type AdditionalInfo;
|
||||
|
||||
/// Serializes a request into FIDO v1.x / CTAP1 / U2F format.
|
||||
///
|
||||
/// See [`crate::u2ftypes::CTAP1RequestAPDU::serialize()`]
|
||||
fn ctap1_format<Dev>(&self, dev: &mut Dev) -> Result<Vec<u8>, HIDError>
|
||||
fn ctap1_format<Dev>(&self, dev: &mut Dev) -> Result<(Vec<u8>, Self::AdditionalInfo), HIDError>
|
||||
where
|
||||
Dev: FidoDevice + Read + Write + fmt::Debug;
|
||||
|
||||
|
@ -68,6 +70,7 @@ pub trait RequestCtap1: fmt::Debug {
|
|||
&self,
|
||||
status: Result<(), ApduErrorStatus>,
|
||||
input: &[u8],
|
||||
add_info: &Self::AdditionalInfo,
|
||||
) -> Result<Self::Output, Retryable<HIDError>>;
|
||||
}
|
||||
|
||||
|
@ -104,7 +107,7 @@ pub(crate) trait PinAuthCommand {
|
|||
|
||||
let client_data_hash = self.client_data_hash();
|
||||
let (pin_auth, pin_auth_protocol) =
|
||||
match calculate_pin_auth(dev, &client_data_hash, &self.pin()) {
|
||||
match calculate_pin_auth(dev, &client_data_hash, self.pin()) {
|
||||
Ok((pin_auth, pin_auth_protocol)) => (pin_auth, pin_auth_protocol),
|
||||
Err(e) => {
|
||||
return Err(repackage_pin_errors(dev, e));
|
||||
|
@ -127,36 +130,26 @@ pub(crate) fn repackage_pin_errors<D: FidoDevice>(
|
|||
// If the given PIN was wrong, determine no. of left retries
|
||||
let cmd = GetRetries::new();
|
||||
let retries = dev.send_cbor(&cmd).ok(); // If we got retries, wrap it in Some, otherwise ignore err
|
||||
return AuthenticatorError::PinError(PinError::InvalidPin(retries));
|
||||
AuthenticatorError::PinError(PinError::InvalidPin(retries))
|
||||
}
|
||||
AuthenticatorError::HIDError(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::PinAuthBlocked,
|
||||
_,
|
||||
))) => {
|
||||
return AuthenticatorError::PinError(PinError::PinAuthBlocked);
|
||||
}
|
||||
))) => AuthenticatorError::PinError(PinError::PinAuthBlocked),
|
||||
AuthenticatorError::HIDError(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::PinBlocked,
|
||||
_,
|
||||
))) => {
|
||||
return AuthenticatorError::PinError(PinError::PinBlocked);
|
||||
}
|
||||
))) => AuthenticatorError::PinError(PinError::PinBlocked),
|
||||
AuthenticatorError::HIDError(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::PinRequired,
|
||||
_,
|
||||
))) => {
|
||||
return AuthenticatorError::PinError(PinError::PinRequired);
|
||||
}
|
||||
))) => AuthenticatorError::PinError(PinError::PinRequired),
|
||||
AuthenticatorError::HIDError(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::PinNotSet,
|
||||
_,
|
||||
))) => {
|
||||
return AuthenticatorError::PinError(PinError::PinNotSet);
|
||||
}
|
||||
))) => AuthenticatorError::PinError(PinError::PinNotSet),
|
||||
// TODO(MS): Add "PinPolicyViolated"
|
||||
err => {
|
||||
return err;
|
||||
}
|
||||
err => err,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,9 +333,9 @@ impl From<u8> for StatusCode {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Into<u8> for StatusCode {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
impl From<StatusCode> for u8 {
|
||||
fn from(v: StatusCode) -> u8 {
|
||||
match v {
|
||||
StatusCode::OK => 0x00,
|
||||
StatusCode::InvalidCommand => 0x01,
|
||||
StatusCode::InvalidParameter => 0x02,
|
||||
|
@ -451,7 +444,7 @@ where
|
|||
None,
|
||||
)))?;
|
||||
|
||||
let pin_command = GetPinToken::new(&info, &shared_secret, &pin)?;
|
||||
let pin_command = GetPinToken::new(&info, &shared_secret, pin)?;
|
||||
let pin_token = dev.send_cbor(&pin_command)?;
|
||||
|
||||
(
|
||||
|
|
|
@ -3,15 +3,9 @@ use crate::transport::errors::HIDError;
|
|||
use crate::u2ftypes::U2FDevice;
|
||||
use serde_cbor::{de::from_slice, Value};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Reset {}
|
||||
|
||||
impl Default for Reset {
|
||||
fn default() -> Reset {
|
||||
Reset {}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestCtap2 for Reset {
|
||||
type Output = ();
|
||||
|
||||
|
@ -92,18 +86,17 @@ pub mod tests {
|
|||
// Test, if we can parse the status codes specified by the spec
|
||||
|
||||
// Ok()
|
||||
let response = issue_command_and_get_response(0, &vec![]).expect("Unexpected error");
|
||||
assert_eq!(response, ());
|
||||
issue_command_and_get_response(0, &[]).expect("Unexpected error");
|
||||
|
||||
// Denied by the user
|
||||
let response = issue_command_and_get_response(0x27, &vec![]).expect_err("Not an error!");
|
||||
let response = issue_command_and_get_response(0x27, &[]).expect_err("Not an error!");
|
||||
assert!(matches!(
|
||||
response,
|
||||
HIDError::Command(CommandError::StatusCode(StatusCode::OperationDenied, None))
|
||||
));
|
||||
|
||||
// Timeout
|
||||
let response = issue_command_and_get_response(0x2F, &vec![]).expect_err("Not an error!");
|
||||
let response = issue_command_and_get_response(0x2F, &[]).expect_err("Not an error!");
|
||||
assert!(matches!(
|
||||
response,
|
||||
HIDError::Command(CommandError::StatusCode(
|
||||
|
|
|
@ -3,15 +3,9 @@ use crate::transport::errors::HIDError;
|
|||
use crate::u2ftypes::U2FDevice;
|
||||
use serde_cbor::{de::from_slice, Value};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Selection {}
|
||||
|
||||
impl Default for Selection {
|
||||
fn default() -> Selection {
|
||||
Selection {}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestCtap2 for Selection {
|
||||
type Output = ();
|
||||
|
||||
|
@ -92,18 +86,17 @@ pub mod tests {
|
|||
// Test, if we can parse the status codes specified by the spec
|
||||
|
||||
// Ok()
|
||||
let response = issue_command_and_get_response(0, &vec![]).expect("Unexpected error");
|
||||
assert_eq!(response, ());
|
||||
issue_command_and_get_response(0, &[]).expect("Unexpected error");
|
||||
|
||||
// Denied by the user
|
||||
let response = issue_command_and_get_response(0x27, &vec![]).expect_err("Not an error!");
|
||||
let response = issue_command_and_get_response(0x27, &[]).expect_err("Not an error!");
|
||||
assert!(matches!(
|
||||
response,
|
||||
HIDError::Command(CommandError::StatusCode(StatusCode::OperationDenied, None))
|
||||
));
|
||||
|
||||
// Timeout
|
||||
let response = issue_command_and_get_response(0x2F, &vec![]).expect_err("Not an error!");
|
||||
let response = issue_command_and_get_response(0x2F, &[]).expect_err("Not an error!");
|
||||
assert!(matches!(
|
||||
response,
|
||||
HIDError::Command(CommandError::StatusCode(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
pub mod commands;
|
||||
pub use commands::get_assertion::AssertionObject;
|
||||
|
||||
pub(crate) mod attestation;
|
||||
pub mod attestation;
|
||||
|
||||
pub mod client_data;
|
||||
pub mod server;
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct RpIdHash(pub [u8; 32]);
|
|||
|
||||
impl fmt::Debug for RpIdHash {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let value = base64::encode_config(&self.0, base64::URL_SAFE_NO_PAD);
|
||||
let value = base64::encode_config(self.0, base64::URL_SAFE_NO_PAD);
|
||||
write!(f, "RpIdHash({})", value)
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ impl RelyingPartyWrapper {
|
|||
hasher.update(&d.id);
|
||||
|
||||
let mut output = [0u8; 32];
|
||||
output.copy_from_slice(&hasher.finalize().as_slice());
|
||||
output.copy_from_slice(hasher.finalize().as_slice());
|
||||
|
||||
RpIdHash(output)
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ impl<'de> Deserialize<'de> for PublicKeyCredentialParameters {
|
|||
return Err(SerdeError::missing_field("type"));
|
||||
}
|
||||
|
||||
let alg = alg.ok_or(SerdeError::missing_field("alg"))?;
|
||||
let alg = alg.ok_or_else(|| SerdeError::missing_field("alg"))?;
|
||||
|
||||
Ok(PublicKeyCredentialParameters { alg })
|
||||
}
|
||||
|
@ -280,8 +280,8 @@ impl<'de> Deserialize<'de> for PublicKeyCredentialDescriptor {
|
|||
return Err(SerdeError::missing_field("type"));
|
||||
}
|
||||
|
||||
let id = id.ok_or(SerdeError::missing_field("id"))?;
|
||||
let transports = transports.unwrap_or(Vec::new());
|
||||
let id = id.ok_or_else(|| SerdeError::missing_field("id"))?;
|
||||
let transports = transports.unwrap_or_default();
|
||||
|
||||
Ok(PublicKeyCredentialDescriptor { id, transports })
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ use crate::{AttestationObject, CollectedClientDataWrapper, Pin, StatusUpdate};
|
|||
use crate::{RegisterResult, SignResult};
|
||||
use libc::size_t;
|
||||
use rand::{thread_rng, Rng};
|
||||
use serde_cbor;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
@ -90,7 +89,7 @@ pub unsafe extern "C" fn rust_ctap2_mgr_free(mgr: *mut AuthenticatorService) {
|
|||
/// The handle returned by this method must be freed by the caller.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_pkcd_new() -> *mut Ctap2PubKeyCredDescriptors {
|
||||
Box::into_raw(Box::new(vec![]))
|
||||
Box::into_raw(Box::default())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
|
@ -139,7 +138,7 @@ pub extern "C" fn rust_ctap2_mgr_new() -> *mut AuthenticatorService {
|
|||
fn rewrap_client_data(
|
||||
client_data: CollectedClientDataWrapper,
|
||||
) -> Result<CString, AuthenticatorError> {
|
||||
let s = CString::new(client_data.serialized_data.clone()).map_err(|_| {
|
||||
let s = CString::new(client_data.serialized_data).map_err(|_| {
|
||||
AuthenticatorError::Custom("Failed to transform client_data to C String".to_string())
|
||||
})?;
|
||||
Ok(s)
|
||||
|
@ -276,8 +275,8 @@ pub unsafe extern "C" fn rust_ctap2_mgr_register(
|
|||
pub_cred_params,
|
||||
exclude_list,
|
||||
options: MakeCredentialsOptions {
|
||||
resident_key: options.resident_key.then(|| true),
|
||||
user_verification: options.user_verification.then(|| true),
|
||||
resident_key: options.resident_key.then_some(true),
|
||||
user_verification: options.user_verification.then_some(true),
|
||||
},
|
||||
extensions: Default::default(),
|
||||
pin,
|
||||
|
@ -370,7 +369,7 @@ pub unsafe extern "C" fn rust_ctap2_mgr_sign(
|
|||
// The token can omit sending back credentials, if the allow_list had only one
|
||||
// entry. Thus we re-add that here now for all found assertions before handing it out.
|
||||
assertion_object.0.iter_mut().for_each(|x| {
|
||||
x.credentials = x.credentials.clone().or(single_key_handle.clone());
|
||||
x.credentials = x.credentials.clone().or_else(|| single_key_handle.clone());
|
||||
});
|
||||
rewrap_sign_result(assertion_object, client_data)
|
||||
}
|
||||
|
@ -385,8 +384,8 @@ pub unsafe extern "C" fn rust_ctap2_mgr_sign(
|
|||
relying_party_id: rpid,
|
||||
allow_list,
|
||||
options: GetAssertionOptions {
|
||||
user_presence: options.user_presence.then(|| true),
|
||||
user_verification: options.user_verification.then(|| true),
|
||||
user_presence: options.user_presence.then_some(true),
|
||||
user_verification: options.user_verification.then_some(true),
|
||||
},
|
||||
extensions: Default::default(),
|
||||
pin,
|
||||
|
@ -401,9 +400,9 @@ pub unsafe extern "C" fn rust_ctap2_mgr_sign(
|
|||
}
|
||||
}
|
||||
|
||||
// /// # Safety
|
||||
// ///
|
||||
// /// This method must be used on an actual U2FResult handle
|
||||
/// # Safety
|
||||
///
|
||||
/// This method must be used on an actual Ctap2RegisterResult-handle
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_register_result_error(res: *const Ctap2RegisterResult) -> u8 {
|
||||
if res.is_null() {
|
||||
|
@ -416,6 +415,9 @@ pub unsafe extern "C" fn rust_ctap2_register_result_error(res: *const Ctap2Regis
|
|||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This method must be used on an actual Ctap2SignResult-handle
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_sign_result_error(res: *const Ctap2SignResult) -> u8 {
|
||||
if res.is_null() {
|
||||
|
@ -479,6 +481,8 @@ unsafe fn client_data_len<T>(
|
|||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is used to get the length, prior to calling
|
||||
/// rust_ctap2_register_result_client_data_copy()
|
||||
#[no_mangle]
|
||||
|
@ -489,6 +493,8 @@ pub unsafe extern "C" fn rust_ctap2_register_result_client_data_len(
|
|||
client_data_len(res, len)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is used to get the length, prior to calling
|
||||
/// rust_ctap2_sign_result_client_data_copy()
|
||||
#[no_mangle]
|
||||
|
@ -510,7 +516,7 @@ unsafe fn client_data_copy<T>(
|
|||
match &*res {
|
||||
Ok((_, client_data)) => {
|
||||
ptr::copy_nonoverlapping(client_data.as_ptr(), dst, client_data.as_bytes().len());
|
||||
return true;
|
||||
true
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
|
@ -583,6 +589,55 @@ pub unsafe extern "C" fn rust_ctap2_register_result_attestation_copy(
|
|||
false
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is used to get the length of the credential ID
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_register_result_credential_id_len(
|
||||
res: *const Ctap2RegisterResult,
|
||||
len: *mut size_t,
|
||||
) -> bool {
|
||||
if res.is_null() || len.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Ok((att_obj, _)) = &*res {
|
||||
if let Some(credential_data) = &att_obj.auth_data.credential_data {
|
||||
*len = credential_data.credential_id.len();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This method does not ensure anything about dst before copying, so
|
||||
/// ensure it is long enough (using rust_ctap2_register_result_credential_id_len)
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_register_result_credential_id_copy(
|
||||
res: *const Ctap2RegisterResult,
|
||||
dst: *mut u8,
|
||||
) -> bool {
|
||||
if res.is_null() || dst.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Ok((att_obj, _)) = &*res {
|
||||
if let Some(credential_data) = &att_obj.auth_data.credential_data {
|
||||
let id = &credential_data.credential_id;
|
||||
ptr::copy_nonoverlapping(id.as_ptr(), dst, id.len());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must be used on an existing Ctap2SignResult.
|
||||
/// This function is used to get how many assertions there are in total
|
||||
/// The returned number can be used as index-maximum to access individual
|
||||
/// fields
|
||||
|
@ -598,7 +653,7 @@ pub unsafe extern "C" fn rust_ctap2_sign_result_assertions_len(
|
|||
match &*res {
|
||||
Ok((assertions, _)) => {
|
||||
*len = assertions.0.len();
|
||||
return true;
|
||||
true
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
|
@ -611,20 +666,19 @@ fn sign_result_item_len(assertion: &Assertion, item_idx: u8) -> Option<usize> {
|
|||
SIGN_RESULT_AUTH_DATA => assertion.auth_data.to_vec().ok().map(|x| x.len()),
|
||||
SIGN_RESULT_SIGNATURE => Some(assertion.signature.len()),
|
||||
SIGN_RESULT_USER_ID => assertion.user.as_ref().map(|u| u.id.len()),
|
||||
SIGN_RESULT_USER_NAME => assertion
|
||||
.user
|
||||
.as_ref()
|
||||
.map(|u| {
|
||||
u.display_name
|
||||
.as_ref()
|
||||
.or(u.name.as_ref())
|
||||
.map(|n| n.as_bytes().len())
|
||||
})
|
||||
.flatten(),
|
||||
SIGN_RESULT_USER_NAME => assertion.user.as_ref().and_then(|u| {
|
||||
u.display_name
|
||||
.as_ref()
|
||||
.or(u.name.as_ref())
|
||||
.map(|n| n.as_bytes().len())
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must be used on an existing Ctap2SignResult.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_sign_result_item_contains(
|
||||
res: *const Ctap2SignResult,
|
||||
|
@ -696,16 +750,12 @@ unsafe fn sign_result_item_copy(assertion: &Assertion, item_idx: u8, dst: *mut u
|
|||
}
|
||||
SIGN_RESULT_SIGNATURE => Some(assertion.signature.as_ref()),
|
||||
SIGN_RESULT_USER_ID => assertion.user.as_ref().map(|u| u.id.as_ref()),
|
||||
SIGN_RESULT_USER_NAME => assertion
|
||||
.user
|
||||
.as_ref()
|
||||
.map(|u| {
|
||||
u.display_name
|
||||
.as_ref()
|
||||
.or(u.name.as_ref())
|
||||
.map(|n| n.as_bytes().as_ref())
|
||||
})
|
||||
.flatten(),
|
||||
SIGN_RESULT_USER_NAME => assertion.user.as_ref().and_then(|u| {
|
||||
u.display_name
|
||||
.as_ref()
|
||||
.or(u.name.as_ref())
|
||||
.map(|n| n.as_bytes())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -744,6 +794,11 @@ pub unsafe extern "C" fn rust_ctap2_sign_result_item_copy(
|
|||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function needs to be called with an existing Ctap2SignResult
|
||||
/// and assertion_idx has to be a valid index for the assertion-list
|
||||
/// (to be determined with rust_ctap2_sign_result_item_len() )
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_ctap2_sign_result_contains_username(
|
||||
res: *const Ctap2SignResult,
|
||||
|
@ -790,8 +845,7 @@ pub unsafe extern "C" fn rust_ctap2_sign_result_username_len(
|
|||
if let Some(name_len) = assertions.0[assertion_idx]
|
||||
.user
|
||||
.as_ref()
|
||||
.map(|u| u.display_name.as_ref().or(u.name.as_ref()))
|
||||
.flatten()
|
||||
.and_then(|u| u.display_name.as_ref().or(u.name.as_ref()))
|
||||
.map(|x| x.as_bytes().len())
|
||||
{
|
||||
*len = name_len;
|
||||
|
@ -827,10 +881,8 @@ pub unsafe extern "C" fn rust_ctap2_sign_result_username_copy(
|
|||
if let Some(name) = assertions.0[assertion_idx]
|
||||
.user
|
||||
.as_ref()
|
||||
.map(|u| u.display_name.as_ref().or(u.name.as_ref()))
|
||||
.flatten()
|
||||
.map(|u| CString::new(u.clone()).ok())
|
||||
.flatten()
|
||||
.and_then(|u| u.display_name.as_ref().or(u.name.as_ref()))
|
||||
.and_then(|u| CString::new(u.clone()).ok())
|
||||
{
|
||||
ptr::copy_nonoverlapping(name.as_ptr(), dst, name.as_bytes().len());
|
||||
true
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![allow(clippy::bool_to_int_with_if)]
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
|
@ -84,11 +88,13 @@ pub struct KeyHandle {
|
|||
|
||||
pub type AppId = Vec<u8>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RegisterResult {
|
||||
CTAP1(Vec<u8>, u2ftypes::U2FDeviceInfo),
|
||||
CTAP2(AttestationObject, CollectedClientDataWrapper),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SignResult {
|
||||
CTAP1(AppId, Vec<u8>, Vec<u8>, u2ftypes::U2FDeviceInfo),
|
||||
CTAP2(AssertionObject, CollectedClientDataWrapper),
|
||||
|
|
|
@ -484,7 +484,7 @@ impl AuthenticatorTransport for Manager {
|
|||
if key_handle.credential.len() >= 256 {
|
||||
return Err(AuthenticatorError::InvalidRelyingPartyInput);
|
||||
}
|
||||
let rp = RelyingPartyWrapper::Hash(RpIdHash::from(&app_id)?);
|
||||
let rp = RelyingPartyWrapper::Hash(RpIdHash::from(app_id)?);
|
||||
|
||||
let allow_list = vec![key_handle.into()];
|
||||
|
||||
|
@ -518,7 +518,7 @@ impl AuthenticatorTransport for Manager {
|
|||
};
|
||||
|
||||
let get_assertion = GetAssertion::new(
|
||||
client_data.clone(),
|
||||
client_data,
|
||||
RelyingPartyWrapper::Data(RelyingParty {
|
||||
id: args.relying_party_id,
|
||||
name: None,
|
||||
|
|
|
@ -349,33 +349,30 @@ impl StateMachineCtap2 {
|
|||
.ok()?;
|
||||
|
||||
// Blocking recv. DeviceSelector will tell us what to do
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(DeviceCommand::Blink) => match dev.block_and_blink() {
|
||||
BlinkResult::DeviceSelected => {
|
||||
// User selected us. Let DeviceSelector know, so it can cancel all other
|
||||
// outstanding open blink-requests.
|
||||
selector
|
||||
.send(DeviceSelectorEvent::SelectedToken(dev.id()))
|
||||
.ok()?;
|
||||
break;
|
||||
}
|
||||
BlinkResult::Cancelled => {
|
||||
info!("Device {:?} was not selected", dev.id());
|
||||
return None;
|
||||
}
|
||||
},
|
||||
Ok(DeviceCommand::Removed) => {
|
||||
info!("Device {:?} was removed", dev.id());
|
||||
return None;
|
||||
}
|
||||
Ok(DeviceCommand::Continue) => {
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("Error when trying to receive messages from DeviceSelector! Exiting.");
|
||||
match rx.recv() {
|
||||
Ok(DeviceCommand::Blink) => match dev.block_and_blink() {
|
||||
BlinkResult::DeviceSelected => {
|
||||
// User selected us. Let DeviceSelector know, so it can cancel all other
|
||||
// outstanding open blink-requests.
|
||||
selector
|
||||
.send(DeviceSelectorEvent::SelectedToken(dev.id()))
|
||||
.ok()?;
|
||||
}
|
||||
BlinkResult::Cancelled => {
|
||||
info!("Device {:?} was not selected", dev.id());
|
||||
return None;
|
||||
}
|
||||
},
|
||||
Ok(DeviceCommand::Removed) => {
|
||||
info!("Device {:?} was removed", dev.id());
|
||||
return None;
|
||||
}
|
||||
Ok(DeviceCommand::Continue) => {
|
||||
// Just continue
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("Error when trying to receive messages from DeviceSelector! Exiting.");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(dev)
|
||||
|
@ -397,7 +394,7 @@ impl StateMachineCtap2 {
|
|||
// locked token). If it is deemed unrecoverable, we error out the 'normal' way with the same error.
|
||||
error!("Callback dropped the channel, so we forward the error to the results-callback: {:?}", error);
|
||||
callback.call(Err(AuthenticatorError::PinError(error)));
|
||||
return Err(());
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -529,7 +526,6 @@ impl StateMachineCtap2 {
|
|||
callback.call(Ok(RegisterResult::CTAP1(data, dev.get_device_info())))
|
||||
}
|
||||
|
||||
Err(HIDError::DeviceNotSupported) | Err(HIDError::UnsupportedCommand) => {}
|
||||
Err(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::ChannelBusy,
|
||||
_,
|
||||
|
@ -639,11 +635,6 @@ impl StateMachineCtap2 {
|
|||
Ok(GetAssertionResult::CTAP2(assertion, client_data)) => {
|
||||
callback.call(Ok(SignResult::CTAP2(assertion, client_data)))
|
||||
}
|
||||
// TODO(baloo): if key_handle is invalid for this device, it
|
||||
// should reply something like:
|
||||
// CTAP2_ERR_INVALID_CREDENTIAL
|
||||
// have to check
|
||||
Err(HIDError::DeviceNotSupported) | Err(HIDError::UnsupportedCommand) => {}
|
||||
Err(HIDError::Command(CommandError::StatusCode(
|
||||
StatusCode::ChannelBusy,
|
||||
_,
|
||||
|
|
|
@ -26,7 +26,7 @@ impl Serialize for StatusUpdate {
|
|||
S: serde::Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_struct("StatusUpdate", 1)?;
|
||||
match &*self {
|
||||
match self {
|
||||
StatusUpdate::DeviceAvailable { dev_info } => {
|
||||
map.serialize_field("DeviceAvailable", &dev_info)?
|
||||
}
|
||||
|
@ -53,8 +53,6 @@ pub(crate) fn send_status(status: &Sender<StatusUpdate>, msg: StatusUpdate) {
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::consts::U2F_AUTHENTICATE;
|
||||
|
||||
use super::*;
|
||||
use crate::consts::Capability;
|
||||
use serde_json::to_string;
|
||||
|
|
|
@ -12,13 +12,13 @@ pub type DeviceBuildParameters = <Device as HIDDevice>::BuildParameters;
|
|||
|
||||
trait DeviceSelectorEventMarker {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BlinkResult {
|
||||
DeviceSelected,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DeviceCommand {
|
||||
Blink,
|
||||
Continue,
|
||||
|
@ -44,6 +44,10 @@ pub struct DeviceSelector {
|
|||
}
|
||||
|
||||
impl DeviceSelector {
|
||||
// Devices are hashed according to their DeviceID (usually a path),
|
||||
// all other members (which can be mutable) are not used, but clippy
|
||||
// can't check this.
|
||||
#![allow(clippy::mutable_key_type)]
|
||||
pub fn run(status: Sender<crate::StatusUpdate>) -> Self {
|
||||
let (selector_send, selector_rec) = channel();
|
||||
// let new_device_callback = Arc::new(new_device_cb);
|
||||
|
@ -244,7 +248,7 @@ pub mod tests {
|
|||
},
|
||||
..Default::default()
|
||||
};
|
||||
dev.set_authenticator_info(info.clone());
|
||||
dev.set_authenticator_info(info);
|
||||
}
|
||||
|
||||
fn send_i_am_token(dev: &Device, selector: &DeviceSelector) {
|
||||
|
@ -335,6 +339,27 @@ pub mod tests {
|
|||
recv_status(&devices[2], &status_rx, ExpectedUpdate::DeviceSelected);
|
||||
}
|
||||
|
||||
// This test is mostly for testing stop() and clone_sender()
|
||||
#[test]
|
||||
fn test_device_selector_stop() {
|
||||
let device = Device::new("device selector 1").unwrap();
|
||||
|
||||
let (status_tx, _) = channel();
|
||||
let mut selector = DeviceSelector::run(status_tx);
|
||||
|
||||
// Adding all
|
||||
selector
|
||||
.clone_sender()
|
||||
.send(DeviceSelectorEvent::DevicesAdded(vec![device.id()]))
|
||||
.unwrap();
|
||||
|
||||
selector
|
||||
.clone_sender()
|
||||
.send(DeviceSelectorEvent::NotAToken(device.id()))
|
||||
.unwrap();
|
||||
selector.stop();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_device_selector_all_pins_with_late_add() {
|
||||
let mut devices = vec![
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Device {
|
|||
buf[6] = 0;
|
||||
buf[7] = 1; // one byte
|
||||
|
||||
self.write(&buf[..])?;
|
||||
self.write_all(&buf)?;
|
||||
|
||||
// Wait for response
|
||||
let mut pfd: libc::pollfd = unsafe { mem::zeroed() };
|
||||
|
@ -57,7 +57,7 @@ impl Device {
|
|||
}
|
||||
|
||||
// Read response
|
||||
self.read(&mut buf[..])?;
|
||||
self.read_exact(&mut buf)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ impl HIDDevice for Device {
|
|||
if !uhid::is_u2f_device(self.fd) {
|
||||
return false;
|
||||
}
|
||||
if let Err(_) = self.ping() {
|
||||
if self.ping().is_err() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
|
|
|
@ -9,8 +9,6 @@ use std::collections::HashMap;
|
|||
use std::error::Error;
|
||||
use std::ffi::OsString;
|
||||
use std::sync::{mpsc::Sender, Arc};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{fs, io};
|
||||
|
||||
const POLL_TIMEOUT: usize = 100;
|
||||
|
@ -78,15 +76,13 @@ where
|
|||
|
||||
let mut initial_devs = Vec::new();
|
||||
// Iterate all existing devices.
|
||||
for dev in fs::read_dir("/dev")? {
|
||||
if let Ok(dev) = dev {
|
||||
let filename_ = dev.file_name();
|
||||
let filename = filename_.to_str().unwrap_or("");
|
||||
if filename.starts_with("uhid") {
|
||||
let path = OsString::from("/dev/".to_owned() + filename);
|
||||
initial_devs.push(path.clone());
|
||||
self.add_device(path);
|
||||
}
|
||||
for dev in (fs::read_dir("/dev")?).flatten() {
|
||||
let filename_ = dev.file_name();
|
||||
let filename = filename_.to_str().unwrap_or("");
|
||||
if filename.starts_with("uhid") {
|
||||
let path = OsString::from("/dev/".to_owned() + filename);
|
||||
initial_devs.push(path.clone());
|
||||
self.add_device(path);
|
||||
}
|
||||
}
|
||||
let _ = self
|
||||
|
|
|
@ -75,10 +75,7 @@ fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
|
|||
let mut desc = GenDescriptor::default();
|
||||
let _ = unsafe { usb_get_report_desc(fd, &mut desc)? };
|
||||
desc.ugd_maxlen = desc.ugd_actlen;
|
||||
let mut value = Vec::with_capacity(desc.ugd_actlen as usize);
|
||||
unsafe {
|
||||
value.set_len(desc.ugd_actlen as usize);
|
||||
}
|
||||
let mut value = vec![0; desc.ugd_actlen as usize];
|
||||
desc.ugd_data = value.as_mut_ptr();
|
||||
let _ = unsafe { usb_get_report_desc(fd, &mut desc)? };
|
||||
Ok(ReportDescriptor { value })
|
||||
|
|
|
@ -9,10 +9,13 @@
|
|||
allow(clippy::cast_lossless, clippy::needless_lifetimes)
|
||||
)]
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use crate::consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID, INIT_HEADER_SIZE, MAX_HID_RPT_SIZE};
|
||||
use crate::consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
use crate::consts::{INIT_HEADER_SIZE, MAX_HID_RPT_SIZE};
|
||||
|
||||
// The 4 MSBs (the tag) are set when it's a long item.
|
||||
const HID_MASK_LONG_ITEM_TAG: u8 = 0b1111_0000;
|
||||
|
@ -178,6 +181,7 @@ pub fn has_fido_usage(desc: ReportDescriptor) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
pub fn read_hid_rpt_sizes(desc: ReportDescriptor) -> io::Result<(usize, usize)> {
|
||||
let mut in_rpt_count = None;
|
||||
let mut out_rpt_count = None;
|
||||
|
@ -186,7 +190,7 @@ pub fn read_hid_rpt_sizes(desc: ReportDescriptor) -> io::Result<(usize, usize)>
|
|||
for data in desc.iter() {
|
||||
match data {
|
||||
Data::ReportCount { data } => {
|
||||
if last_rpt_count != None {
|
||||
if last_rpt_count.is_some() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Duplicate HID_ReportCount",
|
||||
|
|
|
@ -125,7 +125,7 @@ impl HIDDevice for Device {
|
|||
info!("new device {:?}", res.path);
|
||||
Ok(res)
|
||||
} else {
|
||||
Err((HIDError::DeviceNotSupported, res.path.clone()))
|
||||
Err((HIDError::DeviceNotSupported, res.path))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const POLL_TIMEOUT: c_int = 100;
|
|||
fn poll(fds: &mut Vec<::libc::pollfd>) -> io::Result<()> {
|
||||
let nfds = fds.len() as c_ulong;
|
||||
|
||||
let rv = unsafe { ::libc::poll((&mut fds[..]).as_mut_ptr(), nfds, POLL_TIMEOUT) };
|
||||
let rv = unsafe { ::libc::poll((fds[..]).as_mut_ptr(), nfds, POLL_TIMEOUT) };
|
||||
|
||||
if rv < 0 {
|
||||
Err(io::Error::from_raw_os_error(rv))
|
||||
|
|
|
@ -105,7 +105,7 @@ impl Hash for Device {
|
|||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
|
||||
fn get_cid(&self) -> &[u8; 4] {
|
||||
&self.cid
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ pub enum Nonce {
|
|||
// TODO(MS): This is the lazy way: FidoDevice currently only extends HIDDevice by more functions,
|
||||
// but the goal is to remove U2FDevice entirely and copy over the trait-definition here
|
||||
pub trait FidoDevice: HIDDevice {
|
||||
fn send_msg<'msg, Out, Req: Request<Out>>(&mut self, msg: &'msg Req) -> Result<Out, HIDError> {
|
||||
fn send_msg<Out, Req: Request<Out>>(&mut self, msg: &Req) -> Result<Out, HIDError> {
|
||||
if !self.initialized() {
|
||||
return Err(HIDError::DeviceNotInitialized);
|
||||
}
|
||||
|
@ -87,10 +87,7 @@ pub trait FidoDevice: HIDDevice {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_cbor<'msg, Req: RequestCtap2>(
|
||||
&mut self,
|
||||
msg: &'msg Req,
|
||||
) -> Result<Req::Output, HIDError> {
|
||||
fn send_cbor<Req: RequestCtap2>(&mut self, msg: &Req) -> Result<Req::Output, HIDError> {
|
||||
debug!("sending {:?} to {:?}", msg, self);
|
||||
|
||||
let mut data = msg.wire_format(self)?;
|
||||
|
@ -115,12 +112,9 @@ pub trait FidoDevice: HIDDevice {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_ctap1<'msg, Req: RequestCtap1>(
|
||||
&mut self,
|
||||
msg: &'msg Req,
|
||||
) -> Result<Req::Output, HIDError> {
|
||||
fn send_ctap1<Req: RequestCtap1>(&mut self, msg: &Req) -> Result<Req::Output, HIDError> {
|
||||
debug!("sending {:?} to {:?}", msg, self);
|
||||
let data = msg.ctap1_format(self)?;
|
||||
let (data, add_info) = msg.ctap1_format(self)?;
|
||||
|
||||
loop {
|
||||
let (cmd, mut data) = self.sendrecv(HIDCmd::Msg, &data)?;
|
||||
|
@ -139,7 +133,7 @@ pub trait FidoDevice: HIDDevice {
|
|||
// This will bubble up error if status != no error
|
||||
let status = ApduErrorStatus::from([status[0], status[1]]);
|
||||
|
||||
match msg.handle_response_ctap1(status, &data) {
|
||||
match msg.handle_response_ctap1(status, &data, &add_info) {
|
||||
Ok(out) => return Ok(out),
|
||||
Err(Retryable::Retry) => {
|
||||
// sleep 100ms then loop again
|
||||
|
@ -156,7 +150,7 @@ pub trait FidoDevice: HIDDevice {
|
|||
|
||||
// This is ugly as we have 2 init-functions now, but the fastest way currently.
|
||||
fn init(&mut self, nonce: Nonce) -> Result<(), HIDError> {
|
||||
let resp = <Self as HIDDevice>::initialize(self, nonce)?;
|
||||
<Self as HIDDevice>::initialize(self, nonce)?;
|
||||
// TODO(baloo): this logic should be moved to
|
||||
// transport/mod.rs::Device trait
|
||||
if self.supports_ctap2() {
|
||||
|
@ -171,17 +165,16 @@ pub trait FidoDevice: HIDDevice {
|
|||
// We don't really use the result here
|
||||
self.send_ctap1(&command)?;
|
||||
}
|
||||
Ok(resp)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn block_and_blink(&mut self) -> BlinkResult {
|
||||
let resp;
|
||||
let supports_select_cmd = self
|
||||
.get_authenticator_info()
|
||||
.map_or(false, |i| i.versions.contains(&String::from("FIDO_2_1")));
|
||||
if supports_select_cmd {
|
||||
let resp = if supports_select_cmd {
|
||||
let msg = Selection {};
|
||||
resp = self.send_cbor(&msg);
|
||||
self.send_cbor(&msg)
|
||||
} else {
|
||||
// We need to fake a blink-request, because FIDO2.0 forgot to specify one
|
||||
// See: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential
|
||||
|
@ -197,8 +190,9 @@ pub trait FidoDevice: HIDDevice {
|
|||
msg.set_pin_auth(Some(PinAuth::empty_pin_auth()), None);
|
||||
info!("Trying to blink: {:?}", &msg);
|
||||
// We don't care about the Ok-value, just if it is Ok or not
|
||||
resp = self.send_msg(&msg).map(|_| ());
|
||||
}
|
||||
self.send_msg(&msg).map(|_| ())
|
||||
};
|
||||
|
||||
match resp {
|
||||
// Spec only says PinInvalid or PinNotSet should be returned on the fake touch-request,
|
||||
// but Yubikeys for example return PinAuthInvalid. A successful return is also possible
|
||||
|
|
|
@ -40,7 +40,7 @@ impl Device {
|
|||
buf[6] = 0;
|
||||
buf[7] = 1; // one byte
|
||||
|
||||
self.write(&buf[..])?;
|
||||
self.write_all(&buf)?;
|
||||
|
||||
// Wait for response
|
||||
let mut pfd: libc::pollfd = unsafe { mem::zeroed() };
|
||||
|
@ -56,7 +56,7 @@ impl Device {
|
|||
}
|
||||
|
||||
// Read response
|
||||
self.read(&mut buf[..])?;
|
||||
self.read_exact(&mut buf)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ impl HIDDevice for Device {
|
|||
Ok(_) => (),
|
||||
Err(_) => return false,
|
||||
}
|
||||
if let Err(_) = self.ping() {
|
||||
if self.ping().is_err() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
|
|
|
@ -6,12 +6,12 @@ use crate::transport::hid::HIDDevice;
|
|||
use crate::transport::FidoDevice;
|
||||
use crate::transport::{AuthenticatorInfo, ECDHSecret, HIDError};
|
||||
use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::hash::Hash;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Device {}
|
||||
|
||||
impl Read for Device {
|
||||
|
@ -31,7 +31,7 @@ impl Write for Device {
|
|||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
|
||||
fn get_cid(&self) -> &[u8; 4] {
|
||||
panic!("not implemented");
|
||||
}
|
||||
|
||||
|
@ -101,10 +101,4 @@ impl HIDDevice for Device {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for Device {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl FidoDevice for Device {}
|
||||
|
|
|
@ -44,7 +44,7 @@ impl Read for Device {
|
|||
let mut input = [0u8; MAX_HID_RPT_SIZE + 1];
|
||||
let _ = self.file.read(&mut input)?;
|
||||
bytes.clone_from_slice(&input[1..]);
|
||||
Ok(bytes.len() as usize)
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ impl HIDDevice for Device {
|
|||
info!("new device {:?}", res.path);
|
||||
Ok(res)
|
||||
} else {
|
||||
Err((HIDError::DeviceNotSupported, res.path.clone()))
|
||||
Err((HIDError::DeviceNotSupported, res.path))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::transport::device_selector::DeviceSelectorEvent;
|
||||
use crate::transport::device_selector::{DeviceID, DeviceSelectorEvent};
|
||||
use crate::transport::platform::winapi::DeviceInfoSet;
|
||||
use runloop::RunLoop;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -76,7 +76,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn add_device(&mut self, path: &String) {
|
||||
fn add_device(&mut self, path: &DeviceID) {
|
||||
let f = self.new_device_cb.clone();
|
||||
let path = path.clone();
|
||||
let key = path.clone();
|
||||
|
@ -95,7 +95,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn remove_device(&mut self, path: &String) {
|
||||
fn remove_device(&mut self, path: &DeviceID) {
|
||||
let _ = self
|
||||
.selector_sender
|
||||
.send(DeviceSelectorEvent::DeviceRemoved(path.clone()));
|
||||
|
|
|
@ -157,12 +157,7 @@ impl<'a> Iterator for DeviceInfoSetIter<'a> {
|
|||
return None; // An error occurred.
|
||||
}
|
||||
|
||||
let detail = DeviceInterfaceDetailData::new(required_size as usize);
|
||||
if detail.is_none() {
|
||||
return None; // malloc() failed.
|
||||
}
|
||||
|
||||
let detail = detail.unwrap();
|
||||
let detail = DeviceInterfaceDetailData::new(required_size as usize)?;
|
||||
let rv = unsafe {
|
||||
SetupDiGetDeviceInterfaceDetailW(
|
||||
self.set.get(),
|
||||
|
|
|
@ -282,7 +282,7 @@ pub(crate) mod tests {
|
|||
let mut cid = [0u8; 4];
|
||||
thread_rng().fill_bytes(&mut cid);
|
||||
|
||||
device.set_cid(cid.clone());
|
||||
device.set_cid(cid);
|
||||
|
||||
let info = U2FDeviceInfo {
|
||||
vendor_name: Vec::new(),
|
||||
|
@ -297,15 +297,15 @@ pub(crate) mod tests {
|
|||
|
||||
// ctap1.0 U2F_VERSION request
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x7]); // cmd + bcnt
|
||||
msg.extend(&[0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0]);
|
||||
msg.extend([HIDCmd::Msg.into(), 0x0, 0x7]); // cmd + bcnt
|
||||
msg.extend([0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0]);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
// fido response
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(&[HIDCmd::Msg.into(), 0x0, 0x08]); // cmd + bcnt
|
||||
msg.extend(&[0x55, 0x32, 0x46, 0x5f, 0x56, 0x32]); // 'U2F_V2'
|
||||
msg.extend(&SW_NO_ERROR);
|
||||
msg.extend([HIDCmd::Msg.into(), 0x0, 0x08]); // cmd + bcnt
|
||||
msg.extend([0x55, 0x32, 0x46, 0x5f, 0x56, 0x32]); // 'U2F_V2'
|
||||
msg.extend(SW_NO_ERROR);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let res = is_v2_device(&mut device).expect("Failed to get version");
|
||||
|
|
|
@ -33,7 +33,7 @@ impl Signed for usize {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
#[cfg(all(target_os = "linux", not(test)))]
|
||||
pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
||||
if rv.is_negative() {
|
||||
let errno = unsafe { *libc::__errno_location() };
|
||||
|
@ -43,7 +43,7 @@ pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[cfg(all(target_os = "freebsd", not(test)))]
|
||||
pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
||||
if rv.is_negative() {
|
||||
let errno = unsafe { *libc::__error() };
|
||||
|
@ -53,7 +53,7 @@ pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "openbsd"))]
|
||||
#[cfg(all(target_os = "openbsd", not(test)))]
|
||||
pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
||||
if rv.is_negative() {
|
||||
Err(io::Error::last_os_error())
|
||||
|
@ -66,7 +66,7 @@ pub fn io_err(msg: &str) -> io::Error {
|
|||
io::Error::new(io::ErrorKind::Other, msg)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(feature = "crypto_dummy")))]
|
||||
pub fn decode_hex(s: &str) -> Vec<u8> {
|
||||
(0..s.len())
|
||||
.step_by(2)
|
||||
|
|
|
@ -39,7 +39,7 @@ tokio-reactor = { version = "=0.1.3", optional = true }
|
|||
# audioipc2-client and audioipc2-server.
|
||||
tokio-threadpool = { version = "=0.1.17", optional = true }
|
||||
encoding_glue = { path = "../../../../intl/encoding_glue" }
|
||||
authenticator = { version = "0.4.0-alpha.6", features = ["gecko"] }
|
||||
authenticator = { version = "0.4.0-alpha.7", features = ["gecko"] }
|
||||
gkrust_utils = { path = "../../../../xpcom/rust/gkrust_utils" }
|
||||
gecko_logger = { path = "../../../../xpcom/rust/gecko_logger" }
|
||||
rsdparsa_capi = { path = "../../../../dom/media/webrtc/sdp/rsdparsa_capi" }
|
||||
|
|
Загрузка…
Ссылка в новой задаче