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:
John Schanck 2023-01-27 22:52:30 +00:00
Родитель 9f7926a306
Коммит 1c2bfe5620
55 изменённых файлов: 477 добавлений и 426 удалений

4
Cargo.lock сгенерированный
Просмотреть файл

@ -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]]

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
third_party/rust/authenticator/Cargo.lock сгенерированный поставляемый
Просмотреть файл

@ -39,7 +39,7 @@ dependencies = [
[[package]]
name = "authenticator"
version = "0.4.0-alpha.6"
version = "0.4.0-alpha.7"
dependencies = [
"assert_matches",
"base64",

2
third_party/rust/authenticator/Cargo.toml поставляемый
Просмотреть файл

@ -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>",

14
third_party/rust/authenticator/build.rs поставляемый
Просмотреть файл

@ -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(&register_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

4
third_party/rust/authenticator/src/capi.rs поставляемый
Просмотреть файл

@ -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, &register_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

6
third_party/rust/authenticator/src/lib.rs поставляемый
Просмотреть файл

@ -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");

8
third_party/rust/authenticator/src/util.rs поставляемый
Просмотреть файл

@ -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" }