mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1536155 - implement getTransports() for AuthenticatorAttestationResponse. r=geckoview-reviewers,webidl,keeler,smaug,owlish
Differential Revision: https://phabricator.services.mozilla.com/D185225
This commit is contained in:
parent
6773f8457a
commit
1a32e9ea0f
@ -261,9 +261,9 @@ void AndroidWebAuthnTokenManager::HandleRegisterResult(
|
||||
[self = RefPtr<AndroidWebAuthnTokenManager>(this),
|
||||
aResult = std::move(aResult)]() {
|
||||
nsTArray<WebAuthnExtensionResult> extensions;
|
||||
WebAuthnMakeCredentialResult result(aResult.mClientDataJSON,
|
||||
aResult.mAttObj,
|
||||
aResult.mKeyHandle, extensions);
|
||||
WebAuthnMakeCredentialResult result(
|
||||
aResult.mClientDataJSON, aResult.mAttObj, aResult.mKeyHandle,
|
||||
aResult.mTransports, extensions);
|
||||
self->mRegisterPromise.Resolve(std::move(result), __func__);
|
||||
}));
|
||||
}
|
||||
@ -411,6 +411,11 @@ AndroidWebAuthnResult::AndroidWebAuthnResult(
|
||||
reinterpret_cast<uint8_t*>(
|
||||
aResponse->AttestationObject()->GetElements().Elements()),
|
||||
aResponse->AttestationObject()->Length());
|
||||
auto transports = aResponse->Transports();
|
||||
for (size_t i = 0; i < transports->Length(); i++) {
|
||||
mTransports.AppendElement(
|
||||
jni::String::LocalRef(transports->GetElement(i))->ToString());
|
||||
}
|
||||
}
|
||||
|
||||
AndroidWebAuthnResult::AndroidWebAuthnResult(
|
||||
|
@ -84,6 +84,7 @@ class AndroidWebAuthnResult {
|
||||
|
||||
// Attestation-only
|
||||
nsTArray<uint8_t> mAttObj;
|
||||
nsTArray<nsString> mTransports;
|
||||
|
||||
// Attestations and assertions
|
||||
nsTArray<uint8_t> mKeyHandle;
|
||||
|
@ -64,13 +64,19 @@ void AuthenticatorAttestationResponse::GetAttestationObject(
|
||||
aValue.set(mAttestationObjectCachedObj);
|
||||
}
|
||||
|
||||
nsresult AuthenticatorAttestationResponse::SetAttestationObject(
|
||||
void AuthenticatorAttestationResponse::SetAttestationObject(
|
||||
const nsTArray<uint8_t>& aBuffer) {
|
||||
if (!mAttestationObject.Assign(aBuffer, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mAttestationObject.Assign(aBuffer);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
void AuthenticatorAttestationResponse::GetTransports(
|
||||
nsTArray<nsString>& aTransports) {
|
||||
aTransports.Assign(mTransports);
|
||||
}
|
||||
|
||||
void AuthenticatorAttestationResponse::SetTransports(
|
||||
const nsTArray<nsString>& aTransports) {
|
||||
mTransports.Assign(aTransports);
|
||||
}
|
||||
|
||||
void AuthenticatorAttestationResponse::GetAuthenticatorData(
|
||||
|
@ -35,7 +35,11 @@ class AuthenticatorAttestationResponse final : public AuthenticatorResponse {
|
||||
void GetAttestationObject(JSContext* aCx, JS::MutableHandle<JSObject*> aValue,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult SetAttestationObject(const nsTArray<uint8_t>& aBuffer);
|
||||
void SetAttestationObject(const nsTArray<uint8_t>& aBuffer);
|
||||
|
||||
void GetTransports(nsTArray<nsString>& aTransports);
|
||||
|
||||
void SetTransports(const nsTArray<nsString>& aTransports);
|
||||
|
||||
void GetAuthenticatorData(JSContext* aCx, JS::MutableHandle<JSObject*> aValue,
|
||||
ErrorResult& aRv);
|
||||
@ -49,6 +53,7 @@ class AuthenticatorAttestationResponse final : public AuthenticatorResponse {
|
||||
nsTArray<uint8_t> mAttestationObject;
|
||||
nsCOMPtr<nsIWebAuthnAttObj> mAttestationObjectParsed;
|
||||
JS::Heap<JSObject*> mAttestationObjectCachedObj;
|
||||
nsTArray<nsString> mTransports;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
@ -92,6 +92,7 @@ struct WebAuthnMakeCredentialResult {
|
||||
nsCString ClientDataJSON;
|
||||
uint8_t[] AttestationObject;
|
||||
uint8_t[] KeyHandle;
|
||||
nsString[] Transports;
|
||||
WebAuthnExtensionResult[] Extensions;
|
||||
};
|
||||
|
||||
|
@ -409,9 +409,16 @@ void WebAuthnController::RunFinishRegister(
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsString> transports;
|
||||
rv = aResult->GetTransports(transports);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR, true);
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<WebAuthnExtensionResult> extensions;
|
||||
WebAuthnMakeCredentialResult result(clientDataJson, attObj, credentialId,
|
||||
extensions);
|
||||
transports, extensions);
|
||||
|
||||
Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
|
||||
u"CTAPRegisterFinish"_ns, 1);
|
||||
|
@ -711,6 +711,7 @@ void WebAuthnManager::FinishMakeCredential(
|
||||
new AuthenticatorAttestationResponse(mParent);
|
||||
attestation->SetClientDataJSON(aResult.ClientDataJSON());
|
||||
attestation->SetAttestationObject(aResult.AttestationObject());
|
||||
attestation->SetTransports(aResult.Transports());
|
||||
|
||||
RefPtr<PublicKeyCredential> credential = new PublicKeyCredential(mParent);
|
||||
credential->SetId(NS_ConvertASCIItoUTF16(keyHandleBase64Url));
|
||||
|
@ -453,7 +453,6 @@ void WinWebAuthnManager::Register(
|
||||
}
|
||||
|
||||
nsTArray<WebAuthnExtensionResult> extensions;
|
||||
|
||||
if (pWebAuthNCredentialAttestation->dwVersion >=
|
||||
WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_2) {
|
||||
PCWEBAUTHN_EXTENSIONS pExtensionList =
|
||||
@ -478,8 +477,29 @@ void WinWebAuthnManager::Register(
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsString> transports;
|
||||
if (pWebAuthNCredentialAttestation->dwVersion >=
|
||||
WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3) {
|
||||
if (pWebAuthNCredentialAttestation->dwUsedTransport &
|
||||
WEBAUTHN_CTAP_TRANSPORT_USB) {
|
||||
transports.AppendElement(u"usb"_ns);
|
||||
}
|
||||
if (pWebAuthNCredentialAttestation->dwUsedTransport &
|
||||
WEBAUTHN_CTAP_TRANSPORT_NFC) {
|
||||
transports.AppendElement(u"nfc"_ns);
|
||||
}
|
||||
if (pWebAuthNCredentialAttestation->dwUsedTransport &
|
||||
WEBAUTHN_CTAP_TRANSPORT_BLE) {
|
||||
transports.AppendElement(u"ble"_ns);
|
||||
}
|
||||
if (pWebAuthNCredentialAttestation->dwUsedTransport &
|
||||
WEBAUTHN_CTAP_TRANSPORT_INTERNAL) {
|
||||
transports.AppendElement(u"internal"_ns);
|
||||
}
|
||||
}
|
||||
|
||||
WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObject,
|
||||
credentialId, extensions);
|
||||
credentialId, transports, extensions);
|
||||
|
||||
Unused << mTransactionParent->SendConfirmRegister(aTransactionId, result);
|
||||
ClearTransaction();
|
||||
|
@ -34,7 +34,7 @@ use serde_cbor;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::mpsc::{channel, Receiver, RecvError, Sender};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use thin_vec::ThinVec;
|
||||
use thin_vec::{thin_vec, ThinVec};
|
||||
use xpcom::interfaces::{
|
||||
nsICredentialParameters, nsICtapRegisterArgs, nsICtapRegisterResult, nsICtapSignArgs,
|
||||
nsICtapSignResult, nsIWebAuthnAttObj, nsIWebAuthnController, nsIWebAuthnTransport,
|
||||
@ -114,6 +114,18 @@ impl CtapRegisterResult {
|
||||
Err(NS_ERROR_FAILURE)
|
||||
}
|
||||
|
||||
xpcom_method!(get_transports => GetTransports() -> ThinVec<nsString>);
|
||||
fn get_transports(&self) -> Result<ThinVec<nsString>, nsresult> {
|
||||
if self.result.is_err() {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
// The list that we return here might be included in a future GetAssertion request as a
|
||||
// hint as to which transports to try. We currently only support the USB transport. If
|
||||
// that changes, we will need a mechanism to track which transport was used for a
|
||||
// request.
|
||||
Ok(thin_vec![nsString::from("usb")])
|
||||
}
|
||||
|
||||
xpcom_method!(get_status => GetStatus() -> nsresult);
|
||||
fn get_status(&self) -> Result<nsresult, nsresult> {
|
||||
match &self.result {
|
||||
|
@ -119,12 +119,7 @@ interface nsICtapRegisterResult : nsISupports {
|
||||
// attestationObject.
|
||||
readonly attribute Array<octet> credentialId;
|
||||
|
||||
// Bug 1536155
|
||||
// readonly attribute Array<AString> transports;
|
||||
|
||||
// Bug 1816520
|
||||
// readonly attribute Array<octet> publicKey
|
||||
// readonly attribute COSEAlgorithmIdentifier publicKeyAlgorithm;
|
||||
readonly attribute Array<AString> transports;
|
||||
|
||||
// bug 1593571
|
||||
// readonly attribute bool hmacCreateSecret;
|
||||
|
@ -34,9 +34,10 @@
|
||||
ok(aResult.toString().startsWith("InvalidStateError"), "Expecting a InvalidStateError, got " + aResult);
|
||||
}
|
||||
|
||||
// Store the credential of the first successful make credential
|
||||
// operation so we can use it to get assertions later.
|
||||
// Store the credential and transports of the first successful make credential
|
||||
// operation so we can use them to get assertions later.
|
||||
let gCredential;
|
||||
let gTransports;
|
||||
|
||||
// Start a new MakeCredential() request.
|
||||
function requestMakeCredential(excludeCredentials) {
|
||||
@ -69,15 +70,25 @@
|
||||
// Make a credential.
|
||||
await requestMakeCredential([])
|
||||
// Save the credential for later.
|
||||
.then(res => gCredential = res.rawId)
|
||||
.then(res => {
|
||||
gCredential = res.rawId;
|
||||
gTransports = res.response.getTransports();
|
||||
})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
|
||||
// gTransports should be "a sequence of zero or more unique DOMStrings in lexicographical order."
|
||||
for (let i = 0; i < gTransports.length - 1; i++) {
|
||||
if (gTransports[i] >= gTransports[i+1]) {
|
||||
ok(false, "getTransports() should return a sorted list");
|
||||
}
|
||||
}
|
||||
|
||||
// Pass a random credential to exclude.
|
||||
await requestMakeCredential([{
|
||||
type: "public-key",
|
||||
id: crypto.getRandomValues(new Uint8Array(16)),
|
||||
transports: ["usb"],
|
||||
transports: gTransports,
|
||||
}]).then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
|
||||
@ -117,7 +128,7 @@
|
||||
await requestGetAssertion([{
|
||||
type: "public-key",
|
||||
id: gCredential,
|
||||
transports: ["usb"],
|
||||
transports: gTransports,
|
||||
}]).then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
|
||||
|
@ -34,6 +34,7 @@ interface AuthenticatorResponse {
|
||||
Exposed=Window]
|
||||
interface AuthenticatorAttestationResponse : AuthenticatorResponse {
|
||||
[SameObject, Throws] readonly attribute ArrayBuffer attestationObject;
|
||||
sequence<DOMString> getTransports();
|
||||
[Throws] ArrayBuffer getAuthenticatorData();
|
||||
[Throws] ArrayBuffer? getPublicKey();
|
||||
[Throws] COSEAlgorithmIdentifier getPublicKeyAlgorithm();
|
||||
|
@ -124,12 +124,17 @@ import org.mozilla.gecko.util.GeckoBundle;
|
||||
public final byte[] clientDataJson;
|
||||
public final byte[] keyHandle;
|
||||
public final byte[] attestationObject;
|
||||
public final String[] transports;
|
||||
|
||||
public MakeCredentialResponse(
|
||||
final byte[] clientDataJson, final byte[] keyHandle, final byte[] attestationObject) {
|
||||
final byte[] clientDataJson,
|
||||
final byte[] keyHandle,
|
||||
final byte[] attestationObject,
|
||||
final String[] transports) {
|
||||
this.clientDataJson = clientDataJson;
|
||||
this.keyHandle = keyHandle;
|
||||
this.attestationObject = attestationObject;
|
||||
this.transports = transports;
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,11 +313,15 @@ import org.mozilla.gecko.util.GeckoBundle;
|
||||
+ Base64.encodeToString(
|
||||
responseData.getAttestationObject(), Base64.DEFAULT));
|
||||
|
||||
Log.d(
|
||||
LOGTAG, "transports: " + String.join(", ", responseData.getTransports()));
|
||||
|
||||
result.complete(
|
||||
new WebAuthnTokenManager.MakeCredentialResponse(
|
||||
responseData.getClientDataJSON(),
|
||||
responseData.getKeyHandle(),
|
||||
responseData.getAttestationObject()));
|
||||
responseData.getAttestationObject(),
|
||||
responseData.getTransports()));
|
||||
}
|
||||
},
|
||||
e -> {
|
||||
|
Loading…
Reference in New Issue
Block a user