From cb1b6afb63bb4e441fe1c36ae151b88f66c3b3f5 Mon Sep 17 00:00:00 2001 From: John Schanck Date: Thu, 21 Sep 2023 16:07:45 +0000 Subject: [PATCH] Bug 1853711 - vendor authenticator-rs v0.4.0-alpha.22. r=keeler,supply-chain-reviewers Differential Revision: https://phabricator.services.mozilla.com/D188766 --- Cargo.lock | 4 +- dom/webauthn/authrs_bridge/Cargo.toml | 2 +- dom/webauthn/authrs_bridge/src/lib.rs | 40 +- dom/webauthn/authrs_bridge/src/test_token.rs | 39 +- supply-chain/imports.lock | 7 + .../rust/authenticator/.cargo-checksum.json | 2 +- third_party/rust/authenticator/Cargo.lock | 2 +- third_party/rust/authenticator/Cargo.toml | 2 +- .../rust/authenticator/examples/ctap2.rs | 10 +- .../examples/ctap2_discoverable_creds.rs | 64 ++- .../examples/interactive_management.rs | 3 + .../rust/authenticator/examples/set_pin.rs | 3 + .../examples/test_exclude_list.rs | 10 +- .../authenticator/src/authenticatorservice.rs | 15 +- .../rust/authenticator/src/crypto/der.rs | 185 +++++++++ .../rust/authenticator/src/crypto/mod.rs | 377 ++++++++++++------ .../rust/authenticator/src/crypto/nss.rs | 227 +++-------- .../ctap2/commands/credential_management.rs | 11 +- .../src/ctap2/commands/get_assertion.rs | 112 +++--- .../src/ctap2/commands/make_credentials.rs | 16 +- .../rust/authenticator/src/ctap2/mod.rs | 45 ++- .../rust/authenticator/src/ctap2/preflight.rs | 7 +- .../rust/authenticator/src/ctap2/server.rs | 21 +- .../rust/authenticator/src/status_update.rs | 6 +- .../rust/authenticator/src/transport/mod.rs | 2 +- 25 files changed, 755 insertions(+), 457 deletions(-) create mode 100644 third_party/rust/authenticator/src/crypto/der.rs diff --git a/Cargo.lock b/Cargo.lock index 53226c3e40a3..a07bbe9b2545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "authenticator" -version = "0.4.0-alpha.21" +version = "0.4.0-alpha.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0e925f7f169517290d746d6080fccc3d49605eb7ea8f0394fc3fbd6e2c31ab3" +checksum = "2a46aebc9de6a88556552bc40e5ffbd479705cc0ab46fee0dec476dc2886eb13" dependencies = [ "base64 0.21.3", "bitflags 1.3.2", diff --git a/dom/webauthn/authrs_bridge/Cargo.toml b/dom/webauthn/authrs_bridge/Cargo.toml index 0aae876ebe9a..d99caf5e3337 100644 --- a/dom/webauthn/authrs_bridge/Cargo.toml +++ b/dom/webauthn/authrs_bridge/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" authors = ["Martin Sirringhaus", "John Schanck"] [dependencies] -authenticator = { version = "0.4.0-alpha.21", features = ["gecko"] } +authenticator = { version = "0.4.0-alpha.22", features = ["gecko"] } base64 = "^0.21" log = "0.4" moz_task = { path = "../../../xpcom/rust/moz_task" } diff --git a/dom/webauthn/authrs_bridge/src/lib.rs b/dom/webauthn/authrs_bridge/src/lib.rs index 31e3b804ea8b..52a25b188757 100644 --- a/dom/webauthn/authrs_bridge/src/lib.rs +++ b/dom/webauthn/authrs_bridge/src/lib.rs @@ -10,13 +10,12 @@ extern crate xpcom; use authenticator::{ authenticatorservice::{RegisterArgs, SignArgs}, - crypto::COSEKeyType, ctap2::attestation::AttestationObject, ctap2::commands::get_info::AuthenticatorVersion, ctap2::server::{ AuthenticationExtensionsClientInputs, PublicKeyCredentialDescriptor, - PublicKeyCredentialParameters, RelyingParty, ResidentKeyRequirement, User, - UserVerificationRequirement, + PublicKeyCredentialParameters, PublicKeyCredentialUserEntity, RelyingParty, + ResidentKeyRequirement, UserVerificationRequirement, }, errors::{AuthenticatorError, PinError, U2FTokenError}, statecallback::StateCallback, @@ -171,11 +170,11 @@ impl WebAuthnAttObj { let Some(credential_data) = &self.att_obj.auth_data.credential_data else { return Err(NS_ERROR_FAILURE); }; - // We only support encoding (some) EC2 keys in DER SPKI format. - let COSEKeyType::EC2(ref key) = credential_data.credential_public_key.key else { - return Err(NS_ERROR_NOT_AVAILABLE); - }; - Ok(key.der_spki().or(Err(NS_ERROR_NOT_AVAILABLE))?.into()) + Ok(credential_data + .credential_public_key + .der_spki() + .or(Err(NS_ERROR_NOT_AVAILABLE))? + .into()) } xpcom_method!(get_public_key_algorithm => GetPublicKeyAlgorithm() -> i32); @@ -333,14 +332,12 @@ impl Controller { .query_interface::(), ), Ok(result) => { - for assertion in result.assertions { - assertions.push( - CtapSignResult::allocate(InitCtapSignResult { - result: Ok(assertion), - }) - .query_interface::(), - ); - } + assertions.push( + CtapSignResult::allocate(InitCtapSignResult { + result: Ok(result.assertion), + }) + .query_interface::(), + ); } } @@ -440,6 +437,9 @@ fn status_callback( Ok(StatusUpdate::InteractiveManagement(_)) => { debug!("STATUS: interactive management"); } + Ok(StatusUpdate::SelectResultNotice(_, _)) => { + // The selection prompt will be added in Bug 1854016 + } Err(RecvError) => { debug!("STATUS: end"); return; @@ -605,7 +605,7 @@ impl AuthrsTransport { name: None, }, origin: origin.to_string(), - user: User { + user: PublicKeyCredentialUserEntity { id: user_id.to_vec(), name: Some(user_name.to_string()), display_name: None, @@ -783,10 +783,8 @@ impl AuthrsTransport { // In CTAP 2.0, but not CTAP 2.1, the assertion object's credential field // "May be omitted if the allowList has exactly one credential." If we had // a unique allowed credential, then copy its descriptor to the output. - if let Ok(Some(assertion)) = - result.as_mut().map(|result| result.assertions.first_mut()) - { - assertion.credentials = uniq_allowed_cred; + if let Ok(inner) = result.as_mut() { + inner.assertion.credentials = uniq_allowed_cred; } } let _ = controller.finish_sign(tid, result); diff --git a/dom/webauthn/authrs_bridge/src/test_token.rs b/dom/webauthn/authrs_bridge/src/test_token.rs index 7584373bb871..8b2c8990363f 100644 --- a/dom/webauthn/authrs_bridge/src/test_token.rs +++ b/dom/webauthn/authrs_bridge/src/test_token.rs @@ -12,7 +12,7 @@ use authenticator::ctap2::{ client_data::ClientDataHash, commands::{ client_pin::{ClientPIN, ClientPinResponse, PINSubcommand}, - get_assertion::{Assertion, GetAssertion, GetAssertionResponse, GetAssertionResult}, + get_assertion::{GetAssertion, GetAssertionResponse, GetAssertionResult}, get_info::{AuthenticatorInfo, AuthenticatorOptions, AuthenticatorVersion}, get_version::{GetVersion, U2FInfo}, make_credentials::{MakeCredentials, MakeCredentialsResult}, @@ -21,7 +21,10 @@ use authenticator::ctap2::{ RequestCtap1, RequestCtap2, StatusCode, }, preflight::CheckKeyHandle, - server::{PublicKeyCredentialDescriptor, RelyingParty, RelyingPartyWrapper, User}, + server::{ + PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, RelyingParty, + RelyingPartyWrapper, + }, }; use authenticator::errors::{AuthenticatorError, CommandError, HIDError, U2FTokenError}; use authenticator::{ctap2, statecallback::StateCallback}; @@ -75,7 +78,7 @@ impl TestTokenCredential { extensions: Extension::default(), }; - let user = Some(User { + let user = Some(PublicKeyCredentialUserEntity { id: self.user_handle.clone(), ..Default::default() }); @@ -311,7 +314,7 @@ impl VirtualFidoDevice for TestToken { } } - fn get_assertion(&self, req: &GetAssertion) -> Result { + fn get_assertion(&self, req: &GetAssertion) -> Result, HIDError> { // Algorithm 6.2.2 from CTAP 2.1 // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-makeCred-authnr-alg @@ -368,34 +371,40 @@ impl VirtualFidoDevice for TestToken { // 10. Extensions // (not implemented) - let mut assertions: Vec = vec![]; + let mut assertions: Vec = vec![]; if !req.allow_list.is_empty() { // 11. Non-discoverable credential case // return at most one assertion matching an allowed credential ID for credential in eligible_cred_iter { if req.allow_list.iter().any(|x| x.id == credential.id) { let assertion = credential.assert(&req.client_data_hash, flags)?.into(); - assertions.push(assertion); + assertions.push(GetAssertionResult { + assertion, + extensions: Default::default(), + }); break; } } } else { // 12. Discoverable credential case // return any number of assertions from credentials bound to this RP ID - // TODO(Bug 1838932) Until we have conditional mediation we actually don't want to - // return a list of credentials here. The UI to select one of the results blocks - // testing. for credential in eligible_cred_iter.filter(|x| x.is_discoverable_credential) { let assertion = credential.assert(&req.client_data_hash, flags)?.into(); - assertions.push(assertion); - break; + assertions.push(GetAssertionResult { + assertion, + extensions: Default::default(), + }); } } - Ok(GetAssertionResult { - assertions, - extensions: Default::default(), - }) + if assertions.is_empty() { + return Err(HIDError::Command(CommandError::StatusCode( + StatusCode::NoCredentials, + None, + ))); + } + + Ok(assertions) } fn get_info(&self) -> Result { diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 90cfb7c85285..b06c5e2260f6 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -50,6 +50,13 @@ user-id = 175410 user-login = "jschanck" user-name = "John Schanck" +[[publisher.authenticator]] +version = "0.4.0-alpha.22" +when = "2023-09-19" +user-id = 175410 +user-login = "jschanck" +user-name = "John Schanck" + [[publisher.bhttp]] version = "0.3.1" when = "2023-02-23" diff --git a/third_party/rust/authenticator/.cargo-checksum.json b/third_party/rust/authenticator/.cargo-checksum.json index 70e1c7b84a18..5e704b1ed3a4 100644 --- a/third_party/rust/authenticator/.cargo-checksum.json +++ b/third_party/rust/authenticator/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"a561ae22a5ef852e77de3a33a41080c4518026db0c2d96baf07d0bdd626073c8","Cargo.toml":"6141740f4281018fc73e58b7cd4f54ad795c76de00e076ffa8bca681d25ae808","Cross.toml":"8d132da818d48492aa9f4b78a348f0df3adfae45d988d42ebd6be8a5adadb6c3","LICENSE":"e866c8f5864d4cacfe403820e722e9dc03fe3c7565efa5e4dad9051d827bb92a","README.md":"c87d9c7cc44f1dd4ef861a3a9f8cd2eb68aedd3814768871f5fb63c2070806cd","build.rs":"5b909f42e52ed2056afa3693544ef1c1dc5e90d00e7d8730175a228bd0233b43","examples/ctap2.rs":"a78e4956faee775e9058a292bffaa48034b53da7d0f97ff4d4f1d44a43deb188","examples/ctap2_discoverable_creds.rs":"3e4a6b33e1ff0f17499b7133ef440fbd8c786fffff674ce55d12f45a1a1fea65","examples/interactive_management.rs":"dc4433791abdfcbd5ab127b42323f5b6e0b17fb98b07152f461961072496e62e","examples/reset.rs":"a8ffd75b248beeede129698f2c7bc971789fda0e6e8f66ac76cc2f8ae770432d","examples/set_pin.rs":"9fb2dac0e07c6b4b54aaeebd4ef6c31dc15a2451b9370ca612dbd50ef2531355","examples/test_exclude_list.rs":"c5a176aa0c5a005ba3881cfa28c5890b5f31e01fa1e2557305b06b3b5f0ca70b","rustfmt.toml":"ceb6615363d6fff16426eb56f5727f98a7f7ed459ba9af735b1d8b672e2c3b9b","src/authenticatorservice.rs":"979fc19a6057c08d10b0aaf6eb8a2df3165a8745651b768672b36170b94488a6","src/consts.rs":"44fb7c396dc87d1657d1feed08e956fc70608c0b06a034716b626419b442bcfe","src/crypto/dummy.rs":"92e5238da8e6f57bae057f564f5c84719dab6ed22c1b390884fe1a8bccb91f17","src/crypto/mod.rs":"a00209df00deab452b472a23429613b203d815b76284f143eb29b39d207fcf29","src/crypto/nss.rs":"f35c6b60754b06a8126e14ca54babbb376fc236dd840f07908011b49e569dfb1","src/crypto/openssl.rs":"b2577be577b884b569a5bc039b82e0402db097b5652d70d6320922ad6795f1c7","src/ctap2/attestation.rs":"ae55b012de7ee06abf6744283ac60039f9f22150beddb1ea89f5693cf0ef50d2","src/ctap2/client_data.rs":"447c970c9d079431a3cab0bf3b0d68f1848966ffebe77f6f08b3e7a865073786","src/ctap2/commands/authenticator_config.rs":"920822bc7dbfbe4dd3429978d24c15091d829e6b69f46cb7347bb7140518af27","src/ctap2/commands/bio_enrollment.rs":"1f3096f1b7e7a245199ff844840764c643a573bc3b7818a003b6bf693c3e829f","src/ctap2/commands/client_pin.rs":"13e73ad4ce11ba77587a33efade6ea80bbc397f40c1d240e056920ccafb2d56c","src/ctap2/commands/credential_management.rs":"77a28979b762777f275e5f165aecbe14355874431773bcff3e2df03eb0082bab","src/ctap2/commands/get_assertion.rs":"fa4e38fd3fb2d6248685a37e0ce6b13086b5b0da945ae8e83e401b39da91ef03","src/ctap2/commands/get_info.rs":"9c81bf95d397a11fba2077b88c00c50ce682243728bf21ea9bc4dd0646ddca0d","src/ctap2/commands/get_next_assertion.rs":"fb0edd201d90f5a706edf58cdea901f8783f882968a028b466892d0a38d10ffe","src/ctap2/commands/get_version.rs":"643a63066d9752f3b1d313b45aea1b20fadde8b6f58736ff6d8aa06665cca390","src/ctap2/commands/make_credentials.rs":"d8dfee88a8271eccdc40a71d72890ad8ab408b43c18c56be1ba01582ff7e55d5","src/ctap2/commands/mod.rs":"3360906596ea857e228fffd4cf5f9ff937d46edba4669244c67bb202a45e4931","src/ctap2/commands/reset.rs":"610a1979d20e801cb2ac4a6efe15b40699d30a70ba8c3f834c0066da10af3637","src/ctap2/commands/selection.rs":"dd7d21bd063fd618a53fd64a4e88e41e9344f335dfbcec25f8a886b6c4da8e0c","src/ctap2/mod.rs":"16727b1e3c8ed3d836bb5a454f4f08629297c8deed71b471280c0474dc04c984","src/ctap2/preflight.rs":"117817c5a6f0ecd8b412576855eb596385901023ddb616d8c38b802ffff96e93","src/ctap2/server.rs":"2ae2fe5f4e6cddf6e5720c46c84436813a1b81ccc177259643c2f7f2b4b99817","src/ctap2/utils.rs":"7ca56ae241f22de67047d22c6650bbe36f1268ab64bf8cc9256d2f00ef750c0a","src/errors.rs":"0639d55735b5b67562b4ff8b6b6639d1449f56cdcb0a0cdab3209d1bc972cab7","src/lib.rs":"00f2bfd489f77d9f10711983d742148a037e5989d02a44a65ae0fd3cbbd34dc0","src/manager.rs":"b7106c82c62c8bb47d3ec979f3454e0fb04dae0b32d029624b23a880819b16a7","src/statecallback.rs":"6748b74341876d7698aa3b1a6c1de002a74e201375040397255445ff4a1d7982","src/statemachine.rs":"412747465209ac080e941dae7cfdd808709803305977cfc08f349a8d2cfc61b0","src/status_update.rs":"dd4474343481f8e89e410a293018c0291e9cecc884a2636d6f37c220bfadc88f","src/transport/device_selector.rs":"da6de0f43d9b794e58afa738c2b756d9139968c5587f7a51aa84c7442762735c","src/transport/errors.rs":"5af7cb8d22ffa63bf4264d182a0f54b9b3a2cc9d19d832b3495857229f9a2875","src/transport/freebsd/device.rs":"0aa53590382093225b6f17af4deb3224eb52a883d19dc7da520fcbfceb1cad58","src/transport/freebsd/mod.rs":"42dcb57fbeb00140003a8ad39acac9b547062b8f281a3fa5deb5f92a6169dde6","src/transport/freebsd/monitor.rs":"a6b34af4dd2e357a5775b1f3a723766107c11ef98dba859b1188ed08e0e450a2","src/transport/freebsd/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/freebsd/uhid.rs":"a194416a8bc5d428c337f8d96a2248769ca190810852bbe5ee686ab595d8eb4c","src/transport/hid.rs":"9a578446e1e0d5dff7ba666e338b0febcad3a66fb8cf7ab41f58500531a7992a","src/transport/hidproto.rs":"1f36992a806f753bac6582c2263d5cc1dd2924df52af97370e55dd6c189ef545","src/transport/linux/device.rs":"206a5ae404590bc73acc03e22823ea4252848b5afab744a51f9630b0f1af813c","src/transport/linux/hidraw.rs":"c7a0df9b4e51cb2736218ffffa02b2b2547b7c515d69f9bae2c9a8c8f1cb547b","src/transport/linux/hidwrapper.h":"72785db3a9b27ea72b6cf13a958fee032af54304522d002f56322473978a20f9","src/transport/linux/hidwrapper.rs":"d203e8804e7632b8d47a224c186d1f431800f04ddc43360d5c086f71e9b0f674","src/transport/linux/ioctl_aarch64le.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_armle.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_loongarch64.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_mips64le.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_mipsbe.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_mipsle.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_powerpc64be.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_powerpc64le.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_powerpcbe.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_riscv64.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_s390xbe.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_x86.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_x86_64.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/mod.rs":"446e435126d2a58f167f648dd95cba28e8ac9c17f1f799e1eaeab80ea800fc57","src/transport/linux/monitor.rs":"5e3ec2618dd74027ae6ca1527991254e3271cce59106d4920ce0414094e22f64","src/transport/linux/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/macos/device.rs":"cef7cec681d9c777aac16e662bcbe8ff0d39efb2116086bf9f792945f1454c96","src/transport/macos/iokit.rs":"7dc4e7bbf8e42e2fcde0cee8e48d14d6234a5a910bd5d3c4e966d8ba6b73992f","src/transport/macos/mod.rs":"333e561554fc901d4f6092f6e4c85823e2b0c4ff31c9188d0e6d542b71a0a07c","src/transport/macos/monitor.rs":"e02288454bb4010e06b705d82646abddb3799f0cd655f574aa19f9d91485a4a2","src/transport/macos/transaction.rs":"9dcdebd13d5fd5a185b5ad777a80c825a6ba5e76b141c238aa115b451b9a72fa","src/transport/mock/device.rs":"83152416b4a54a805fa21487efc2fa95cebc0c55299f34f0ff049d56531706e4","src/transport/mock/mod.rs":"9c4c87efd19adddc1a91c699a6c328063cfbac5531b76346a5ff92e986aded8f","src/transport/mock/transaction.rs":"be3ed8c389dfa04122364b82515edd76fad6f5d5f72d15cacd45a84fb8397292","src/transport/mod.rs":"9b96909f4a8095af2fe4e317a5715e1bc795af1be0a1874d7143a852616c6fde","src/transport/netbsd/device.rs":"4c8404683c1fe07e562ec7126538643278e632f20e1f38b909a02526ef50d8e4","src/transport/netbsd/fd.rs":"5464019025d03ea2a39c82f76b238bbbdb0ea63f5a5fc7c9d974e235139cd53b","src/transport/netbsd/mod.rs":"b1c52aa29537330cebe67427062d6c94871cab2a9b0c04b2305d686f07e88fd5","src/transport/netbsd/monitor.rs":"fb2917e4ba53cc9867987a539061f82d011f4c6e478df1157d965d32df2eb922","src/transport/netbsd/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/netbsd/uhid.rs":"d15be35e2413240066a8f086bb8846b08a6a92bf6a1941c3eec1329dd3a4f9ce","src/transport/openbsd/device.rs":"8fcd46ae1e1df4434aa93e629ec379f7944a0120c3e75b0ee4f9f2afa3a187be","src/transport/openbsd/mod.rs":"514274d414042ff84b3667a41a736e78581e22fda87ccc97c2bc05617e381a30","src/transport/openbsd/monitor.rs":"2e0ba6ecc69b450be9cbfd21a7c65036ed2ce593b12363596d3eae0b5bfb79e8","src/transport/openbsd/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/stub/device.rs":"d064faee6c5e4681e6b81878aa419de54c9df9a7eb805336d0cfda82318253d1","src/transport/stub/mod.rs":"6a7fec504a52d403b0241b18cd8b95088a31807571f4c0a67e4055afc74f4453","src/transport/stub/transaction.rs":"c9a3ade9562468163f28fd51e7ff3e0bf5854b7edade9e987000d11c5d0e62d2","src/transport/windows/device.rs":"a5e997dc84acf526cbd23d0228d6182ab23d0e56a9314349e5066457502e10a7","src/transport/windows/mod.rs":"218e7f2fe91ecb390c12bba5a5ffdad2c1f0b22861c937f4d386262e5b3dd617","src/transport/windows/monitor.rs":"95913d49e7d83482e420493d89b53ffceb6a49e646a87de934dff507b3092b4c","src/transport/windows/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/windows/winapi.rs":"b2a4cc85f14e39cadfbf068ee001c9d776f028d3cf09cb926d4364c5b437c112","src/u2ftypes.rs":"b9c96004c13a8c2cf510983bfb701909c8f5953dfbb5764040d54814bb05f370","src/util.rs":"80a6e00a86a0042d827dfba1c689864898ac56ee7582baa41dd6ad54c5961e57","testing/cross/powerpc64le-unknown-linux-gnu.Dockerfile":"d7463ff4376e3e0ca3fed879fab4aa975c4c0a3e7924c5b88aef9381a5d013de","testing/cross/x86_64-unknown-linux-gnu.Dockerfile":"11c79c04b07a171b0c9b63ef75fa75f33263ce76e3c1eda0879a3e723ebd0c24","testing/run_cross.sh":"cc2a7e0359f210eba2e7121f81eb8ab0125cea6e0d0f2698177b0fe2ad0c33d8"},"package":"e0e925f7f169517290d746d6080fccc3d49605eb7ea8f0394fc3fbd6e2c31ab3"} \ No newline at end of file +{"files":{"Cargo.lock":"18631bf9eea80bcbafbaffc9047abe3706f57143ac87efc3de604f2116cf9f01","Cargo.toml":"f8b0222aac5bb48418203d6ba1faefa5a4e8aa1ddb074c72952d01d83bae4a37","Cross.toml":"8d132da818d48492aa9f4b78a348f0df3adfae45d988d42ebd6be8a5adadb6c3","LICENSE":"e866c8f5864d4cacfe403820e722e9dc03fe3c7565efa5e4dad9051d827bb92a","README.md":"c87d9c7cc44f1dd4ef861a3a9f8cd2eb68aedd3814768871f5fb63c2070806cd","build.rs":"5b909f42e52ed2056afa3693544ef1c1dc5e90d00e7d8730175a228bd0233b43","examples/ctap2.rs":"e83d16c1b5aaca585b7df0655a696ddfeb0529aa2f4645bd30c74f58574fc0c6","examples/ctap2_discoverable_creds.rs":"539951c016e6058b7633fc76ec0f1c02a405eedb45dfc912c70c0b6991a77937","examples/interactive_management.rs":"e164439c7925f748620540668e5fea467342a16dd062f9d08f217ffe59bf1b44","examples/reset.rs":"a8ffd75b248beeede129698f2c7bc971789fda0e6e8f66ac76cc2f8ae770432d","examples/set_pin.rs":"14806b2d20034534f77dd5000c440af4dd4f1f4eedbd335942cd1cc1fcd0037a","examples/test_exclude_list.rs":"f8c02aaa6236723f5c52440e5155608517cd19048899a21ee5deb5ea8537edca","rustfmt.toml":"ceb6615363d6fff16426eb56f5727f98a7f7ed459ba9af735b1d8b672e2c3b9b","src/authenticatorservice.rs":"59a43779765a7e40841b507eb2a23da0693e0fdfec85a0ffc4f99e66aa76391f","src/consts.rs":"44fb7c396dc87d1657d1feed08e956fc70608c0b06a034716b626419b442bcfe","src/crypto/der.rs":"7afd8c914a2df36052355cd5b21507472ed4d3925305050f9d7f2e5ca7e4d018","src/crypto/dummy.rs":"92e5238da8e6f57bae057f564f5c84719dab6ed22c1b390884fe1a8bccb91f17","src/crypto/mod.rs":"3e77cc3b2be7573145fed8ed8731736f1a6e777e38df248041e3038f2721b91f","src/crypto/nss.rs":"0dcdf2af1d49f8aa9c235c5bcdeabcf26abed219dfd84b17667cd2d0ae9d9c8c","src/crypto/openssl.rs":"b2577be577b884b569a5bc039b82e0402db097b5652d70d6320922ad6795f1c7","src/ctap2/attestation.rs":"ae55b012de7ee06abf6744283ac60039f9f22150beddb1ea89f5693cf0ef50d2","src/ctap2/client_data.rs":"447c970c9d079431a3cab0bf3b0d68f1848966ffebe77f6f08b3e7a865073786","src/ctap2/commands/authenticator_config.rs":"920822bc7dbfbe4dd3429978d24c15091d829e6b69f46cb7347bb7140518af27","src/ctap2/commands/bio_enrollment.rs":"1f3096f1b7e7a245199ff844840764c643a573bc3b7818a003b6bf693c3e829f","src/ctap2/commands/client_pin.rs":"13e73ad4ce11ba77587a33efade6ea80bbc397f40c1d240e056920ccafb2d56c","src/ctap2/commands/credential_management.rs":"fcb726e17f9805461a0069f306a9118b76cac1daaf3ebf9ba947676ee71785ea","src/ctap2/commands/get_assertion.rs":"0f7266d61d76d72a37b0658f0afae2757b83570e9d78f3c8cdc867cde7a3ef3e","src/ctap2/commands/get_info.rs":"9c81bf95d397a11fba2077b88c00c50ce682243728bf21ea9bc4dd0646ddca0d","src/ctap2/commands/get_next_assertion.rs":"fb0edd201d90f5a706edf58cdea901f8783f882968a028b466892d0a38d10ffe","src/ctap2/commands/get_version.rs":"643a63066d9752f3b1d313b45aea1b20fadde8b6f58736ff6d8aa06665cca390","src/ctap2/commands/make_credentials.rs":"efcec147a67019cf0e79d5bd6e359ba3fb73c91075ae3362c75a6b2f3b5b078a","src/ctap2/commands/mod.rs":"3360906596ea857e228fffd4cf5f9ff937d46edba4669244c67bb202a45e4931","src/ctap2/commands/reset.rs":"610a1979d20e801cb2ac4a6efe15b40699d30a70ba8c3f834c0066da10af3637","src/ctap2/commands/selection.rs":"dd7d21bd063fd618a53fd64a4e88e41e9344f335dfbcec25f8a886b6c4da8e0c","src/ctap2/mod.rs":"a4ad90c06e40107d750d581232d562eec8a2757abe1efec6c742e3b0cb958d59","src/ctap2/preflight.rs":"1ed8a07274c81879983e5d655eadc926d8b1fe15904bfb821da7ea3a7d2338af","src/ctap2/server.rs":"1e57ae45dae1fecaed524d643c6fb51ba2c38649ef876638f390246d6223e762","src/ctap2/utils.rs":"7ca56ae241f22de67047d22c6650bbe36f1268ab64bf8cc9256d2f00ef750c0a","src/errors.rs":"0639d55735b5b67562b4ff8b6b6639d1449f56cdcb0a0cdab3209d1bc972cab7","src/lib.rs":"00f2bfd489f77d9f10711983d742148a037e5989d02a44a65ae0fd3cbbd34dc0","src/manager.rs":"b7106c82c62c8bb47d3ec979f3454e0fb04dae0b32d029624b23a880819b16a7","src/statecallback.rs":"6748b74341876d7698aa3b1a6c1de002a74e201375040397255445ff4a1d7982","src/statemachine.rs":"412747465209ac080e941dae7cfdd808709803305977cfc08f349a8d2cfc61b0","src/status_update.rs":"6b8de35dbcba36dcf6ff388b73c49e04568155ed288b35ec9582001fbdd177e7","src/transport/device_selector.rs":"da6de0f43d9b794e58afa738c2b756d9139968c5587f7a51aa84c7442762735c","src/transport/errors.rs":"5af7cb8d22ffa63bf4264d182a0f54b9b3a2cc9d19d832b3495857229f9a2875","src/transport/freebsd/device.rs":"0aa53590382093225b6f17af4deb3224eb52a883d19dc7da520fcbfceb1cad58","src/transport/freebsd/mod.rs":"42dcb57fbeb00140003a8ad39acac9b547062b8f281a3fa5deb5f92a6169dde6","src/transport/freebsd/monitor.rs":"a6b34af4dd2e357a5775b1f3a723766107c11ef98dba859b1188ed08e0e450a2","src/transport/freebsd/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/freebsd/uhid.rs":"a194416a8bc5d428c337f8d96a2248769ca190810852bbe5ee686ab595d8eb4c","src/transport/hid.rs":"9a578446e1e0d5dff7ba666e338b0febcad3a66fb8cf7ab41f58500531a7992a","src/transport/hidproto.rs":"1f36992a806f753bac6582c2263d5cc1dd2924df52af97370e55dd6c189ef545","src/transport/linux/device.rs":"206a5ae404590bc73acc03e22823ea4252848b5afab744a51f9630b0f1af813c","src/transport/linux/hidraw.rs":"c7a0df9b4e51cb2736218ffffa02b2b2547b7c515d69f9bae2c9a8c8f1cb547b","src/transport/linux/hidwrapper.h":"72785db3a9b27ea72b6cf13a958fee032af54304522d002f56322473978a20f9","src/transport/linux/hidwrapper.rs":"d203e8804e7632b8d47a224c186d1f431800f04ddc43360d5c086f71e9b0f674","src/transport/linux/ioctl_aarch64le.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_armle.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_loongarch64.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_mips64le.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_mipsbe.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_mipsle.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_powerpc64be.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_powerpc64le.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_powerpcbe.rs":"fbda309934ad8bda689cd4fb5c0ca696fe26dedb493fe9d5a5322c3047d474fd","src/transport/linux/ioctl_riscv64.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_s390xbe.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_x86.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/ioctl_x86_64.rs":"2d8b265cd39a9f46816f83d5a5df0701c13eb842bc609325bad42ce50add3bf0","src/transport/linux/mod.rs":"446e435126d2a58f167f648dd95cba28e8ac9c17f1f799e1eaeab80ea800fc57","src/transport/linux/monitor.rs":"5e3ec2618dd74027ae6ca1527991254e3271cce59106d4920ce0414094e22f64","src/transport/linux/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/macos/device.rs":"cef7cec681d9c777aac16e662bcbe8ff0d39efb2116086bf9f792945f1454c96","src/transport/macos/iokit.rs":"7dc4e7bbf8e42e2fcde0cee8e48d14d6234a5a910bd5d3c4e966d8ba6b73992f","src/transport/macos/mod.rs":"333e561554fc901d4f6092f6e4c85823e2b0c4ff31c9188d0e6d542b71a0a07c","src/transport/macos/monitor.rs":"e02288454bb4010e06b705d82646abddb3799f0cd655f574aa19f9d91485a4a2","src/transport/macos/transaction.rs":"9dcdebd13d5fd5a185b5ad777a80c825a6ba5e76b141c238aa115b451b9a72fa","src/transport/mock/device.rs":"83152416b4a54a805fa21487efc2fa95cebc0c55299f34f0ff049d56531706e4","src/transport/mock/mod.rs":"9c4c87efd19adddc1a91c699a6c328063cfbac5531b76346a5ff92e986aded8f","src/transport/mock/transaction.rs":"be3ed8c389dfa04122364b82515edd76fad6f5d5f72d15cacd45a84fb8397292","src/transport/mod.rs":"8b01d4b8aae9d711519a2a195ce7ecd6b470b8c826fb1f66d5ab18867f8b5136","src/transport/netbsd/device.rs":"4c8404683c1fe07e562ec7126538643278e632f20e1f38b909a02526ef50d8e4","src/transport/netbsd/fd.rs":"5464019025d03ea2a39c82f76b238bbbdb0ea63f5a5fc7c9d974e235139cd53b","src/transport/netbsd/mod.rs":"b1c52aa29537330cebe67427062d6c94871cab2a9b0c04b2305d686f07e88fd5","src/transport/netbsd/monitor.rs":"fb2917e4ba53cc9867987a539061f82d011f4c6e478df1157d965d32df2eb922","src/transport/netbsd/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/netbsd/uhid.rs":"d15be35e2413240066a8f086bb8846b08a6a92bf6a1941c3eec1329dd3a4f9ce","src/transport/openbsd/device.rs":"8fcd46ae1e1df4434aa93e629ec379f7944a0120c3e75b0ee4f9f2afa3a187be","src/transport/openbsd/mod.rs":"514274d414042ff84b3667a41a736e78581e22fda87ccc97c2bc05617e381a30","src/transport/openbsd/monitor.rs":"2e0ba6ecc69b450be9cbfd21a7c65036ed2ce593b12363596d3eae0b5bfb79e8","src/transport/openbsd/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/stub/device.rs":"d064faee6c5e4681e6b81878aa419de54c9df9a7eb805336d0cfda82318253d1","src/transport/stub/mod.rs":"6a7fec504a52d403b0241b18cd8b95088a31807571f4c0a67e4055afc74f4453","src/transport/stub/transaction.rs":"c9a3ade9562468163f28fd51e7ff3e0bf5854b7edade9e987000d11c5d0e62d2","src/transport/windows/device.rs":"a5e997dc84acf526cbd23d0228d6182ab23d0e56a9314349e5066457502e10a7","src/transport/windows/mod.rs":"218e7f2fe91ecb390c12bba5a5ffdad2c1f0b22861c937f4d386262e5b3dd617","src/transport/windows/monitor.rs":"95913d49e7d83482e420493d89b53ffceb6a49e646a87de934dff507b3092b4c","src/transport/windows/transaction.rs":"ec28475a70dded260f9a7908c7f88dd3771f5d64b9a5dda835411d13b713c39a","src/transport/windows/winapi.rs":"b2a4cc85f14e39cadfbf068ee001c9d776f028d3cf09cb926d4364c5b437c112","src/u2ftypes.rs":"b9c96004c13a8c2cf510983bfb701909c8f5953dfbb5764040d54814bb05f370","src/util.rs":"80a6e00a86a0042d827dfba1c689864898ac56ee7582baa41dd6ad54c5961e57","testing/cross/powerpc64le-unknown-linux-gnu.Dockerfile":"d7463ff4376e3e0ca3fed879fab4aa975c4c0a3e7924c5b88aef9381a5d013de","testing/cross/x86_64-unknown-linux-gnu.Dockerfile":"11c79c04b07a171b0c9b63ef75fa75f33263ce76e3c1eda0879a3e723ebd0c24","testing/run_cross.sh":"cc2a7e0359f210eba2e7121f81eb8ab0125cea6e0d0f2698177b0fe2ad0c33d8"},"package":"2a46aebc9de6a88556552bc40e5ffbd479705cc0ab46fee0dec476dc2886eb13"} \ No newline at end of file diff --git a/third_party/rust/authenticator/Cargo.lock b/third_party/rust/authenticator/Cargo.lock index dd0c4094cc9b..dd80c07ef990 100644 --- a/third_party/rust/authenticator/Cargo.lock +++ b/third_party/rust/authenticator/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ [[package]] name = "authenticator" -version = "0.4.0-alpha.21" +version = "0.4.0-alpha.22" dependencies = [ "assert_matches", "base64", diff --git a/third_party/rust/authenticator/Cargo.toml b/third_party/rust/authenticator/Cargo.toml index e8efadca9ff6..8a23613b239a 100644 --- a/third_party/rust/authenticator/Cargo.toml +++ b/third_party/rust/authenticator/Cargo.toml @@ -12,7 +12,7 @@ [package] edition = "2018" name = "authenticator" -version = "0.4.0-alpha.21" +version = "0.4.0-alpha.22" authors = [ "J.C. Jones ", "Tim Taubert ", diff --git a/third_party/rust/authenticator/examples/ctap2.rs b/third_party/rust/authenticator/examples/ctap2.rs index 06f8dd5c2a62..be742a5e0ee8 100644 --- a/third_party/rust/authenticator/examples/ctap2.rs +++ b/third_party/rust/authenticator/examples/ctap2.rs @@ -7,8 +7,9 @@ use authenticator::{ crypto::COSEAlgorithm, ctap2::server::{ AuthenticationExtensionsClientInputs, CredentialProtectionPolicy, - PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, RelyingParty, - ResidentKeyRequirement, Transport, User, UserVerificationRequirement, + PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, + PublicKeyCredentialUserEntity, RelyingParty, ResidentKeyRequirement, Transport, + UserVerificationRequirement, }, statecallback::StateCallback, Pin, StatusPinUv, StatusUpdate, @@ -132,6 +133,9 @@ fn main() { Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } + Ok(StatusUpdate::SelectResultNotice(_, _)) => { + panic!("Unexpected select device notice") + } Err(RecvError) => { println!("STATUS: end"); return; @@ -139,7 +143,7 @@ fn main() { } }); - let user = User { + let user = PublicKeyCredentialUserEntity { id: "user_id".as_bytes().to_vec(), name: Some("A. User".to_string()), display_name: None, diff --git a/third_party/rust/authenticator/examples/ctap2_discoverable_creds.rs b/third_party/rust/authenticator/examples/ctap2_discoverable_creds.rs index db7f90d299ba..d19ccc6f9ee8 100644 --- a/third_party/rust/authenticator/examples/ctap2_discoverable_creds.rs +++ b/third_party/rust/authenticator/examples/ctap2_discoverable_creds.rs @@ -7,8 +7,8 @@ use authenticator::{ crypto::COSEAlgorithm, ctap2::server::{ AuthenticationExtensionsClientInputs, PublicKeyCredentialDescriptor, - PublicKeyCredentialParameters, RelyingParty, ResidentKeyRequirement, Transport, User, - UserVerificationRequirement, + PublicKeyCredentialParameters, PublicKeyCredentialUserEntity, RelyingParty, + ResidentKeyRequirement, Transport, UserVerificationRequirement, }, statecallback::StateCallback, Pin, StatusPinUv, StatusUpdate, @@ -16,7 +16,8 @@ use authenticator::{ use getopts::Options; use sha2::{Digest, Sha256}; use std::sync::mpsc::{channel, RecvError}; -use std::{env, thread}; +use std::{env, io, thread}; +use std::io::Write; fn print_usage(program: &str, opts: Options) { println!("------------------------------------------------------------------------"); @@ -28,6 +29,37 @@ fn print_usage(program: &str, opts: Options) { print!("{}", opts.usage(&brief)); } +fn ask_user_choice(choices: &[PublicKeyCredentialUserEntity]) -> Option { + for (idx, op) in choices.iter().enumerate() { + println!("({idx}) \"{}\"", op.name.as_ref().unwrap()); + } + println!("({}) Cancel", choices.len()); + + let mut input = String::new(); + loop { + input.clear(); + print!("Your choice: "); + io::stdout() + .lock() + .flush() + .expect("Failed to flush stdout!"); + io::stdin() + .read_line(&mut input) + .expect("error: unable to read user input"); + if let Ok(idx) = input.trim().parse::() { + if idx < choices.len() { + // Add a newline in case of success for better separation of in/output + println!(); + return Some(idx); + } else if idx == choices.len() { + println!(); + return None; + } + println!("invalid input"); + } + } +} + fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms: u64) { println!(); println!("*********************************************************************"); @@ -98,6 +130,9 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms: Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } + Ok(StatusUpdate::SelectResultNotice(_, _)) => { + panic!("Unexpected select result notice") + } Err(RecvError) => { println!("STATUS: end"); return; @@ -105,7 +140,7 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms: } }); - let user = User { + let user = PublicKeyCredentialUserEntity { id: username.as_bytes().to_vec(), name: Some(username.to_string()), display_name: None, @@ -181,6 +216,10 @@ fn main() { "timeout in seconds", "SEC", ); + opts.optflag( + "s", + "skip_reg", + "Skip registration"); opts.optflag("h", "help", "print this help menu"); let matches = match opts.parse(&args[1..]) { @@ -208,8 +247,10 @@ fn main() { } }; - for username in &["A. User", "A. Nother", "Dr. Who"] { - register_user(&mut manager, username, timeout_ms) + if !matches.opt_present("skip_reg") { + for username in &["A. User", "A. Nother", "Dr. Who"] { + register_user(&mut manager, username, timeout_ms) + } } println!(); @@ -278,6 +319,11 @@ fn main() { Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } + Ok(StatusUpdate::SelectResultNotice(index_sender, users)) => { + println!("Multiple signatures returned. Select one or cancel."); + let idx = ask_user_choice(&users); + index_sender.send(idx).expect("Failed to send choice"); + } Err(RecvError) => { println!("STATUS: end"); return; @@ -322,11 +368,7 @@ fn main() { println!("Found credentials:"); println!( "{:?}", - assertion_object - .assertions - .iter() - .map(|x| x.user.clone().unwrap().name.unwrap()) // Unwrapping here, as these shouldn't fail - .collect::>() + assertion_object.assertion.user.clone().unwrap().name.unwrap() // Unwrapping here, as these shouldn't fail ); println!("-----------------------------------------------------------------"); println!("Done."); diff --git a/third_party/rust/authenticator/examples/interactive_management.rs b/third_party/rust/authenticator/examples/interactive_management.rs index 7272eebe576e..6ef0e9dcc3d6 100644 --- a/third_party/rust/authenticator/examples/interactive_management.rs +++ b/third_party/rust/authenticator/examples/interactive_management.rs @@ -727,6 +727,9 @@ fn interactive_status_callback(status_rx: Receiver) { Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } + Ok(StatusUpdate::SelectResultNotice(_, _)) => { + panic!("Unexpected select device notice") + } Err(RecvError) => { println!("STATUS: end"); return; diff --git a/third_party/rust/authenticator/examples/set_pin.rs b/third_party/rust/authenticator/examples/set_pin.rs index 2b204a32ff76..5534ca08fd32 100644 --- a/third_party/rust/authenticator/examples/set_pin.rs +++ b/third_party/rust/authenticator/examples/set_pin.rs @@ -114,6 +114,9 @@ fn main() { Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } + Ok(StatusUpdate::SelectResultNotice(_, _)) => { + panic!("Unexpected select device notice") + } Err(RecvError) => { println!("STATUS: end"); return; diff --git a/third_party/rust/authenticator/examples/test_exclude_list.rs b/third_party/rust/authenticator/examples/test_exclude_list.rs index cf7a04526d9d..e24c49d05aa4 100644 --- a/third_party/rust/authenticator/examples/test_exclude_list.rs +++ b/third_party/rust/authenticator/examples/test_exclude_list.rs @@ -7,8 +7,9 @@ use authenticator::{ crypto::COSEAlgorithm, ctap2::commands::StatusCode, ctap2::server::{ - PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, RelyingParty, - ResidentKeyRequirement, Transport, User, UserVerificationRequirement, + PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, + PublicKeyCredentialUserEntity, RelyingParty, ResidentKeyRequirement, Transport, + UserVerificationRequirement, }, errors::{AuthenticatorError, CommandError, HIDError, UnsupportedOption}, statecallback::StateCallback, @@ -127,6 +128,9 @@ fn main() { Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } + Ok(StatusUpdate::SelectResultNotice(_, _)) => { + panic!("Unexpected select device notice") + } Err(RecvError) => { println!("STATUS: end"); return; @@ -134,7 +138,7 @@ fn main() { } }); - let user = User { + let user = PublicKeyCredentialUserEntity { id: "user_id".as_bytes().to_vec(), name: Some("A. User".to_string()), display_name: None, diff --git a/third_party/rust/authenticator/src/authenticatorservice.rs b/third_party/rust/authenticator/src/authenticatorservice.rs index 8ffb6db967ae..e5935150edde 100644 --- a/third_party/rust/authenticator/src/authenticatorservice.rs +++ b/third_party/rust/authenticator/src/authenticatorservice.rs @@ -5,8 +5,8 @@ use crate::ctap2::commands::client_pin::Pin; use crate::ctap2::server::{ AuthenticationExtensionsClientInputs, PublicKeyCredentialDescriptor, - PublicKeyCredentialParameters, RelyingParty, ResidentKeyRequirement, User, - UserVerificationRequirement, + PublicKeyCredentialParameters, PublicKeyCredentialUserEntity, RelyingParty, + ResidentKeyRequirement, UserVerificationRequirement, }; use crate::errors::*; use crate::manager::Manager; @@ -18,7 +18,7 @@ pub struct RegisterArgs { pub client_data_hash: [u8; 32], pub relying_party: RelyingParty, pub origin: String, - pub user: User, + pub user: PublicKeyCredentialUserEntity, pub pub_cred_params: Vec, pub exclude_list: Vec, pub user_verification_req: UserVerificationRequirement, @@ -318,7 +318,8 @@ mod tests { use super::{AuthenticatorService, AuthenticatorTransport, Pin, RegisterArgs, SignArgs}; use crate::consts::PARAMETER_SIZE; use crate::ctap2::server::{ - RelyingParty, ResidentKeyRequirement, User, UserVerificationRequirement, + PublicKeyCredentialUserEntity, RelyingParty, ResidentKeyRequirement, + UserVerificationRequirement, }; use crate::errors::AuthenticatorError; use crate::statecallback::StateCallback; @@ -439,7 +440,7 @@ mod tests { name: None, }, origin: "example.com".to_string(), - user: User { + user: PublicKeyCredentialUserEntity { id: "user_id".as_bytes().to_vec(), name: Some("A. User".to_string()), display_name: None, @@ -515,7 +516,7 @@ mod tests { name: None, }, origin: "example.com".to_string(), - user: User { + user: PublicKeyCredentialUserEntity { id: "user_id".as_bytes().to_vec(), name: Some("A. User".to_string()), display_name: None, @@ -610,7 +611,7 @@ mod tests { name: None, }, origin: "example.com".to_string(), - user: User { + user: PublicKeyCredentialUserEntity { id: "user_id".as_bytes().to_vec(), name: Some("A. User".to_string()), display_name: None, diff --git a/third_party/rust/authenticator/src/crypto/der.rs b/third_party/rust/authenticator/src/crypto/der.rs new file mode 100644 index 000000000000..39c5e0b676b0 --- /dev/null +++ b/third_party/rust/authenticator/src/crypto/der.rs @@ -0,0 +1,185 @@ +use super::CryptoError; + +pub const TAG_INTEGER: u8 = 0x02; +pub const TAG_BIT_STRING: u8 = 0x03; +#[cfg(all(test, feature = "crypto_nss"))] +pub const TAG_OCTET_STRING: u8 = 0x04; +pub const TAG_NULL: u8 = 0x05; +pub const TAG_OBJECT_ID: u8 = 0x06; +pub const TAG_SEQUENCE: u8 = 0x30; + +// Object identifiers in DER tag-length-value form +pub const OID_EC_PUBLIC_KEY_BYTES: &[u8] = &[ + /* RFC 5480 (id-ecPublicKey) */ + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, +]; +pub const OID_SECP256R1_BYTES: &[u8] = &[ + /* RFC 5480 (secp256r1) */ + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, +]; +pub const OID_ED25519_BYTES: &[u8] = &[/* RFC 8410 (id-ed25519) */ 0x2b, 0x65, 0x70]; +pub const OID_RS256_BYTES: &[u8] = &[ + /* RFC 4055 (sha256WithRSAEncryption) */ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, +]; + +pub type Result = std::result::Result; + +const MAX_TAG_AND_LENGTH_BYTES: usize = 4; +fn write_tag_and_length(out: &mut Vec, tag: u8, len: usize) -> Result<()> { + if len > 0xFFFF { + return Err(CryptoError::LibraryFailure); + } + out.push(tag); + if len > 0xFF { + out.push(0x82); + out.push((len >> 8) as u8); + out.push(len as u8); + } else if len > 0x7F { + out.push(0x81); + out.push(len as u8); + } else { + out.push(len as u8); + } + Ok(()) +} + +pub fn integer(val: &[u8]) -> Result> { + if val.is_empty() { + return Err(CryptoError::MalformedInput); + } + // trim leading zeros, leaving a single zero if the input is the zero vector. + let mut val = val; + while val.len() > 1 && val[0] == 0 { + val = &val[1..]; + } + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + 1 + val.len()); + if val[0] & 0x80 != 0 { + // needs zero prefix + write_tag_and_length(&mut out, TAG_INTEGER, 1 + val.len())?; + out.push(0x00); + out.extend_from_slice(val); + } else { + write_tag_and_length(&mut out, TAG_INTEGER, val.len())?; + out.extend_from_slice(val); + } + Ok(out) +} + +pub fn bit_string(val: &[u8]) -> Result> { + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + 1 + val.len()); + write_tag_and_length(&mut out, TAG_BIT_STRING, 1 + val.len())?; + out.push(0x00); // trailing bits aren't supported + out.extend_from_slice(val); + Ok(out) +} + +pub fn null() -> Result> { + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES); + write_tag_and_length(&mut out, TAG_NULL, 0)?; + Ok(out) +} + +pub fn object_id(val: &[u8]) -> Result> { + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + val.len()); + write_tag_and_length(&mut out, TAG_OBJECT_ID, val.len())?; + out.extend_from_slice(val); + Ok(out) +} + +pub fn sequence(items: &[&[u8]]) -> Result> { + let len = items.iter().map(|i| i.len()).sum(); + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + len); + write_tag_and_length(&mut out, TAG_SEQUENCE, len)?; + for item in items { + out.extend_from_slice(item); + } + Ok(out) +} + +#[cfg(all(test, feature = "crypto_nss"))] +pub fn octet_string(val: &[u8]) -> Result> { + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + val.len()); + write_tag_and_length(&mut out, TAG_OCTET_STRING, val.len())?; + out.extend_from_slice(val); + Ok(out) +} + +#[cfg(all(test, feature = "crypto_nss"))] +pub fn context_specific_explicit_tag(tag: u8, content: &[u8]) -> Result> { + let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + content.len()); + write_tag_and_length(&mut out, 0xa0 + tag, content.len())?; + out.extend_from_slice(content); + Ok(out) +} + +// Given "tag || len || value || rest" where tag and len are of length one, len is in [0, 127], +// and value is of length len, returns (value, rest) +#[cfg(all(test, feature = "crypto_nss"))] +fn expect_tag_with_short_len(tag: u8, z: &[u8]) -> Result<(&[u8], &[u8])> { + if z.is_empty() { + return Err(CryptoError::MalformedInput); + } + let (h, z) = z.split_at(1); + if h[0] != tag || z.is_empty() { + return Err(CryptoError::MalformedInput); + } + let (h, z) = z.split_at(1); + if h[0] >= 0x80 || h[0] as usize > z.len() { + return Err(CryptoError::MalformedInput); + } + Ok(z.split_at(h[0] as usize)) +} + +// Given a DER encoded RFC 3279 Ecdsa-Sig-Value, +// Ecdsa-Sig-Value ::= SEQUENCE { +// r INTEGER, +// s INTEGER }, +// with r and s < 2^256, returns a 64 byte array containing +// r and s encoded as 32 byte zero-padded big endian unsigned +// integers +#[cfg(all(test, feature = "crypto_nss"))] +pub fn read_p256_sig(z: &[u8]) -> Result> { + // Strip the tag and length. + let (z, rest) = expect_tag_with_short_len(TAG_SEQUENCE, z)?; + + // The input should not have any trailing data. + if !rest.is_empty() { + return Err(CryptoError::MalformedInput); + } + + let read_u256 = |z| -> Result<(&[u8], &[u8])> { + let (r, z) = expect_tag_with_short_len(TAG_INTEGER, z)?; + // We're expecting r < 2^256, so no more than 33 bytes as a signed integer. + if r.is_empty() || r.len() > 33 { + return Err(CryptoError::MalformedInput); + } + // If it is 33 bytes the leading byte must be zero. + if r.len() == 33 && r[0] != 0 { + return Err(CryptoError::MalformedInput); + } + // Ensure r is no more than 32 bytes. + if r.len() == 33 { + Ok((&r[1..], z)) + } else { + Ok((r, z)) + } + }; + + let (r, z) = read_u256(z)?; + let (s, z) = read_u256(z)?; + + // We should have consumed the entire buffer + if !z.is_empty() { + return Err(CryptoError::MalformedInput); + } + + // Left pad each integer with zeros to length 32 and concatenate the results + let mut out = vec![0u8; 64]; + { + let (r_out, s_out) = out.split_at_mut(32); + r_out[32 - r.len()..].copy_from_slice(r); + s_out[32 - s.len()..].copy_from_slice(s); + } + Ok(out) +} diff --git a/third_party/rust/authenticator/src/crypto/mod.rs b/third_party/rust/authenticator/src/crypto/mod.rs index b412ef67c4dd..fd74030ed756 100644 --- a/third_party/rust/authenticator/src/crypto/mod.rs +++ b/third_party/rust/authenticator/src/crypto/mod.rs @@ -35,19 +35,9 @@ use backend::{ random_bytes, sha256, }; -pub use backend::ecdsa_p256_sha256_sign_raw; +mod der; -// Object identifiers in DER tag-length-value form -const DER_OID_EC_PUBLIC_KEY_BYTES: &[u8] = &[ - 0x06, 0x07, - /* {iso(1) member-body(2) us(840) ansi-x962(10045) keyType(2) ecPublicKey(1)} */ - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, -]; -const DER_OID_P256_BYTES: &[u8] = &[ - 0x06, 0x08, - /* {iso(1) member-body(2) us(840) ansi-x962(10045) curves(3) prime(1) prime256v1(7)} */ - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, -]; +pub use backend::ecdsa_p256_sha256_sign_raw; pub struct PinUvAuthProtocol(Box); impl PinUvAuthProtocol { @@ -726,37 +716,25 @@ impl COSEEC2Key { } pub fn der_spki(&self) -> Result, CryptoError> { - let (curve_oid, seq_len, alg_len, spk_len) = match self.curve { - Curve::SECP256R1 => ( - DER_OID_P256_BYTES, - [0x59].as_slice(), - [0x13].as_slice(), - [0x42].as_slice(), - ), - x => return Err(CryptoError::UnsupportedCurve(x)), - }; + if self.curve != Curve::SECP256R1 { + return Err(CryptoError::UnsupportedCurve(self.curve)); + } - // [RFC 5280] - let mut spki: Vec = vec![]; // SubjectPublicKeyInfo - spki.push(0x30); - spki.extend_from_slice(seq_len); - // AlgorithmIdentifier - spki.push(0x30); - spki.extend_from_slice(alg_len); - // ObjectIdentifier - spki.extend_from_slice(DER_OID_EC_PUBLIC_KEY_BYTES); - // RFC 5480 ECParameters - spki.extend_from_slice(curve_oid); - // BIT STRING encoding uncompressed SEC1 public point - spki.push(0x03); - spki.extend_from_slice(spk_len); - spki.push(0x0); // no trailing zeros - spki.push(0x04); // SEC 1 encoded uncompressed point - spki.extend_from_slice(&self.x); - spki.extend_from_slice(&self.y); - - Ok(spki) + der::sequence(&[ + // algorithm: AlgorithmIdentifier + &der::sequence(&[ + // algorithm + &der::object_id(der::OID_EC_PUBLIC_KEY_BYTES)?, + // parameters + &der::object_id(der::OID_SECP256R1_BYTES)?, + ])?, + // subjectPublicKey + &der::bit_string( + // SEC 1 uncompressed format + &[&[0x04], self.x.as_slice(), self.y.as_slice()].concat(), + )?, + ]) } } @@ -771,6 +749,30 @@ pub struct COSEOKPKey { pub x: Vec, } +impl COSEOKPKey { + pub fn der_spki(&self) -> Result, CryptoError> { + if self.curve != Curve::Ed25519 { + return Err(CryptoError::UnsupportedCurve(self.curve)); + } + + // SubjectPublicKeyInfo + der::sequence(&[ + // algorithm: AlgorithmIdentifier + &der::sequence(&[ + // algorithm + &der::object_id(der::OID_ED25519_BYTES)?, + // parameters + // (absent as per RFC 8410) + ])?, + // subjectPublicKey + &der::bit_string( + // RFC 8410 + self.x.as_slice(), + )?, + ]) + } +} + /// A COSE RSA PublicKey. This is a provided credential from a registered authenticator. #[derive(Clone, Debug, PartialEq, Eq)] pub struct COSERSAKey { @@ -780,6 +782,26 @@ pub struct COSERSAKey { pub e: Vec, } +impl COSERSAKey { + pub fn der_spki(&self) -> Result, CryptoError> { + // SubjectPublicKeyInfo + der::sequence(&[ + // algorithm: AlgorithmIdentifier + &der::sequence(&[ + // algorithm + &der::object_id(der::OID_RS256_BYTES)?, + // parameters + &der::null()?, + ])?, + // subjectPublicKey + &der::bit_string( + // RFC 4055 RSAPublicKey + &der::sequence(&[&der::integer(&self.n)?, &der::integer(&self.e)?])?, + )?, + ]) + } +} + // https://tools.ietf.org/html/rfc8152#section-13 #[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -820,10 +842,10 @@ impl TryFrom for COSEKeyTypeId { #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum COSEKeyType { - /// Identifies this as an Elliptic Curve octet key pair - OKP(COSEOKPKey), // Not used here /// Identifies this as an Elliptic Curve EC2 key EC2(COSEEC2Key), + /// Identifies this as an Elliptic Curve octet key pair + OKP(COSEOKPKey), /// Identifies this as an RSA key RSA(COSERSAKey), } @@ -854,6 +876,14 @@ impl COSEKey { }; Ok((private, public)) } + + pub fn der_spki(&self) -> Result, CryptoError> { + match &self.key { + COSEKeyType::EC2(ec2_key) => ec2_key.der_spki(), + COSEKeyType::OKP(okp_key) => okp_key.der_spki(), + COSEKeyType::RSA(rsa_key) => rsa_key.der_spki(), + } + } } impl<'de> Deserialize<'de> for COSEKey { @@ -993,7 +1023,7 @@ impl Serialize for COSEKey { S: Serializer, { let map_len = match &self.key { - COSEKeyType::OKP(_) => 3, + COSEKeyType::OKP(_) => 4, COSEKeyType::EC2(_) => 5, COSEKeyType::RSA(_) => 4, }; @@ -1127,7 +1157,7 @@ mod test { Curve, PinProtocolImpl, PinUvAuth1, PinUvAuth2, PinUvAuthProtocol, PublicInputs, SharedSecret, }; - use crate::crypto::{COSEEC2Key, COSEKeyType, COSERSAKey}; + use crate::crypto::{COSEEC2Key, COSEKeyType, COSEOKPKey, COSERSAKey}; use crate::ctap2::attestation::AAGuid; use crate::ctap2::commands::client_pin::Pin; use crate::ctap2::commands::get_info::{ @@ -1137,87 +1167,196 @@ mod test { use crate::AuthenticatorInfo; use serde_cbor::de::from_slice; + // Extracted from RFC 8410 Example 10.1 + const SAMPLE_ED25519_KEY: &[u8] = &[ + 0x19, 0xbf, 0x44, 0x09, 0x69, 0x84, 0xcd, 0xfe, 0x85, 0x41, 0xba, 0xc1, 0x67, 0xdc, 0x3b, + 0x96, 0xc8, 0x50, 0x86, 0xaa, 0x30, 0xb6, 0xb6, 0xcb, 0x0c, 0x5c, 0x38, 0xad, 0x70, 0x31, + 0x66, 0xe1, + ]; + + const SAMPLE_P256_X: &[u8] = &[ + 0xfc, 0x9e, 0xd3, 0x6f, 0x7c, 0x1a, 0xa9, 0x15, 0xce, 0x3e, 0xa1, 0x77, 0xf0, 0x75, 0x67, + 0xf0, 0x7f, 0x16, 0xf9, 0x47, 0x9d, 0x95, 0xad, 0x8e, 0xd4, 0x97, 0x1d, 0x33, 0x05, 0xe3, + 0x1a, 0x80, + ]; + const SAMPLE_P256_Y: &[u8] = &[ + 0x50, 0xb7, 0x33, 0xaf, 0x8c, 0x0b, 0x0e, 0xe1, 0xda, 0x8d, 0xe0, 0xac, 0xf9, 0xd8, 0xe1, + 0x32, 0x82, 0xf0, 0x63, 0xb7, 0xb3, 0x0d, 0x73, 0xd4, 0xd3, 0x2c, 0x9a, 0xad, 0x6d, 0xfa, + 0x8b, 0x27, + ]; + + const SAMPLE_RSA_MODULUS: &[u8] = &[ + 0xd4, 0xd2, 0x53, 0xed, 0x7a, 0x69, 0xb1, 0x84, 0xc9, 0xfb, 0x70, 0x30, 0x0c, 0x51, 0xb1, + 0x8f, 0x89, 0x6c, 0xb1, 0x31, 0x6d, 0x87, 0xbe, 0xe1, 0xc7, 0xf7, 0xb0, 0x4f, 0xe7, 0x27, + 0xa7, 0xb7, 0x7c, 0x55, 0x20, 0x37, 0xa8, 0xac, 0x40, 0xf4, 0xbc, 0x59, 0xc4, 0x92, 0x8f, + 0x13, 0x5b, 0x5e, 0xa7, 0x18, 0x05, 0xcc, 0xd7, 0x9c, 0xfb, 0x88, 0x6c, 0xf1, 0xbc, 0x6b, + 0x1b, 0x8d, 0xb7, 0x8d, 0x2d, 0xaa, 0xcb, 0xee, 0xdb, 0xab, 0x49, 0x36, 0x77, 0xe5, 0xd1, + 0x84, 0xa1, 0x40, 0x3f, 0xf6, 0xf7, 0x98, 0x6c, 0xaa, 0x24, 0x48, 0x30, 0x44, 0xdc, 0x68, + 0xbd, 0x9e, 0x74, 0x37, 0xaf, 0x27, 0x12, 0x90, 0x74, 0x0d, 0x9e, 0x3c, 0xa5, 0x3a, 0x1d, + 0xb8, 0x54, 0x92, 0xd4, 0x6d, 0x1f, 0xf9, 0x39, 0xb8, 0x1d, 0x8a, 0x5e, 0xbe, 0x12, 0xbd, + 0xe2, 0x9c, 0xf2, 0x5a, 0x48, 0x5d, 0x71, 0x2c, 0x71, 0x72, 0x6d, 0xd2, 0xcb, 0x37, 0xb1, + 0xe6, 0x2f, 0x76, 0x43, 0xda, 0xca, 0x44, 0x30, 0x7b, 0x28, 0xe7, 0xe4, 0xec, 0xa9, 0xc9, + 0x1a, 0x5f, 0xe5, 0x51, 0x03, 0x25, 0x60, 0x7c, 0x5a, 0x69, 0x12, 0x4d, 0x50, 0xfd, 0xb2, + 0xb8, 0x6e, 0x13, 0xb2, 0x92, 0xda, 0x0e, 0x31, 0xc9, 0xf1, 0x9c, 0xde, 0x17, 0x63, 0xe4, + 0xcb, 0xac, 0xd5, 0xee, 0x84, 0x06, 0xde, 0x67, 0x2d, 0xb8, 0xd2, 0xe1, 0x4b, 0xbb, 0x49, + 0xea, 0x45, 0xd4, 0xa1, 0x7f, 0x46, 0xf2, 0xd6, 0x0c, 0x05, 0x9d, 0x1d, 0x1a, 0x99, 0x41, + 0x20, 0x5e, 0x1a, 0xa4, 0xcc, 0x21, 0x44, 0x58, 0x8b, 0xcd, 0x98, 0xe4, 0x3d, 0x53, 0x20, + 0xfc, 0xfc, 0x7b, 0x9f, 0x43, 0x35, 0xfb, 0x38, 0x37, 0x23, 0xd0, 0x76, 0xe3, 0x3d, 0x4f, + 0x89, 0x9b, 0x89, 0x32, 0x81, 0x89, 0xed, 0x58, 0xc0, 0x80, 0x18, 0x83, 0x5b, 0xaf, 0x5a, + 0xa5, + ]; + #[test] - fn test_serialize_rsa_key() { - let data: [u8; 272] = [ - 0xa4, 0x01, 0x03, 0x03, 0x39, 0x01, 0x00, 0x20, 0x59, 0x01, 0x00, 0xd4, 0xd2, 0x53, - 0xed, 0x7a, 0x69, 0xb1, 0x84, 0xc9, 0xfb, 0x70, 0x30, 0x0c, 0x51, 0xb1, 0x8f, 0x89, - 0x6c, 0xb1, 0x31, 0x6d, 0x87, 0xbe, 0xe1, 0xc7, 0xf7, 0xb0, 0x4f, 0xe7, 0x27, 0xa7, - 0xb7, 0x7c, 0x55, 0x20, 0x37, 0xa8, 0xac, 0x40, 0xf4, 0xbc, 0x59, 0xc4, 0x92, 0x8f, - 0x13, 0x5b, 0x5e, 0xa7, 0x18, 0x05, 0xcc, 0xd7, 0x9c, 0xfb, 0x88, 0x6c, 0xf1, 0xbc, - 0x6b, 0x1b, 0x8d, 0xb7, 0x8d, 0x2d, 0xaa, 0xcb, 0xee, 0xdb, 0xab, 0x49, 0x36, 0x77, - 0xe5, 0xd1, 0x84, 0xa1, 0x40, 0x3f, 0xf6, 0xf7, 0x98, 0x6c, 0xaa, 0x24, 0x48, 0x30, - 0x44, 0xdc, 0x68, 0xbd, 0x9e, 0x74, 0x37, 0xaf, 0x27, 0x12, 0x90, 0x74, 0x0d, 0x9e, - 0x3c, 0xa5, 0x3a, 0x1d, 0xb8, 0x54, 0x92, 0xd4, 0x6d, 0x1f, 0xf9, 0x39, 0xb8, 0x1d, - 0x8a, 0x5e, 0xbe, 0x12, 0xbd, 0xe2, 0x9c, 0xf2, 0x5a, 0x48, 0x5d, 0x71, 0x2c, 0x71, - 0x72, 0x6d, 0xd2, 0xcb, 0x37, 0xb1, 0xe6, 0x2f, 0x76, 0x43, 0xda, 0xca, 0x44, 0x30, - 0x7b, 0x28, 0xe7, 0xe4, 0xec, 0xa9, 0xc9, 0x1a, 0x5f, 0xe5, 0x51, 0x03, 0x25, 0x60, - 0x7c, 0x5a, 0x69, 0x12, 0x4d, 0x50, 0xfd, 0xb2, 0xb8, 0x6e, 0x13, 0xb2, 0x92, 0xda, - 0x0e, 0x31, 0xc9, 0xf1, 0x9c, 0xde, 0x17, 0x63, 0xe4, 0xcb, 0xac, 0xd5, 0xee, 0x84, - 0x06, 0xde, 0x67, 0x2d, 0xb8, 0xd2, 0xe1, 0x4b, 0xbb, 0x49, 0xea, 0x45, 0xd4, 0xa1, - 0x7f, 0x46, 0xf2, 0xd6, 0x0c, 0x05, 0x9d, 0x1d, 0x1a, 0x99, 0x41, 0x20, 0x5e, 0x1a, - 0xa4, 0xcc, 0x21, 0x44, 0x58, 0x8b, 0xcd, 0x98, 0xe4, 0x3d, 0x53, 0x20, 0xfc, 0xfc, - 0x7b, 0x9f, 0x43, 0x35, 0xfb, 0x38, 0x37, 0x23, 0xd0, 0x76, 0xe3, 0x3d, 0x4f, 0x89, - 0x9b, 0x89, 0x32, 0x81, 0x89, 0xed, 0x58, 0xc0, 0x80, 0x18, 0x83, 0x5b, 0xaf, 0x5a, - 0xa5, 0x21, 0x43, 0x01, 0x00, 0x01, + fn test_rsa_key_to_der_spki() { + // $ ascii2der | xxd -i + // SEQUENCE { + // SEQUENCE { + // # sha256WithRSAEncryption + // OBJECT_IDENTIFIER { 1.2.840.113549.1.1.11 } + // NULL {} + // } + // BIT_STRING { + // `00` + // SEQUENCE { + // INTEGER { `00d4d253ed7a69b184c9fb70300c51b18f896cb1316d87bee1c7f7b04fe727a7b77c552037a8ac40f4bc59c4928f135b5ea71805ccd79cfb886cf1bc6b1b8db78d2daacbeedbab493677e5d184a1403ff6f7986caa24483044dc68bd9e7437af271290740d9e3ca53a1db85492d46d1ff939b81d8a5ebe12bde29cf25a485d712c71726dd2cb37b1e62f7643daca44307b28e7e4eca9c91a5fe5510325607c5a69124d50fdb2b86e13b292da0e31c9f19cde1763e4cbacd5ee8406de672db8d2e14bbb49ea45d4a17f46f2d60c059d1d1a9941205e1aa4cc2144588bcd98e43d5320fcfc7b9f4335fb383723d076e33d4f899b89328189ed58c08018835baf5aa5` } + // INTEGER { 65537 } + // } + // } + // } + let expected: &[u8] = &[ + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xd4, 0xd2, 0x53, 0xed, 0x7a, 0x69, 0xb1, 0x84, 0xc9, + 0xfb, 0x70, 0x30, 0x0c, 0x51, 0xb1, 0x8f, 0x89, 0x6c, 0xb1, 0x31, 0x6d, 0x87, 0xbe, + 0xe1, 0xc7, 0xf7, 0xb0, 0x4f, 0xe7, 0x27, 0xa7, 0xb7, 0x7c, 0x55, 0x20, 0x37, 0xa8, + 0xac, 0x40, 0xf4, 0xbc, 0x59, 0xc4, 0x92, 0x8f, 0x13, 0x5b, 0x5e, 0xa7, 0x18, 0x05, + 0xcc, 0xd7, 0x9c, 0xfb, 0x88, 0x6c, 0xf1, 0xbc, 0x6b, 0x1b, 0x8d, 0xb7, 0x8d, 0x2d, + 0xaa, 0xcb, 0xee, 0xdb, 0xab, 0x49, 0x36, 0x77, 0xe5, 0xd1, 0x84, 0xa1, 0x40, 0x3f, + 0xf6, 0xf7, 0x98, 0x6c, 0xaa, 0x24, 0x48, 0x30, 0x44, 0xdc, 0x68, 0xbd, 0x9e, 0x74, + 0x37, 0xaf, 0x27, 0x12, 0x90, 0x74, 0x0d, 0x9e, 0x3c, 0xa5, 0x3a, 0x1d, 0xb8, 0x54, + 0x92, 0xd4, 0x6d, 0x1f, 0xf9, 0x39, 0xb8, 0x1d, 0x8a, 0x5e, 0xbe, 0x12, 0xbd, 0xe2, + 0x9c, 0xf2, 0x5a, 0x48, 0x5d, 0x71, 0x2c, 0x71, 0x72, 0x6d, 0xd2, 0xcb, 0x37, 0xb1, + 0xe6, 0x2f, 0x76, 0x43, 0xda, 0xca, 0x44, 0x30, 0x7b, 0x28, 0xe7, 0xe4, 0xec, 0xa9, + 0xc9, 0x1a, 0x5f, 0xe5, 0x51, 0x03, 0x25, 0x60, 0x7c, 0x5a, 0x69, 0x12, 0x4d, 0x50, + 0xfd, 0xb2, 0xb8, 0x6e, 0x13, 0xb2, 0x92, 0xda, 0x0e, 0x31, 0xc9, 0xf1, 0x9c, 0xde, + 0x17, 0x63, 0xe4, 0xcb, 0xac, 0xd5, 0xee, 0x84, 0x06, 0xde, 0x67, 0x2d, 0xb8, 0xd2, + 0xe1, 0x4b, 0xbb, 0x49, 0xea, 0x45, 0xd4, 0xa1, 0x7f, 0x46, 0xf2, 0xd6, 0x0c, 0x05, + 0x9d, 0x1d, 0x1a, 0x99, 0x41, 0x20, 0x5e, 0x1a, 0xa4, 0xcc, 0x21, 0x44, 0x58, 0x8b, + 0xcd, 0x98, 0xe4, 0x3d, 0x53, 0x20, 0xfc, 0xfc, 0x7b, 0x9f, 0x43, 0x35, 0xfb, 0x38, + 0x37, 0x23, 0xd0, 0x76, 0xe3, 0x3d, 0x4f, 0x89, 0x9b, 0x89, 0x32, 0x81, 0x89, 0xed, + 0x58, 0xc0, 0x80, 0x18, 0x83, 0x5b, 0xaf, 0x5a, 0xa5, 0x02, 0x03, 0x01, 0x00, 0x01, ]; - let expected: COSEKey = COSEKey { - alg: COSEAlgorithm::RS256, - key: COSEKeyType::RSA(COSERSAKey { - e: vec![1, 0, 1], - n: vec![ - 0xd4, 0xd2, 0x53, 0xed, 0x7a, 0x69, 0xb1, 0x84, 0xc9, 0xfb, 0x70, 0x30, 0x0c, - 0x51, 0xb1, 0x8f, 0x89, 0x6c, 0xb1, 0x31, 0x6d, 0x87, 0xbe, 0xe1, 0xc7, 0xf7, - 0xb0, 0x4f, 0xe7, 0x27, 0xa7, 0xb7, 0x7c, 0x55, 0x20, 0x37, 0xa8, 0xac, 0x40, - 0xf4, 0xbc, 0x59, 0xc4, 0x92, 0x8f, 0x13, 0x5b, 0x5e, 0xa7, 0x18, 0x05, 0xcc, - 0xd7, 0x9c, 0xfb, 0x88, 0x6c, 0xf1, 0xbc, 0x6b, 0x1b, 0x8d, 0xb7, 0x8d, 0x2d, - 0xaa, 0xcb, 0xee, 0xdb, 0xab, 0x49, 0x36, 0x77, 0xe5, 0xd1, 0x84, 0xa1, 0x40, - 0x3f, 0xf6, 0xf7, 0x98, 0x6c, 0xaa, 0x24, 0x48, 0x30, 0x44, 0xdc, 0x68, 0xbd, - 0x9e, 0x74, 0x37, 0xaf, 0x27, 0x12, 0x90, 0x74, 0x0d, 0x9e, 0x3c, 0xa5, 0x3a, - 0x1d, 0xb8, 0x54, 0x92, 0xd4, 0x6d, 0x1f, 0xf9, 0x39, 0xb8, 0x1d, 0x8a, 0x5e, - 0xbe, 0x12, 0xbd, 0xe2, 0x9c, 0xf2, 0x5a, 0x48, 0x5d, 0x71, 0x2c, 0x71, 0x72, - 0x6d, 0xd2, 0xcb, 0x37, 0xb1, 0xe6, 0x2f, 0x76, 0x43, 0xda, 0xca, 0x44, 0x30, - 0x7b, 0x28, 0xe7, 0xe4, 0xec, 0xa9, 0xc9, 0x1a, 0x5f, 0xe5, 0x51, 0x03, 0x25, - 0x60, 0x7c, 0x5a, 0x69, 0x12, 0x4d, 0x50, 0xfd, 0xb2, 0xb8, 0x6e, 0x13, 0xb2, - 0x92, 0xda, 0x0e, 0x31, 0xc9, 0xf1, 0x9c, 0xde, 0x17, 0x63, 0xe4, 0xcb, 0xac, - 0xd5, 0xee, 0x84, 0x06, 0xde, 0x67, 0x2d, 0xb8, 0xd2, 0xe1, 0x4b, 0xbb, 0x49, - 0xea, 0x45, 0xd4, 0xa1, 0x7f, 0x46, 0xf2, 0xd6, 0x0c, 0x05, 0x9d, 0x1d, 0x1a, - 0x99, 0x41, 0x20, 0x5e, 0x1a, 0xa4, 0xcc, 0x21, 0x44, 0x58, 0x8b, 0xcd, 0x98, - 0xe4, 0x3d, 0x53, 0x20, 0xfc, 0xfc, 0x7b, 0x9f, 0x43, 0x35, 0xfb, 0x38, 0x37, - 0x23, 0xd0, 0x76, 0xe3, 0x3d, 0x4f, 0x89, 0x9b, 0x89, 0x32, 0x81, 0x89, 0xed, - 0x58, 0xc0, 0x80, 0x18, 0x83, 0x5b, 0xaf, 0x5a, 0xa5, - ], - }), + let rsa_key = COSERSAKey { + e: vec![1, 0, 1], + n: SAMPLE_RSA_MODULUS.to_vec(), }; - let actual: COSEKey = from_slice(&data).unwrap(); - assert_eq!(actual, expected); - assert_eq!(&data[..], &serde_cbor::to_vec(&actual).unwrap()); + let cose_key: COSEKey = COSEKey { + alg: COSEAlgorithm::RS256, + key: COSEKeyType::RSA(rsa_key), + }; + let actual = cose_key.der_spki().expect("Failed to serialize to SPKI"); + assert_eq!(expected, &actual); } #[test] - fn test_serialize_ec2_key() { - let x = [ - 0xfc, 0x9e, 0xd3, 0x6f, 0x7c, 0x1a, 0xa9, 0x15, 0xce, 0x3e, 0xa1, 0x77, 0xf0, 0x75, - 0x67, 0xf0, 0x7f, 0x16, 0xf9, 0x47, 0x9d, 0x95, 0xad, 0x8e, 0xd4, 0x97, 0x1d, 0x33, - 0x05, 0xe3, 0x1a, 0x80, - ]; - let y = [ - 0x50, 0xb7, 0x33, 0xaf, 0x8c, 0x0b, 0x0e, 0xe1, 0xda, 0x8d, 0xe0, 0xac, 0xf9, 0xd8, - 0xe1, 0x32, 0x82, 0xf0, 0x63, 0xb7, 0xb3, 0x0d, 0x73, 0xd4, 0xd3, 0x2c, 0x9a, 0xad, - 0x6d, 0xfa, 0x8b, 0x27, - ]; - let serialized_key = [ - 0x04, 0xfc, 0x9e, 0xd3, 0x6f, 0x7c, 0x1a, 0xa9, 0x15, 0xce, 0x3e, 0xa1, 0x77, 0xf0, - 0x75, 0x67, 0xf0, 0x7f, 0x16, 0xf9, 0x47, 0x9d, 0x95, 0xad, 0x8e, 0xd4, 0x97, 0x1d, - 0x33, 0x05, 0xe3, 0x1a, 0x80, 0x50, 0xb7, 0x33, 0xaf, 0x8c, 0x0b, 0x0e, 0xe1, 0xda, - 0x8d, 0xe0, 0xac, 0xf9, 0xd8, 0xe1, 0x32, 0x82, 0xf0, 0x63, 0xb7, 0xb3, 0x0d, 0x73, - 0xd4, 0xd3, 0x2c, 0x9a, 0xad, 0x6d, 0xfa, 0x8b, 0x27, - ]; + fn test_rsa_key_to_cbor() { + let key = COSERSAKey { + e: vec![1, 0, 1], + n: SAMPLE_RSA_MODULUS.to_vec(), + }; + let cose_key: COSEKey = COSEKey { + alg: COSEAlgorithm::RS256, + key: COSEKeyType::RSA(key), + }; + let cose_key_cbor = serde_cbor::to_vec(&cose_key).expect("Failed to serialize key"); + let actual = serde_cbor::from_slice(&cose_key_cbor).expect("Failed to deserialize key"); + assert_eq!(cose_key, actual); + } - let ec2_key = COSEEC2Key::from_sec1_uncompressed(Curve::SECP256R1, &serialized_key) - .expect("Failed to decode SEC 1 key"); - assert_eq!(ec2_key.x, x); - assert_eq!(ec2_key.y, y); + #[test] + fn test_ec2_key_to_der_spki() { + // $ ascii2der | xxd -i + // SEQUENCE { + // SEQUENCE { + // # ecPublicKey + // OBJECT_IDENTIFIER { 1.2.840.10045.2.1 } + // # secp256r1 + // OBJECT_IDENTIFIER { 1.2.840.10045.3.1.7 } + // } + // BIT_STRING { `00` `04fc9ed36f7c1aa915ce3ea177f07567f07f16f9479d95ad8ed4971d3305e31a8050b733af8c0b0ee1da8de0acf9d8e13282f063b7b30d73d4d32c9aad6dfa8b27` } + // } + let expected = [ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xfc, + 0x9e, 0xd3, 0x6f, 0x7c, 0x1a, 0xa9, 0x15, 0xce, 0x3e, 0xa1, 0x77, 0xf0, 0x75, 0x67, + 0xf0, 0x7f, 0x16, 0xf9, 0x47, 0x9d, 0x95, 0xad, 0x8e, 0xd4, 0x97, 0x1d, 0x33, 0x05, + 0xe3, 0x1a, 0x80, 0x50, 0xb7, 0x33, 0xaf, 0x8c, 0x0b, 0x0e, 0xe1, 0xda, 0x8d, 0xe0, + 0xac, 0xf9, 0xd8, 0xe1, 0x32, 0x82, 0xf0, 0x63, 0xb7, 0xb3, 0x0d, 0x73, 0xd4, 0xd3, + 0x2c, 0x9a, 0xad, 0x6d, 0xfa, 0x8b, 0x27, + ]; + let ec2_key = COSEEC2Key { + curve: Curve::SECP256R1, + x: SAMPLE_P256_X.to_vec(), + y: SAMPLE_P256_Y.to_vec(), + }; + let cose_key = COSEKey { + alg: COSEAlgorithm::EDDSA, + key: COSEKeyType::EC2(ec2_key), + }; + let actual = cose_key.der_spki().expect("Failed to serialize key"); + assert_eq!(actual, expected); + } + + #[test] + fn test_ec2_key_to_cbor() { + let ec2_key = COSEEC2Key { + curve: Curve::SECP256R1, + x: SAMPLE_P256_X.to_vec(), + y: SAMPLE_P256_Y.to_vec(), + }; + let cose_key = COSEKey { + alg: COSEAlgorithm::EDDSA, + key: COSEKeyType::EC2(ec2_key), + }; + let cose_key_cbor = serde_cbor::to_vec(&cose_key).expect("Failed to serialize key"); + let actual = serde_cbor::from_slice(&cose_key_cbor).expect("Failed to deserialize key"); + assert_eq!(cose_key, actual); + } + + #[test] + fn test_okp_key_to_der_spki() { + // RFC 8410 Example 10.1 + let expected = [ + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0x19, 0xbf, + 0x44, 0x09, 0x69, 0x84, 0xcd, 0xfe, 0x85, 0x41, 0xba, 0xc1, 0x67, 0xdc, 0x3b, 0x96, + 0xc8, 0x50, 0x86, 0xaa, 0x30, 0xb6, 0xb6, 0xcb, 0x0c, 0x5c, 0x38, 0xad, 0x70, 0x31, + 0x66, 0xe1, + ]; + let okp_key = COSEOKPKey { + curve: Curve::Ed25519, + x: SAMPLE_ED25519_KEY.to_vec(), + }; + let cose_key = COSEKey { + alg: COSEAlgorithm::EDDSA, + key: COSEKeyType::OKP(okp_key), + }; + let actual = cose_key.der_spki().expect("Failed to serialize key"); + assert_eq!(actual, expected); + } + + #[test] + fn test_okp_key_to_cbor() { + let okp_key = COSEOKPKey { + curve: Curve::Ed25519, + x: SAMPLE_ED25519_KEY.to_vec(), + }; + let cose_key = COSEKey { + alg: COSEAlgorithm::EDDSA, + key: COSEKeyType::OKP(okp_key), + }; + let cose_key_cbor = serde_cbor::to_vec(&cose_key).expect("Failed to serialize key"); + let actual = serde_cbor::from_slice(&cose_key_cbor).expect("Failed to deserialize key"); + assert_eq!(cose_key, actual); } #[test] diff --git a/third_party/rust/authenticator/src/crypto/nss.rs b/third_party/rust/authenticator/src/crypto/nss.rs index 2386d158158f..56c23db5ddf0 100644 --- a/third_party/rust/authenticator/src/crypto/nss.rs +++ b/third_party/rust/authenticator/src/crypto/nss.rs @@ -1,4 +1,4 @@ -use super::{CryptoError, DER_OID_P256_BYTES}; +use super::CryptoError; use nss_gk_api::p11::{ PK11Origin, PK11_CreateContextBySymKey, PK11_Decrypt, PK11_DigestFinal, PK11_DigestOp, PK11_Encrypt, PK11_ExportDERPrivateKeyInfo, PK11_GenerateKeyPairWithOpFlags, @@ -17,11 +17,7 @@ use std::convert::TryFrom; use std::os::raw::{c_int, c_uint}; use std::ptr; -const DER_TAG_INTEGER: u8 = 0x02; -const DER_TAG_SEQUENCE: u8 = 0x30; - -#[cfg(test)] -use super::DER_OID_EC_PUBLIC_KEY_BYTES; +use super::der; #[cfg(test)] use nss_gk_api::p11::PK11_VerifyWithMechanism; @@ -34,118 +30,6 @@ impl From for CryptoError { pub type Result = std::result::Result; -// DER encode a pair of 32 byte unsigned integers as an RFC 3279 Ecdsa-Sig-Value. -// Ecdsa-Sig-Value ::= SEQUENCE { -// r INTEGER, -// s INTEGER }. -fn encode_der_p256_sig(r: &[u8], s: &[u8]) -> Result> { - if r.len() != 32 || s.len() != 32 { - return Err(CryptoError::MalformedInput); - } - // Each of the inputs is no more than 32 bytes as an unsigned integer. So each is no more than - // 33 bytes as a signed integer and no more than 35 bytes with tag and length. The surrounding - // tag and length for the SEQUENCE has length 2, so the output is no more than 72 bytes. - let mut out = Vec::with_capacity(72); - out.push(DER_TAG_SEQUENCE); - out.push(0xaa); // placeholder for final length - - let encode_u256 = |out: &mut Vec, r: &[u8]| { - // trim leading zeros, leaving a single zero if the input is the zero vector. - let mut r = r; - while r.len() > 1 && r[0] == 0 { - r = &r[1..]; - } - out.push(DER_TAG_INTEGER); - if r[0] & 0x80 != 0 { - // Pad with a zero byte to avoid r being interpreted as a negative value. - out.push((r.len() + 1) as u8); - out.push(0x00); - } else { - out.push(r.len() as u8); - } - out.extend_from_slice(r); - }; - - encode_u256(&mut out, r); - encode_u256(&mut out, s); - - // Write the length of the sequence - out[1] = (out.len() - 2) as u8; - - Ok(out) -} - -// Given "tag || len || value || rest" where tag and len are of length one, len is in [0, 127], -// and value is of length len, returns (value, rest) -#[cfg(test)] -fn der_expect_tag_with_short_len(tag: u8, z: &[u8]) -> Result<(&[u8], &[u8])> { - if z.is_empty() { - return Err(CryptoError::MalformedInput); - } - let (h, z) = z.split_at(1); - if h[0] != tag || z.is_empty() { - return Err(CryptoError::MalformedInput); - } - let (h, z) = z.split_at(1); - if h[0] >= 0x80 || h[0] as usize > z.len() { - return Err(CryptoError::MalformedInput); - } - Ok(z.split_at(h[0] as usize)) -} - -// Given a DER encoded RFC 3279 Ecdsa-Sig-Value, -// Ecdsa-Sig-Value ::= SEQUENCE { -// r INTEGER, -// s INTEGER }, -// with r and s < 2^256, returns a 64 byte array containing -// r and s encoded as 32 byte zero-padded big endian unsigned -// integers -#[cfg(test)] -fn decode_der_p256_sig(z: &[u8]) -> Result> { - // Strip the tag and length. - let (z, rest) = der_expect_tag_with_short_len(DER_TAG_SEQUENCE, z)?; - - // The input should not have any trailing data. - if !rest.is_empty() { - return Err(CryptoError::MalformedInput); - } - - let read_u256 = |z| -> Result<(&[u8], &[u8])> { - let (r, z) = der_expect_tag_with_short_len(DER_TAG_INTEGER, z)?; - // We're expecting r < 2^256, so no more than 33 bytes as a signed integer. - if r.is_empty() || r.len() > 33 { - return Err(CryptoError::MalformedInput); - } - // If it is 33 bytes the leading byte must be zero. - if r.len() == 33 && r[0] != 0 { - return Err(CryptoError::MalformedInput); - } - // Ensure r is no more than 32 bytes. - if r.len() == 33 { - Ok((&r[1..], z)) - } else { - Ok((r, z)) - } - }; - - let (r, z) = read_u256(z)?; - let (s, z) = read_u256(z)?; - - // We should have consumed the entire buffer - if !z.is_empty() { - return Err(CryptoError::MalformedInput); - } - - // Left pad each integer with zeros to length 32 and concatenate the results - let mut out = vec![0u8; 64]; - { - let (r_out, s_out) = out.split_at_mut(32); - r_out[32 - r.len()..].copy_from_slice(r); - s_out[32 - s.len()..].copy_from_slice(s); - } - Ok(out) -} - fn nss_public_key_from_der_spki(spki: &[u8]) -> Result { // TODO: replace this with an nss-gk-api function // https://github.com/mozilla/nss-gk-api/issues/7 @@ -186,7 +70,8 @@ fn generate_p256_nss() -> Result<(PrivateKey, PublicKey)> { // Hard-coding the P256 OID here is easier than extracting a group name from peer_public and // comparing it with P256. We'll fail in `PK11_GenerateKeyPairWithOpFlags` if peer_public is on // the wrong curve. - let mut oid = SECItemBorrowed::wrap(DER_OID_P256_BYTES); + let oid_bytes = der::object_id(der::OID_SECP256R1_BYTES)?; + let mut oid = SECItemBorrowed::wrap(&oid_bytes); let oid_ptr: *mut SECItem = oid.as_mut(); let slot = Slot::internal()?; @@ -270,7 +155,7 @@ pub fn ecdsa_p256_sha256_sign_raw(private: &[u8], data: &[u8]) -> Result } let (r, s) = signature_buf.split_at(32); - encode_der_p256_sig(r, s) + der::sequence(&[&der::integer(r)?, &der::integer(s)?]) } /// Ephemeral ECDH over P256. Takes a DER SubjectPublicKeyInfo that encodes a public key. Generates @@ -498,65 +383,63 @@ pub fn test_ecdh_p256_raw( let peer_public = nss_public_key_from_der_spki(peer_spki)?; - /* NSS has no mechanism to import a raw elliptic curve coordinate as a private key. - * We need to encode it in a key storage format such as PKCS#8. To avoid a dependency - * on an ASN.1 encoder for this test, we'll do it manually. */ - let pkcs8_private_key_info_version = &[0x02, 0x01, 0x00]; - let rfc5915_ec_private_key_version = &[0x02, 0x01, 0x01]; + // NSS has no mechanism to import a raw elliptic curve coordinate as a private key. + // We need to encode it in an RFC 5208 PrivateKeyInfo: + // + // PrivateKeyInfo ::= SEQUENCE { + // version Version, + // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + // privateKey PrivateKey, + // attributes [0] IMPLICIT Attributes OPTIONAL } + // + // Version ::= INTEGER + // PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + // PrivateKey ::= OCTET STRING + // Attributes ::= SET OF Attribute + // + // The privateKey field will contain an RFC 5915 ECPrivateKey: + // ECPrivateKey ::= SEQUENCE { + // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + // privateKey OCTET STRING, + // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + // publicKey [1] BIT STRING OPTIONAL + // } - let (curve_oid, seq_len, alg_len, attr_len, ecpriv_len, param_len, spk_len) = ( - DER_OID_P256_BYTES, - [0x81, 0x87].as_slice(), - [0x13].as_slice(), - [0x6d].as_slice(), - [0x6b].as_slice(), - [0x44].as_slice(), - [0x42].as_slice(), - ); - - let priv_len = client_private.len() as u8; // < 127 - - let mut pkcs8_priv: Vec = vec![]; - // RFC 5208 PrivateKeyInfo - pkcs8_priv.push(0x30); - pkcs8_priv.extend_from_slice(seq_len); - // Integer (0) - pkcs8_priv.extend_from_slice(pkcs8_private_key_info_version); - // AlgorithmIdentifier - pkcs8_priv.push(0x30); - pkcs8_priv.extend_from_slice(alg_len); - // ObjectIdentifier - pkcs8_priv.extend_from_slice(DER_OID_EC_PUBLIC_KEY_BYTES); - // RFC 5480 ECParameters - pkcs8_priv.extend_from_slice(curve_oid); - // Attributes - pkcs8_priv.push(0x04); - pkcs8_priv.extend_from_slice(attr_len); - // RFC 5915 ECPrivateKey - pkcs8_priv.push(0x30); - pkcs8_priv.extend_from_slice(ecpriv_len); - pkcs8_priv.extend_from_slice(rfc5915_ec_private_key_version); - pkcs8_priv.push(0x04); - pkcs8_priv.push(priv_len); - pkcs8_priv.extend_from_slice(client_private); - pkcs8_priv.push(0xa1); - pkcs8_priv.extend_from_slice(param_len); - pkcs8_priv.push(0x03); - pkcs8_priv.extend_from_slice(spk_len); - pkcs8_priv.push(0x0); - pkcs8_priv.push(0x04); // SEC 1 encoded uncompressed point - pkcs8_priv.extend_from_slice(client_public_x); - pkcs8_priv.extend_from_slice(client_public_y); + // PrivateKeyInfo + let priv_key_info = der::sequence(&[ + // version + &der::integer(&[0x00])?, + // privateKeyAlgorithm + &der::sequence(&[ + &der::object_id(der::OID_EC_PUBLIC_KEY_BYTES)?, + &der::object_id(der::OID_SECP256R1_BYTES)?, + ])?, + // privateKey + &der::octet_string( + // ECPrivateKey + &der::sequence(&[ + // version + &der::integer(&[0x01])?, + // privateKey + &der::octet_string(client_private)?, + // publicKey + &der::context_specific_explicit_tag( + 1, // publicKey + &der::bit_string(&[&[0x04], client_public_x, client_public_y].concat())?, + )?, + ])?, + )?, + ])?; // Now we can import the private key. let slot = Slot::internal()?; - let mut pkcs8_priv_item = SECItemBorrowed::wrap(&pkcs8_priv); - let pkcs8_priv_item_ptr: *mut SECItem = pkcs8_priv_item.as_mut(); + let mut priv_key_info_item = SECItemBorrowed::wrap(&priv_key_info); + let priv_key_info_item_ptr: *mut SECItem = priv_key_info_item.as_mut(); let mut client_private_ptr = ptr::null_mut(); unsafe { PK11_ImportDERPrivateKeyInfoAndReturnKey( *slot, - pkcs8_priv_item_ptr, + priv_key_info_item_ptr, ptr::null_mut(), ptr::null_mut(), PR_FALSE, @@ -581,7 +464,7 @@ pub fn test_ecdsa_p256_sha256_verify_raw( ) -> Result<()> { nss_gk_api::init(); - let signature = decode_der_p256_sig(signature)?; + let signature = der::read_p256_sig(signature)?; let public = nss_public_key_from_der_spki(public)?; unsafe { PK11_VerifyWithMechanism( diff --git a/third_party/rust/authenticator/src/ctap2/commands/credential_management.rs b/third_party/rust/authenticator/src/ctap2/commands/credential_management.rs index 09e3845de4a0..fa406a38909a 100644 --- a/third_party/rust/authenticator/src/ctap2/commands/credential_management.rs +++ b/third_party/rust/authenticator/src/ctap2/commands/credential_management.rs @@ -2,7 +2,8 @@ use super::{Command, CommandError, PinUvAuthCommand, RequestCtap2, StatusCode}; use crate::{ crypto::{COSEKey, PinUvAuthParam, PinUvAuthToken}, ctap2::server::{ - PublicKeyCredentialDescriptor, RelyingParty, RpIdHash, User, UserVerificationRequirement, + PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, RelyingParty, RpIdHash, + UserVerificationRequirement, }, errors::AuthenticatorError, transport::errors::HIDError, @@ -21,7 +22,7 @@ use std::fmt; struct CredManagementParams { rp_id_hash: Option, // RP ID SHA-256 hash credential_id: Option, // Credential Identifier - user: Option, // User Entity + user: Option, // User Entity } impl CredManagementParams { @@ -68,7 +69,7 @@ pub(crate) enum CredManagementCommand { EnumerateCredentialsBegin(RpIdHash), EnumerateCredentialsGetNextCredential, DeleteCredential(PublicKeyCredentialDescriptor), - UpdateUserInformation((PublicKeyCredentialDescriptor, User)), + UpdateUserInformation((PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity)), } impl CredManagementCommand { @@ -157,7 +158,7 @@ pub struct CredentialManagementResponse { /// Total number of RPs present on the authenticator pub total_rps: Option, /// User Information - pub user: Option, + pub user: Option, /// Credential ID pub credential_id: Option, /// Public key of the credential. @@ -182,7 +183,7 @@ pub struct CredentialRpListEntry { #[derive(Debug, PartialEq, Eq, Serialize)] pub struct CredentialListEntry { /// User Information - pub user: User, + pub user: PublicKeyCredentialUserEntity, /// Credential ID pub credential_id: PublicKeyCredentialDescriptor, /// Public key of the credential. diff --git a/third_party/rust/authenticator/src/ctap2/commands/get_assertion.rs b/third_party/rust/authenticator/src/ctap2/commands/get_assertion.rs index 12606456a0da..d1988f7b0712 100644 --- a/third_party/rust/authenticator/src/ctap2/commands/get_assertion.rs +++ b/third_party/rust/authenticator/src/ctap2/commands/get_assertion.rs @@ -13,7 +13,7 @@ use crate::ctap2::commands::get_next_assertion::GetNextAssertion; use crate::ctap2::commands::make_credentials::UserVerification; use crate::ctap2::server::{ AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, - PublicKeyCredentialDescriptor, RelyingPartyWrapper, RpIdHash, User, + PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, RelyingPartyWrapper, RpIdHash, UserVerificationRequirement, }; use crate::ctap2::utils::{read_be_u32, read_byte}; @@ -195,14 +195,10 @@ impl GetAssertion { // Handle extensions whose outputs are not encoded in the authenticator data. // 1. appId if let Some(app_id) = &self.extensions.app_id { - result.extensions.app_id = result - .assertions - .first() - .map(|assertion| { - assertion.auth_data.rp_id_hash - == RelyingPartyWrapper::from(app_id.as_str()).hash() - }) - .or(Some(false)); + result.extensions.app_id = Some( + result.assertion.auth_data.rp_id_hash + == RelyingPartyWrapper::from(app_id.as_str()).hash(), + ); } } } @@ -307,7 +303,7 @@ impl Serialize for GetAssertion { } impl RequestCtap1 for GetAssertion { - type Output = GetAssertionResult; + type Output = Vec; type AdditionalInfo = PublicKeyCredentialDescriptor; fn ctap1_format(&self) -> Result<(Vec, Self::AdditionalInfo), HIDError> { @@ -358,24 +354,27 @@ impl RequestCtap1 for GetAssertion { return Err(Retryable::Error(HIDError::ApduStatus(err))); } - let mut output = GetAssertionResult::from_ctap1(input, &self.rp.hash(), add_info) + let mut result = GetAssertionResult::from_ctap1(input, &self.rp.hash(), add_info) .map_err(|e| Retryable::Error(HIDError::Command(e)))?; - self.finalize_result(&mut output); - Ok(output) + self.finalize_result(&mut result); + // Although there's only one result, we return a vector for consistency with CTAP2. + Ok(vec![result]) } fn send_to_virtual_device( &self, dev: &mut Dev, ) -> Result { - let mut output = dev.get_assertion(self)?; - self.finalize_result(&mut output); - Ok(output) + let mut results = dev.get_assertion(self)?; + for result in results.iter_mut() { + self.finalize_result(result); + } + Ok(results) } } impl RequestCtap2 for GetAssertion { - type Output = GetAssertionResult; + type Output = Vec; fn command(&self) -> Command { Command::GetAssertion @@ -411,22 +410,27 @@ impl RequestCtap2 for GetAssertion { let assertion: GetAssertionResponse = from_slice(&input[1..]).map_err(CommandError::Deserializing)?; let number_of_credentials = assertion.number_of_credentials.unwrap_or(1); - let mut assertions = Vec::with_capacity(number_of_credentials); - assertions.push(assertion.into()); + + let mut results = Vec::with_capacity(number_of_credentials); + results.push(GetAssertionResult { + assertion: assertion.into(), + extensions: Default::default(), + }); let msg = GetNextAssertion; // We already have one, so skipping 0 for _ in 1..number_of_credentials { - let new_cred = dev.send_cbor(&msg)?; - assertions.push(new_cred.into()); + let assertion = dev.send_cbor(&msg)?; + results.push(GetAssertionResult { + assertion: assertion.into(), + extensions: Default::default(), + }); } - let mut output = GetAssertionResult { - assertions, - extensions: Default::default(), - }; - self.finalize_result(&mut output); - Ok(output) + for result in results.iter_mut() { + self.finalize_result(result); + } + Ok(results) } else { let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?; Err(CommandError::StatusCode(status, Some(data)).into()) @@ -437,9 +441,11 @@ impl RequestCtap2 for GetAssertion { &self, dev: &mut Dev, ) -> Result { - let mut output = dev.get_assertion(self)?; - self.finalize_result(&mut output); - Ok(output) + let mut results = dev.get_assertion(self)?; + for result in results.iter_mut() { + self.finalize_result(result); + } + Ok(results) } } @@ -449,7 +455,7 @@ pub struct Assertion { * mandatory in CTAP2.1 */ pub auth_data: AuthenticatorData, pub signature: Vec, - pub user: Option, + pub user: Option, } impl From for Assertion { @@ -465,7 +471,7 @@ impl From for Assertion { #[derive(Debug, PartialEq, Eq)] pub struct GetAssertionResult { - pub assertions: Vec, + pub assertion: Assertion, pub extensions: AuthenticationExtensionsClientOutputs, } @@ -501,30 +507,17 @@ impl GetAssertionResult { }; Ok(GetAssertionResult { - assertions: vec![assertion], + assertion, extensions: Default::default(), }) } - - pub fn u2f_sign_data(&self) -> Vec { - if let Some(first) = self.assertions.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.signature); - res - // first.signature.clone() - } else { - Vec::new() - } - } } pub struct GetAssertionResponse { pub credentials: Option, pub auth_data: AuthenticatorData, pub signature: Vec, - pub user: Option, + pub user: Option, pub number_of_credentials: Option, } @@ -628,7 +621,8 @@ pub mod test { do_credential_list_filtering_ctap1, do_credential_list_filtering_ctap2, }; use crate::ctap2::server::{ - PublicKeyCredentialDescriptor, RelyingParty, RelyingPartyWrapper, RpIdHash, Transport, User, + PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, RelyingParty, + RelyingPartyWrapper, RpIdHash, Transport, }; use crate::transport::device_selector::Device; use crate::transport::hid::HIDDevice; @@ -778,7 +772,7 @@ pub mod test { 0x47, 0xf1, 0x8d, 0xb4, 0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7, 0xa0, ], - user: Some(User { + user: Some(PublicKeyCredentialUserEntity { id: vec![ 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, @@ -790,10 +784,10 @@ pub mod test { auth_data: expected_auth_data, }; - let expected = GetAssertionResult { - assertions: vec![expected_assertion], + let expected = vec![GetAssertionResult { + assertion: expected_assertion, extensions: Default::default(), - }; + }]; let response = device.send_cbor(&assertion).unwrap(); assert_eq!(response, expected); } @@ -925,10 +919,10 @@ pub mod test { auth_data: expected_auth_data, }; - let expected = GetAssertionResult { - assertions: vec![expected_assertion], + let expected = vec![GetAssertionResult { + assertion: expected_assertion, extensions: Default::default(), - }; + }]; assert_eq!(response, expected); } @@ -1069,10 +1063,10 @@ pub mod test { auth_data: expected_auth_data, }; - let expected = GetAssertionResult { - assertions: vec![expected_assertion], + let expected = vec![GetAssertionResult { + assertion: expected_assertion, extensions: Default::default(), - }; + }]; assert_eq!(response, expected); } @@ -1337,7 +1331,7 @@ pub mod test { let resp = GetAssertionResult::from_ctap1(&sample, &rp_hash, &add_info) .expect("could not handle response"); assert_eq!( - resp.assertions[0].auth_data.flags, + resp.assertion.auth_data.flags, AuthenticatorDataFlags::USER_PRESENT | AuthenticatorDataFlags::RESERVED_1 ); } diff --git a/third_party/rust/authenticator/src/ctap2/commands/make_credentials.rs b/third_party/rust/authenticator/src/ctap2/commands/make_credentials.rs index 70f3d18c056e..bd925335c955 100644 --- a/third_party/rust/authenticator/src/ctap2/commands/make_credentials.rs +++ b/third_party/rust/authenticator/src/ctap2/commands/make_credentials.rs @@ -15,7 +15,8 @@ use crate::ctap2::client_data::ClientDataHash; use crate::ctap2::server::{ AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, CredentialProtectionPolicy, PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, - RelyingParty, RelyingPartyWrapper, RpIdHash, User, UserVerificationRequirement, + PublicKeyCredentialUserEntity, RelyingParty, RelyingPartyWrapper, RpIdHash, + UserVerificationRequirement, }; use crate::ctap2::utils::{read_byte, serde_parse_err}; use crate::errors::AuthenticatorError; @@ -260,7 +261,7 @@ pub struct MakeCredentials { pub client_data_hash: ClientDataHash, pub rp: RelyingPartyWrapper, // Note(baloo): If none -> ctap1 - pub user: Option, + pub user: Option, pub pub_cred_params: Vec, pub exclude_list: Vec, @@ -281,7 +282,7 @@ impl MakeCredentials { pub fn new( client_data_hash: ClientDataHash, rp: RelyingPartyWrapper, - user: Option, + user: Option, pub_cred_params: Vec, exclude_list: Vec, options: MakeCredentialsOptions, @@ -564,7 +565,7 @@ pub(crate) fn dummy_make_credentials_cmd() -> MakeCredentials { id: String::from("make.me.blink"), ..Default::default() }), - Some(User { + Some(PublicKeyCredentialUserEntity { id: vec![0], name: Some(String::from("make.me.blink")), ..Default::default() @@ -597,7 +598,8 @@ pub mod test { use crate::ctap2::commands::{RequestCtap1, RequestCtap2}; use crate::ctap2::server::RpIdHash; use crate::ctap2::server::{ - PublicKeyCredentialParameters, RelyingParty, RelyingPartyWrapper, User, + PublicKeyCredentialParameters, PublicKeyCredentialUserEntity, RelyingParty, + RelyingPartyWrapper, }; use crate::transport::device_selector::Device; use crate::transport::hid::HIDDevice; @@ -620,7 +622,7 @@ pub mod test { id: String::from("example.com"), name: Some(String::from("Acme")), }), - Some(User { + Some(PublicKeyCredentialUserEntity { id: base64::engine::general_purpose::URL_SAFE .decode("MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=") .unwrap(), @@ -677,7 +679,7 @@ pub mod test { id: String::from("example.com"), name: Some(String::from("Acme")), }), - Some(User { + Some(PublicKeyCredentialUserEntity { id: base64::engine::general_purpose::URL_SAFE .decode("MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=") .unwrap(), diff --git a/third_party/rust/authenticator/src/ctap2/mod.rs b/third_party/rust/authenticator/src/ctap2/mod.rs index 8999eb053246..db5afd5f1687 100644 --- a/third_party/rust/authenticator/src/ctap2/mod.rs +++ b/third_party/rust/authenticator/src/ctap2/mod.rs @@ -680,15 +680,34 @@ pub fn sign( debug!("{get_assertion:?} using {pin_uv_auth_result:?}"); debug!("------------------------------------------------------------------"); send_status(&status, crate::StatusUpdate::PresenceRequired); - let resp = dev.send_msg_cancellable(&get_assertion, alive); - match resp { - Ok(result) => { - callback.call(Ok(result)); - return true; - } + let mut results = match dev.send_msg_cancellable(&get_assertion, alive) { + Ok(results) => results, Err(e) => { handle_errors!(e, status, callback, pin_uv_auth_result, skip_uv); } + }; + if results.len() == 1 { + callback.call(Ok(results.swap_remove(0))); + return true; + } + let (tx, rx) = channel(); + let user_entities = results + .iter() + .filter_map(|x| x.assertion.user.clone()) + .collect(); + send_status( + &status, + crate::StatusUpdate::SelectResultNotice(tx, user_entities), + ); + match rx.recv() { + Ok(Some(index)) if index < results.len() => { + callback.call(Ok(results.swap_remove(index))); + return true; + } + _ => { + callback.call(Err(AuthenticatorError::CancelledByUser)); + return true; + } } } false @@ -886,8 +905,10 @@ pub(crate) fn bio_enrollment( Some(PinUvAuthResult::SuccessGetPinToken(t)) | Some(PinUvAuthResult::SuccessGetPinUvAuthTokenUsingUvWithPermissions(t)) | Some(PinUvAuthResult::SuccessGetPinUvAuthTokenUsingPinWithPermissions(t)) - if t.permissions - .contains(PinUvAuthTokenPermission::BioEnrollment) => + if !authinfo.versions.contains(&AuthenticatorVersion::FIDO_2_1) // Only 2.1 has a permission-system + || use_legacy_preview // Preview doesn't use permissions + || t.permissions + .contains(PinUvAuthTokenPermission::BioEnrollment) => { skip_puap = true; cached_puat = true; @@ -1154,7 +1175,10 @@ pub(crate) fn credential_management( Some(PinUvAuthResult::SuccessGetPinToken(t)) | Some(PinUvAuthResult::SuccessGetPinUvAuthTokenUsingUvWithPermissions(t)) | Some(PinUvAuthResult::SuccessGetPinUvAuthTokenUsingPinWithPermissions(t)) - if t.permissions == PinUvAuthTokenPermission::CredentialManagement => + if !authinfo.versions.contains(&AuthenticatorVersion::FIDO_2_1) // Only 2.1 has a permission-system + || use_legacy_preview // Preview doesn't use permissions + || t.permissions + .contains(PinUvAuthTokenPermission::CredentialManagement) => { skip_puap = true; cached_puat = true; @@ -1432,7 +1456,8 @@ pub(crate) fn configure_authenticator( Some(PinUvAuthResult::SuccessGetPinToken(t)) | Some(PinUvAuthResult::SuccessGetPinUvAuthTokenUsingUvWithPermissions(t)) | Some(PinUvAuthResult::SuccessGetPinUvAuthTokenUsingPinWithPermissions(t)) - if t.permissions == PinUvAuthTokenPermission::AuthenticatorConfiguration => + if t.permissions + .contains(PinUvAuthTokenPermission::AuthenticatorConfiguration) => { skip_puap = true; cached_puat = true; diff --git a/third_party/rust/authenticator/src/ctap2/preflight.rs b/third_party/rust/authenticator/src/ctap2/preflight.rs index 27ca4bf523d7..3ba1f98968dc 100644 --- a/third_party/rust/authenticator/src/ctap2/preflight.rs +++ b/third_party/rust/authenticator/src/ctap2/preflight.rs @@ -161,13 +161,12 @@ pub(crate) fn do_credential_list_filtering_ctap2( ); silent_assert.set_pin_uv_auth_param(pin_uv_auth_token.clone())?; match dev.send_msg(&silent_assert) { - Ok(response) => { + Ok(mut response) => { // This chunk contains a key_handle that is already known to the device. // Filter out all credentials the device returned. Those are valid. let credential_ids = response - .assertions - .iter() - .filter_map(|a| a.credentials.clone()) + .iter_mut() + .filter_map(|result| result.assertion.credentials.take()) .collect(); // Replace credential_id_list with the valid credentials final_list = credential_ids; diff --git a/third_party/rust/authenticator/src/ctap2/server.rs b/third_party/rust/authenticator/src/ctap2/server.rs index c022da807eff..1019bb110860 100644 --- a/third_party/rust/authenticator/src/ctap2/server.rs +++ b/third_party/rust/authenticator/src/ctap2/server.rs @@ -40,12 +40,9 @@ impl RpIdHash { } } +// NOTE: WebAuthn requires all fields and CTAP2 does not. #[derive(Debug, Serialize, Clone, Default, Deserialize, PartialEq, Eq)] pub struct RelyingParty { - // TODO(baloo): spec is wrong !!!!111 - // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#commands - // in the example "A PublicKeyCredentialRpEntity DOM object defined as follows:" - // inconsistent with https://w3c.github.io/webauthn/#sctn-rp-credential-params pub id: String, #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, @@ -94,9 +91,9 @@ impl RelyingPartyWrapper { } } -// TODO(baloo): should we rename this PublicKeyCredentialUserEntity ? +// NOTE: WebAuthn requires all fields and CTAP2 does not. #[derive(Debug, Serialize, Clone, Eq, PartialEq, Deserialize, Default)] -pub struct User { +pub struct PublicKeyCredentialUserEntity { #[serde(with = "serde_bytes")] pub id: Vec, pub name: Option, @@ -406,13 +403,13 @@ pub struct AuthenticationExtensionsClientOutputs { #[cfg(test)] mod test { use super::{ - COSEAlgorithm, PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, RelyingParty, - Transport, User, + COSEAlgorithm, PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, + PublicKeyCredentialUserEntity, RelyingParty, Transport, }; use serde_cbor::from_slice; - fn create_user() -> User { - User { + fn create_user() -> PublicKeyCredentialUserEntity { + PublicKeyCredentialUserEntity { id: vec![ 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, @@ -479,7 +476,7 @@ mod test { 0x69, 0x74, 0x68, // ... ]; let expected = create_user(); - let actual: User = from_slice(&input).unwrap(); + let actual: PublicKeyCredentialUserEntity = from_slice(&input).unwrap(); assert_eq!(expected, actual); } @@ -519,7 +516,7 @@ mod test { #[test] fn serialize_user_nodisplayname() { - let user = User { + let user = PublicKeyCredentialUserEntity { id: vec![ 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, diff --git a/third_party/rust/authenticator/src/status_update.rs b/third_party/rust/authenticator/src/status_update.rs index e3d0c624f280..c4a7fee75a93 100644 --- a/third_party/rust/authenticator/src/status_update.rs +++ b/third_party/rust/authenticator/src/status_update.rs @@ -7,7 +7,7 @@ use crate::{ get_info::AuthenticatorInfo, PinUvAuthResult, }, - server::{PublicKeyCredentialDescriptor, User}, + server::{PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity}, }, BioEnrollmentResult, CredentialManagementResult, }; @@ -18,7 +18,7 @@ use std::sync::mpsc::Sender; pub enum CredManagementCmd { GetCredentials, DeleteCredential(PublicKeyCredentialDescriptor), - UpdateUserInformation(PublicKeyCredentialDescriptor, User), + UpdateUserInformation(PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity), } #[derive(Debug, Deserialize, DeriveSer)] @@ -105,6 +105,8 @@ pub enum StatusUpdate { SelectDeviceNotice, /// Sent when a token was selected for interactive management InteractiveManagement(InteractiveUpdate), + /// Sent when a token returns multiple results for a getAssertion request + SelectResultNotice(Sender>, Vec), } pub(crate) fn send_status(status: &Sender, msg: StatusUpdate) { diff --git a/third_party/rust/authenticator/src/transport/mod.rs b/third_party/rust/authenticator/src/transport/mod.rs index 551260577fa9..52eea2ab2fe9 100644 --- a/third_party/rust/authenticator/src/transport/mod.rs +++ b/third_party/rust/authenticator/src/transport/mod.rs @@ -345,7 +345,7 @@ where pub trait VirtualFidoDevice: FidoDevice { fn check_key_handle(&self, req: &CheckKeyHandle) -> Result<(), HIDError>; fn client_pin(&self, req: &ClientPIN) -> Result; - fn get_assertion(&self, req: &GetAssertion) -> Result; + fn get_assertion(&self, req: &GetAssertion) -> Result, HIDError>; fn get_info(&self) -> Result; fn get_version(&self, req: &GetVersion) -> Result; fn make_credentials(&self, req: &MakeCredentials) -> Result;