Bug 1795020 - support WebAuthn large blob extension. r=webidl,keeler,smaug
Differential Revision: https://phabricator.services.mozilla.com/D244974
This commit is contained in:
@@ -238,13 +238,16 @@ class API_AVAILABLE(macos(13.3)) MacOSWebAuthnService final
|
||||
void FinishMakeCredential(const nsTArray<uint8_t>& aRawAttestationObject,
|
||||
const nsTArray<uint8_t>& aCredentialId,
|
||||
const nsTArray<nsString>& aTransports,
|
||||
const Maybe<nsString>& aAuthenticatorAttachment);
|
||||
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||
const Maybe<bool>& aLargeBlobSupported);
|
||||
|
||||
void FinishGetAssertion(const nsTArray<uint8_t>& aCredentialId,
|
||||
const nsTArray<uint8_t>& aSignature,
|
||||
const nsTArray<uint8_t>& aAuthenticatorData,
|
||||
const nsTArray<uint8_t>& aUserHandle,
|
||||
const Maybe<nsString>& aAuthenticatorAttachment);
|
||||
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||
const Maybe<nsTArray<uint8_t>>& aLargeBlobValue,
|
||||
const Maybe<bool>& aLargeBlobWritten);
|
||||
void ReleasePlatformResources();
|
||||
void AbortTransaction(nsresult aError);
|
||||
|
||||
@@ -314,18 +317,17 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
||||
nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID));
|
||||
nsTArray<nsString> transports;
|
||||
mozilla::Maybe<nsString> authenticatorAttachment;
|
||||
mozilla::Maybe<bool> largeBlobSupported;
|
||||
if ([credential isKindOfClass:
|
||||
[ASAuthorizationPlatformPublicKeyCredentialRegistration
|
||||
class]]) {
|
||||
transports.AppendElement(u"hybrid"_ns);
|
||||
transports.AppendElement(u"internal"_ns);
|
||||
#if defined(MAC_OS_VERSION_13_5) && \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_5
|
||||
if (__builtin_available(macos 13.5, *)) {
|
||||
ASAuthorizationPlatformPublicKeyCredentialRegistration*
|
||||
platformCredential =
|
||||
(ASAuthorizationPlatformPublicKeyCredentialRegistration*)
|
||||
credential;
|
||||
if (__builtin_available(macos 13.5, *)) {
|
||||
switch (platformCredential.attachment) {
|
||||
case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
|
||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||
@@ -337,7 +339,11 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (__builtin_available(macos 14.0, *)) {
|
||||
if (platformCredential.largeBlob) {
|
||||
largeBlobSupported.emplace(platformCredential.largeBlob.isSupported);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The platform didn't tell us what transport was used, but we know it
|
||||
// wasn't the internal transport. The transport response is not signed by
|
||||
@@ -348,7 +354,8 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||
}
|
||||
mCallback->FinishMakeCredential(rawAttestationObject, credentialId,
|
||||
transports, authenticatorAttachment);
|
||||
transports, authenticatorAttachment,
|
||||
largeBlobSupported);
|
||||
} else if ([authorization.credential
|
||||
conformsToProtocol:
|
||||
@protocol(ASAuthorizationPublicKeyCredentialAssertion)]) {
|
||||
@@ -364,16 +371,14 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
||||
NSDataToArray(credential.rawAuthenticatorData));
|
||||
nsTArray<uint8_t> userHandle(NSDataToArray(credential.userID));
|
||||
mozilla::Maybe<nsString> authenticatorAttachment;
|
||||
mozilla::Maybe<nsTArray<uint8_t>> largeBlobValue;
|
||||
mozilla::Maybe<bool> largeBlobWritten;
|
||||
if ([credential
|
||||
isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialAssertion
|
||||
class]]) {
|
||||
#if defined(MAC_OS_VERSION_13_5) && \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_5
|
||||
ASAuthorizationPlatformPublicKeyCredentialAssertion* platformCredential =
|
||||
(ASAuthorizationPlatformPublicKeyCredentialAssertion*)credential;
|
||||
if (__builtin_available(macos 13.5, *)) {
|
||||
ASAuthorizationPlatformPublicKeyCredentialAssertion*
|
||||
platformCredential =
|
||||
(ASAuthorizationPlatformPublicKeyCredentialAssertion*)
|
||||
credential;
|
||||
switch (platformCredential.attachment) {
|
||||
case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
|
||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||
@@ -385,12 +390,22 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (__builtin_available(macos 14.0, *)) {
|
||||
if (platformCredential.largeBlob) {
|
||||
if (platformCredential.largeBlob.readData) {
|
||||
largeBlobValue.emplace(
|
||||
NSDataToArray(platformCredential.largeBlob.readData));
|
||||
} else {
|
||||
largeBlobWritten.emplace(platformCredential.largeBlob.didWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||
}
|
||||
mCallback->FinishGetAssertion(credentialId, signature, rawAuthenticatorData,
|
||||
userHandle, authenticatorAttachment);
|
||||
userHandle, authenticatorAttachment,
|
||||
largeBlobValue, largeBlobWritten);
|
||||
} else {
|
||||
MOZ_LOG(
|
||||
gMacOSWebAuthnServiceLog, mozilla::LogLevel::Error,
|
||||
@@ -722,6 +737,27 @@ MacOSWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
crossPlatformRegistrationRequest.userVerificationPreference =
|
||||
*userVerificationPreference;
|
||||
}
|
||||
|
||||
if (__builtin_available(macos 14.0, *)) {
|
||||
bool largeBlobSupportRequired;
|
||||
nsresult rv =
|
||||
aArgs->GetLargeBlobSupportRequired(&largeBlobSupportRequired);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
self->mRegisterPromise->Reject(rv);
|
||||
return;
|
||||
}
|
||||
ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement
|
||||
largeBlobRequirement =
|
||||
largeBlobSupportRequired
|
||||
? ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirementRequired
|
||||
: ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirementPreferred;
|
||||
platformRegistrationRequest.largeBlob =
|
||||
[[ASAuthorizationPublicKeyCredentialLargeBlobRegistrationInput
|
||||
alloc] initWithSupportRequirement:largeBlobRequirement];
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> clientDataHash;
|
||||
nsresult rv = aArgs->GetClientDataHash(clientDataHash);
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -800,7 +836,8 @@ void MacOSWebAuthnService::FinishMakeCredential(
|
||||
const nsTArray<uint8_t>& aRawAttestationObject,
|
||||
const nsTArray<uint8_t>& aCredentialId,
|
||||
const nsTArray<nsString>& aTransports,
|
||||
const Maybe<nsString>& aAuthenticatorAttachment) {
|
||||
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||
const Maybe<bool>& aLargeBlobSupported) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mRegisterPromise) {
|
||||
return;
|
||||
@@ -808,7 +845,7 @@ void MacOSWebAuthnService::FinishMakeCredential(
|
||||
|
||||
RefPtr<WebAuthnRegisterResult> result(new WebAuthnRegisterResult(
|
||||
aRawAttestationObject, Nothing(), aCredentialId, aTransports,
|
||||
aAuthenticatorAttachment));
|
||||
aAuthenticatorAttachment, aLargeBlobSupported));
|
||||
Unused << mRegisterPromise->Resolve(result);
|
||||
mRegisterPromise = nullptr;
|
||||
}
|
||||
@@ -1037,6 +1074,47 @@ void MacOSWebAuthnService::DoGetAssertion(
|
||||
crossPlatformAssertionRequest.userVerificationPreference =
|
||||
*userVerificationPreference;
|
||||
}
|
||||
|
||||
if (__builtin_available(macos 14.0, *)) {
|
||||
nsTArray<uint8_t> largeBlobWrite;
|
||||
bool largeBlobRead;
|
||||
nsresult rv = aArgs->GetLargeBlobRead(&largeBlobRead);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
self->mSignPromise->Reject(rv);
|
||||
return;
|
||||
}
|
||||
if (largeBlobRead) {
|
||||
platformAssertionRequest
|
||||
.largeBlob = [[ASAuthorizationPublicKeyCredentialLargeBlobAssertionInput
|
||||
alloc]
|
||||
initWithOperation:
|
||||
ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperationRead];
|
||||
} else {
|
||||
rv = aArgs->GetLargeBlobWrite(largeBlobWrite);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
self->mSignPromise->Reject(rv);
|
||||
return;
|
||||
}
|
||||
ASAuthorizationPublicKeyCredentialLargeBlobAssertionInput*
|
||||
largeBlobAssertionInput =
|
||||
[[ASAuthorizationPublicKeyCredentialLargeBlobAssertionInput
|
||||
alloc]
|
||||
initWithOperation:
|
||||
ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperationWrite];
|
||||
// We need to fully form the input before assigning it to
|
||||
// platformAssertionRequest.largeBlob. See
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=276961
|
||||
largeBlobAssertionInput.dataToWrite =
|
||||
[NSData dataWithBytes:largeBlobWrite.Elements()
|
||||
length:largeBlobWrite.Length()];
|
||||
platformAssertionRequest.largeBlob = largeBlobAssertionInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> clientDataHash;
|
||||
nsresult rv = aArgs->GetClientDataHash(clientDataHash);
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -1057,7 +1135,9 @@ void MacOSWebAuthnService::FinishGetAssertion(
|
||||
const nsTArray<uint8_t>& aCredentialId, const nsTArray<uint8_t>& aSignature,
|
||||
const nsTArray<uint8_t>& aAuthenticatorData,
|
||||
const nsTArray<uint8_t>& aUserHandle,
|
||||
const Maybe<nsString>& aAuthenticatorAttachment) {
|
||||
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||
const Maybe<nsTArray<uint8_t>>& aLargeBlobValue,
|
||||
const Maybe<bool>& aLargeBlobWritten) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mSignPromise) {
|
||||
return;
|
||||
@@ -1065,7 +1145,7 @@ void MacOSWebAuthnService::FinishGetAssertion(
|
||||
|
||||
RefPtr<WebAuthnSignResult> result(new WebAuthnSignResult(
|
||||
aAuthenticatorData, Nothing(), aCredentialId, aSignature, aUserHandle,
|
||||
aAuthenticatorAttachment));
|
||||
aAuthenticatorAttachment, aLargeBlobValue, aLargeBlobWritten));
|
||||
Unused << mSignPromise->Resolve(result);
|
||||
mSignPromise = nullptr;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,14 @@ struct WebAuthnExtensionHmacSecret {
|
||||
bool hmacCreateSecret;
|
||||
};
|
||||
|
||||
struct WebAuthnExtensionLargeBlob {
|
||||
bool? flag; // In registrations this indicates whether large blob support is required.
|
||||
// In authentications this indicates whether this is a request to read the
|
||||
// a blob or whether it is a request to write one.
|
||||
uint8_t[] write; // Authentication only. The value to be written when `flag` is
|
||||
// present and false.
|
||||
};
|
||||
|
||||
struct WebAuthnExtensionMinPinLength {
|
||||
bool minPinLength;
|
||||
};
|
||||
@@ -64,6 +72,7 @@ struct WebAuthnExtensionPrfEvalByCredentialEntry {
|
||||
union WebAuthnExtension {
|
||||
WebAuthnExtensionCredProps;
|
||||
WebAuthnExtensionHmacSecret;
|
||||
WebAuthnExtensionLargeBlob;
|
||||
WebAuthnExtensionMinPinLength;
|
||||
WebAuthnExtensionPrf;
|
||||
};
|
||||
@@ -80,6 +89,13 @@ struct WebAuthnExtensionResultHmacSecret {
|
||||
bool hmacCreateSecret;
|
||||
};
|
||||
|
||||
struct WebAuthnExtensionResultLargeBlob {
|
||||
bool flag; // In registration this indicates support. In authentication
|
||||
// it indicates whether this is a read return or a write return.
|
||||
uint8_t[] blob; // Authentication only. Read return.
|
||||
bool written; // Authentication only. Write return
|
||||
};
|
||||
|
||||
struct WebAuthnExtensionResultPrf {
|
||||
bool? enabled;
|
||||
WebAuthnExtensionPrfValues? results;
|
||||
@@ -89,6 +105,7 @@ union WebAuthnExtensionResult {
|
||||
WebAuthnExtensionResultAppId;
|
||||
WebAuthnExtensionResultCredProps;
|
||||
WebAuthnExtensionResultHmacSecret;
|
||||
WebAuthnExtensionResultLargeBlob;
|
||||
WebAuthnExtensionResultPrf;
|
||||
};
|
||||
|
||||
|
||||
@@ -197,6 +197,14 @@ already_AddRefed<Promise> PublicKeyCredential::GetClientCapabilities(
|
||||
entry->mKey = u"extension:hmacCreateSecret"_ns;
|
||||
entry->mValue = true;
|
||||
|
||||
entry = capabilities.Entries().AppendElement();
|
||||
entry->mKey = u"extension:largeBlob"_ns;
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||
entry->mValue = true;
|
||||
#else
|
||||
entry->mValue = false;
|
||||
#endif
|
||||
|
||||
entry = capabilities.Entries().AppendElement();
|
||||
entry->mKey = u"extension:minPinLength"_ns;
|
||||
entry->mValue = true;
|
||||
@@ -282,6 +290,26 @@ void PublicKeyCredential::GetClientExtensionResults(
|
||||
mClientExtensionOutputs.mHmacCreateSecret.Value());
|
||||
}
|
||||
|
||||
if (mClientExtensionOutputs.mLargeBlob.WasPassed()) {
|
||||
const AuthenticationExtensionsLargeBlobOutputs& src =
|
||||
mClientExtensionOutputs.mLargeBlob.Value();
|
||||
AuthenticationExtensionsLargeBlobOutputs& dest =
|
||||
aResult.mLargeBlob.Construct();
|
||||
|
||||
if (src.mSupported.WasPassed()) {
|
||||
dest.mSupported.Construct(src.mSupported.Value());
|
||||
}
|
||||
|
||||
if (src.mWritten.WasPassed()) {
|
||||
dest.mWritten.Construct(src.mWritten.Value());
|
||||
}
|
||||
|
||||
if (mLargeBlobValue.isSome()) {
|
||||
dest.mBlob.Construct().Init(
|
||||
TypedArrayCreator<ArrayBuffer>(mLargeBlobValue.ref()).Create(cx));
|
||||
}
|
||||
}
|
||||
|
||||
if (mClientExtensionOutputs.mPrf.WasPassed()) {
|
||||
AuthenticationExtensionsPRFOutputs& dest = aResult.mPrf.Construct();
|
||||
|
||||
@@ -336,6 +364,15 @@ void PublicKeyCredential::ToJSON(JSContext* aCx,
|
||||
mClientExtensionOutputs.mPrf.Value().mEnabled.Value());
|
||||
}
|
||||
}
|
||||
if (mClientExtensionOutputs.mLargeBlob.WasPassed()) {
|
||||
const AuthenticationExtensionsLargeBlobOutputs& src =
|
||||
mClientExtensionOutputs.mLargeBlob.Value();
|
||||
AuthenticationExtensionsLargeBlobOutputsJSON& dest =
|
||||
json.mClientExtensionResults.mLargeBlob.Construct();
|
||||
if (src.mSupported.WasPassed()) {
|
||||
dest.mSupported.Construct(src.mSupported.Value());
|
||||
}
|
||||
}
|
||||
json.mType.Assign(u"public-key"_ns);
|
||||
if (!ToJSValue(aCx, json, &value)) {
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
@@ -360,6 +397,27 @@ void PublicKeyCredential::ToJSON(JSContext* aCx,
|
||||
if (mClientExtensionOutputs.mPrf.WasPassed()) {
|
||||
json.mClientExtensionResults.mPrf.Construct();
|
||||
}
|
||||
if (mClientExtensionOutputs.mLargeBlob.WasPassed()) {
|
||||
const AuthenticationExtensionsLargeBlobOutputs& src =
|
||||
mClientExtensionOutputs.mLargeBlob.Value();
|
||||
AuthenticationExtensionsLargeBlobOutputsJSON& dest =
|
||||
json.mClientExtensionResults.mLargeBlob.Construct();
|
||||
if (src.mWritten.WasPassed()) {
|
||||
dest.mWritten.Construct(src.mWritten.Value());
|
||||
}
|
||||
if (mLargeBlobValue.isSome()) {
|
||||
nsCString largeBlobB64;
|
||||
nsresult rv = mozilla::Base64URLEncode(
|
||||
mLargeBlobValue->Length(), mLargeBlobValue->Elements(),
|
||||
Base64URLEncodePaddingPolicy::Omit, largeBlobB64);
|
||||
if (NS_FAILED(rv)) {
|
||||
aError.ThrowEncodingError(
|
||||
"could not encode large blob data as urlsafe base64");
|
||||
return;
|
||||
}
|
||||
dest.mBlob.Construct(NS_ConvertUTF8toUTF16(largeBlobB64));
|
||||
}
|
||||
}
|
||||
json.mType.Assign(u"public-key"_ns);
|
||||
if (!ToJSValue(aCx, json, &value)) {
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
@@ -390,6 +448,28 @@ void PublicKeyCredential::SetClientExtensionResultHmacSecret(
|
||||
mClientExtensionOutputs.mHmacCreateSecret.Value() = aHmacCreateSecret;
|
||||
}
|
||||
|
||||
void PublicKeyCredential::InitClientExtensionResultLargeBlob() {
|
||||
mClientExtensionOutputs.mLargeBlob.Construct();
|
||||
}
|
||||
|
||||
void PublicKeyCredential::SetClientExtensionResultLargeBlobSupported(
|
||||
bool aLargeBlobSupported) {
|
||||
mClientExtensionOutputs.mLargeBlob.Value().mSupported.Construct(
|
||||
aLargeBlobSupported);
|
||||
}
|
||||
|
||||
void PublicKeyCredential::SetClientExtensionResultLargeBlobValue(
|
||||
const nsTArray<uint8_t>& aLargeBlobValue) {
|
||||
mLargeBlobValue.emplace(aLargeBlobValue.Length());
|
||||
mLargeBlobValue->Assign(aLargeBlobValue);
|
||||
}
|
||||
|
||||
void PublicKeyCredential::SetClientExtensionResultLargeBlobWritten(
|
||||
bool aLargeBlobWritten) {
|
||||
mClientExtensionOutputs.mLargeBlob.Value().mWritten.Construct(
|
||||
aLargeBlobWritten);
|
||||
}
|
||||
|
||||
void PublicKeyCredential::InitClientExtensionResultPrf() {
|
||||
mClientExtensionOutputs.mPrf.Construct();
|
||||
}
|
||||
@@ -474,6 +554,26 @@ bool DecodeAuthenticationExtensionsPRFInputsJSON(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeAuthenticationExtensionsLargeBlobInputsJSON(
|
||||
GlobalObject& aGlobal,
|
||||
const AuthenticationExtensionsLargeBlobInputsJSON& aInputsJSON,
|
||||
AuthenticationExtensionsLargeBlobInputs& aInputs, ErrorResult& aRv) {
|
||||
if (aInputsJSON.mSupport.WasPassed()) {
|
||||
aInputs.mSupport.Construct(aInputsJSON.mSupport.Value());
|
||||
}
|
||||
if (aInputsJSON.mRead.WasPassed()) {
|
||||
aInputs.mRead.Construct(aInputsJSON.mRead.Value());
|
||||
}
|
||||
if (aInputsJSON.mWrite.WasPassed()) {
|
||||
if (!Base64DecodeToArrayBuffer(
|
||||
aGlobal, aInputsJSON.mWrite.Value(),
|
||||
aInputs.mWrite.Construct().SetAsArrayBuffer(), aRv)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PublicKeyCredential::ParseCreationOptionsFromJSON(
|
||||
GlobalObject& aGlobal,
|
||||
const PublicKeyCredentialCreationOptionsJSON& aOptions,
|
||||
@@ -547,6 +647,18 @@ void PublicKeyCredential::ParseCreationOptionsFromJSON(
|
||||
aResult.mExtensions.mMinPinLength.Construct(
|
||||
aOptions.mExtensions.Value().mMinPinLength.Value());
|
||||
}
|
||||
if (aOptions.mExtensions.Value().mLargeBlob.WasPassed()) {
|
||||
const AuthenticationExtensionsLargeBlobInputsJSON& largeBlobInputsJSON =
|
||||
aOptions.mExtensions.Value().mLargeBlob.Value();
|
||||
AuthenticationExtensionsLargeBlobInputs& largeBlobInputs =
|
||||
aResult.mExtensions.mLargeBlob.Construct();
|
||||
if (!DecodeAuthenticationExtensionsLargeBlobInputsJSON(
|
||||
aGlobal, largeBlobInputsJSON, largeBlobInputs, aRv)) {
|
||||
aRv.ThrowEncodingError(
|
||||
"could not decode large blob inputs as urlsafe base64");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (aOptions.mExtensions.Value().mPrf.WasPassed()) {
|
||||
const AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
|
||||
aOptions.mExtensions.Value().mPrf.Value();
|
||||
@@ -615,6 +727,18 @@ void PublicKeyCredential::ParseRequestOptionsFromJSON(
|
||||
aResult.mExtensions.mHmacCreateSecret.Construct(
|
||||
aOptions.mExtensions.Value().mHmacCreateSecret.Value());
|
||||
}
|
||||
if (aOptions.mExtensions.Value().mLargeBlob.WasPassed()) {
|
||||
const AuthenticationExtensionsLargeBlobInputsJSON& largeBlobInputsJSON =
|
||||
aOptions.mExtensions.Value().mLargeBlob.Value();
|
||||
AuthenticationExtensionsLargeBlobInputs& largeBlobInputs =
|
||||
aResult.mExtensions.mLargeBlob.Construct();
|
||||
if (!DecodeAuthenticationExtensionsLargeBlobInputsJSON(
|
||||
aGlobal, largeBlobInputsJSON, largeBlobInputs, aRv)) {
|
||||
aRv.ThrowEncodingError(
|
||||
"could not decode large blob inputs as urlsafe base64");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (aOptions.mExtensions.Value().mMinPinLength.WasPassed()) {
|
||||
aResult.mExtensions.mMinPinLength.Construct(
|
||||
aOptions.mExtensions.Value().mMinPinLength.Value());
|
||||
|
||||
@@ -71,6 +71,13 @@ class PublicKeyCredential final : public Credential {
|
||||
void SetClientExtensionResultCredPropsRk(bool aResult);
|
||||
|
||||
void SetClientExtensionResultHmacSecret(bool aHmacCreateSecret);
|
||||
|
||||
void InitClientExtensionResultLargeBlob();
|
||||
void SetClientExtensionResultLargeBlobSupported(bool aSupported);
|
||||
void SetClientExtensionResultLargeBlobValue(
|
||||
const nsTArray<uint8_t>& aLargeBlobValue);
|
||||
void SetClientExtensionResultLargeBlobWritten(bool aLargeBlobWritten);
|
||||
|
||||
void InitClientExtensionResultPrf();
|
||||
void SetClientExtensionResultPrfEnabled(bool aPrfEnabled);
|
||||
void SetClientExtensionResultPrfResultsFirst(
|
||||
@@ -99,6 +106,7 @@ class PublicKeyCredential final : public Credential {
|
||||
// We need a reference to JSContext in order to convert nsTArray to
|
||||
// BufferSource, so we need to store these outside mClientExtensionOutputs and
|
||||
// defer the conversion until the GetClientExtensionResults call.
|
||||
Maybe<nsTArray<uint8_t>> mLargeBlobValue;
|
||||
Maybe<nsTArray<uint8_t>> mPrfResultsFirst;
|
||||
Maybe<nsTArray<uint8_t>> mPrfResultsSecond;
|
||||
};
|
||||
|
||||
@@ -216,6 +216,16 @@ WebAuthnRegisterArgs::GetPrivateBrowsing(bool* aPrivateBrowsing) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnRegisterArgs::GetLargeBlobSupportRequired(
|
||||
bool* aLargeBlobSupportRequired) {
|
||||
if (mLargeBlobSupportRequired.isSome()) {
|
||||
*aLargeBlobSupportRequired = mLargeBlobSupportRequired.ref();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebAuthnSignArgs, nsIWebAuthnSignArgs)
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -437,4 +447,22 @@ WebAuthnSignArgs::GetPrivateBrowsing(bool* aPrivateBrowsing) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnSignArgs::GetLargeBlobRead(bool* aLargeBlobRead) {
|
||||
if (mLargeBlobRead.isSome()) {
|
||||
*aLargeBlobRead = mLargeBlobRead.ref();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnSignArgs::GetLargeBlobWrite(nsTArray<uint8_t>& aLargeBlobWrite) {
|
||||
if (mLargeBlobRead.isSome() && mLargeBlobRead.ref() == false) {
|
||||
aLargeBlobWrite.Assign(mLargeBlobWrite);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
@@ -28,6 +28,7 @@ class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
|
||||
mInfo(aInfo),
|
||||
mCredProps(false),
|
||||
mHmacCreateSecret(false),
|
||||
mLargeBlobSupportRequired(Nothing()),
|
||||
mMinPinLength(false),
|
||||
mPrf(false) {
|
||||
for (const WebAuthnExtension& ext : mInfo.Extensions()) {
|
||||
@@ -39,6 +40,10 @@ class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
|
||||
mHmacCreateSecret =
|
||||
ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret();
|
||||
break;
|
||||
case WebAuthnExtension::TWebAuthnExtensionLargeBlob:
|
||||
mLargeBlobSupportRequired =
|
||||
ext.get_WebAuthnExtensionLargeBlob().flag();
|
||||
break;
|
||||
case WebAuthnExtension::TWebAuthnExtensionMinPinLength:
|
||||
mMinPinLength =
|
||||
ext.get_WebAuthnExtensionMinPinLength().minPinLength();
|
||||
@@ -63,6 +68,7 @@ class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
|
||||
// Flags to indicate whether an extension is being requested.
|
||||
bool mCredProps;
|
||||
bool mHmacCreateSecret;
|
||||
Maybe<bool> mLargeBlobSupportRequired;
|
||||
bool mMinPinLength;
|
||||
bool mPrf;
|
||||
};
|
||||
@@ -89,6 +95,16 @@ class WebAuthnSignArgs final : public nsIWebAuthnSignArgs {
|
||||
break;
|
||||
case WebAuthnExtension::TWebAuthnExtensionMinPinLength:
|
||||
break;
|
||||
case WebAuthnExtension::TWebAuthnExtensionLargeBlob:
|
||||
if (ext.get_WebAuthnExtensionLargeBlob().flag().isSome()) {
|
||||
bool read = ext.get_WebAuthnExtensionLargeBlob().flag().ref();
|
||||
mLargeBlobRead.emplace(read);
|
||||
if (!read) {
|
||||
mLargeBlobWrite.AppendElements(
|
||||
ext.get_WebAuthnExtensionLargeBlob().write());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WebAuthnExtension::TWebAuthnExtensionPrf:
|
||||
mPrf = ext.get_WebAuthnExtensionPrf().eval().isSome() ||
|
||||
ext.get_WebAuthnExtensionPrf().evalByCredentialMaybe();
|
||||
@@ -106,6 +122,8 @@ class WebAuthnSignArgs final : public nsIWebAuthnSignArgs {
|
||||
const nsCString mClientDataJSON;
|
||||
const bool mPrivateBrowsing;
|
||||
const WebAuthnGetAssertionInfo mInfo;
|
||||
Maybe<bool> mLargeBlobRead;
|
||||
nsTArray<uint8_t> mLargeBlobWrite;
|
||||
bool mPrf;
|
||||
};
|
||||
|
||||
|
||||
@@ -271,6 +271,24 @@ already_AddRefed<Promise> WebAuthnHandler::MakeCredential(
|
||||
}
|
||||
}
|
||||
|
||||
// <https://w3c.github.io/webauthn/#sctn-large-blob-extension>
|
||||
if (aOptions.mExtensions.mLargeBlob.WasPassed()) {
|
||||
if (aOptions.mExtensions.mLargeBlob.Value().mRead.WasPassed() ||
|
||||
aOptions.mExtensions.mLargeBlob.Value().mWrite.WasPassed()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
Maybe<bool> supportRequired;
|
||||
const Optional<nsString>& largeBlobSupport =
|
||||
aOptions.mExtensions.mLargeBlob.Value().mSupport;
|
||||
if (largeBlobSupport.WasPassed()) {
|
||||
supportRequired.emplace(largeBlobSupport.Value().Equals(u"required"_ns));
|
||||
}
|
||||
nsTArray<uint8_t> write; // unused
|
||||
extensions.AppendElement(
|
||||
WebAuthnExtensionLargeBlob(supportRequired, write));
|
||||
}
|
||||
|
||||
// <https://w3c.github.io/webauthn/#prf-extension>
|
||||
if (aOptions.mExtensions.mPrf.WasPassed()) {
|
||||
const AuthenticationExtensionsPRFInputs& prf =
|
||||
@@ -533,6 +551,30 @@ already_AddRefed<Promise> WebAuthnHandler::GetAssertion(
|
||||
maybeAppId.emplace(std::move(appId));
|
||||
}
|
||||
|
||||
// <https://w3c.github.io/webauthn/#sctn-large-blob-extension>
|
||||
if (aOptions.mExtensions.mLargeBlob.WasPassed()) {
|
||||
const AuthenticationExtensionsLargeBlobInputs& extLargeBlob =
|
||||
aOptions.mExtensions.mLargeBlob.Value();
|
||||
if (extLargeBlob.mSupport.WasPassed() ||
|
||||
(extLargeBlob.mRead.WasPassed() && extLargeBlob.mWrite.WasPassed()) ||
|
||||
(extLargeBlob.mWrite.WasPassed() &&
|
||||
aOptions.mAllowCredentials.Length() != 1)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
Maybe<bool> read = Nothing();
|
||||
if (extLargeBlob.mRead.WasPassed() && extLargeBlob.mRead.Value()) {
|
||||
read.emplace(true);
|
||||
}
|
||||
|
||||
CryptoBuffer write;
|
||||
if (extLargeBlob.mWrite.WasPassed()) {
|
||||
read.emplace(false);
|
||||
write.Assign(extLargeBlob.mWrite.Value());
|
||||
}
|
||||
extensions.AppendElement(WebAuthnExtensionLargeBlob(read, write));
|
||||
}
|
||||
|
||||
// <https://w3c.github.io/webauthn/#prf-extension>
|
||||
if (aOptions.mExtensions.mPrf.WasPassed()) {
|
||||
const AuthenticationExtensionsPRFInputs& prf =
|
||||
@@ -753,6 +795,12 @@ void WebAuthnHandler::FinishMakeCredential(
|
||||
ext.get_WebAuthnExtensionResultHmacSecret().hmacCreateSecret();
|
||||
credential->SetClientExtensionResultHmacSecret(hmacCreateSecret);
|
||||
}
|
||||
if (ext.type() ==
|
||||
WebAuthnExtensionResult::TWebAuthnExtensionResultLargeBlob) {
|
||||
credential->InitClientExtensionResultLargeBlob();
|
||||
credential->SetClientExtensionResultLargeBlobSupported(
|
||||
ext.get_WebAuthnExtensionResultLargeBlob().flag());
|
||||
}
|
||||
if (ext.type() == WebAuthnExtensionResult::TWebAuthnExtensionResultPrf) {
|
||||
credential->InitClientExtensionResultPrf();
|
||||
const Maybe<bool> prfEnabled =
|
||||
@@ -823,6 +871,24 @@ void WebAuthnHandler::FinishGetAssertion(
|
||||
bool appid = ext.get_WebAuthnExtensionResultAppId().AppId();
|
||||
credential->SetClientExtensionResultAppId(appid);
|
||||
}
|
||||
if (ext.type() ==
|
||||
WebAuthnExtensionResult::TWebAuthnExtensionResultLargeBlob) {
|
||||
if (ext.get_WebAuthnExtensionResultLargeBlob().flag() &&
|
||||
ext.get_WebAuthnExtensionResultLargeBlob().written()) {
|
||||
// Signal a read failure by including an empty largeBlob extension.
|
||||
credential->InitClientExtensionResultLargeBlob();
|
||||
} else if (ext.get_WebAuthnExtensionResultLargeBlob().flag()) {
|
||||
const nsTArray<uint8_t>& largeBlobValue =
|
||||
ext.get_WebAuthnExtensionResultLargeBlob().blob();
|
||||
credential->InitClientExtensionResultLargeBlob();
|
||||
credential->SetClientExtensionResultLargeBlobValue(largeBlobValue);
|
||||
} else {
|
||||
bool largeBlobWritten =
|
||||
ext.get_WebAuthnExtensionResultLargeBlob().written();
|
||||
credential->InitClientExtensionResultLargeBlob();
|
||||
credential->SetClientExtensionResultLargeBlobWritten(largeBlobWritten);
|
||||
}
|
||||
}
|
||||
if (ext.type() == WebAuthnExtensionResult::TWebAuthnExtensionResultPrf) {
|
||||
credential->InitClientExtensionResultPrf();
|
||||
Maybe<WebAuthnExtensionPrfValues> prfResults =
|
||||
|
||||
@@ -90,6 +90,15 @@ WebAuthnRegisterResult::SetCredPropsRk(bool aCredPropsRk) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnRegisterResult::GetLargeBlobSupported(bool* aLargeBlobSupported) {
|
||||
if (mLargeBlobSupported.isSome()) {
|
||||
*aLargeBlobSupported = mLargeBlobSupported.ref();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnRegisterResult::GetPrfEnabled(bool* aPrfEnabled) {
|
||||
if (mPrf.isSome()) {
|
||||
@@ -218,6 +227,24 @@ WebAuthnSignResult::SetUsedAppId(bool aUsedAppId) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnSignResult::GetLargeBlobValue(nsTArray<uint8_t>& aLargeBlobValue) {
|
||||
if (mLargeBlobValue.isSome()) {
|
||||
aLargeBlobValue.Assign(*mLargeBlobValue);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnSignResult::GetLargeBlobWritten(bool* aLargeBlobWritten) {
|
||||
if (mLargeBlobWritten.isSome()) {
|
||||
*aLargeBlobWritten = mLargeBlobWritten.ref();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnSignResult::GetPrfMaybe(bool* aPrfMaybe) {
|
||||
*aPrfMaybe = mPrfFirst.isSome();
|
||||
|
||||
@@ -33,10 +33,12 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
|
||||
const Maybe<nsCString>& aClientDataJSON,
|
||||
const nsTArray<uint8_t>& aCredentialId,
|
||||
const nsTArray<nsString>& aTransports,
|
||||
const Maybe<nsString>& aAuthenticatorAttachment)
|
||||
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||
const Maybe<bool>& aLargeBlobSupported)
|
||||
: mClientDataJSON(aClientDataJSON),
|
||||
mCredPropsRk(Nothing()),
|
||||
mAuthenticatorAttachment(aAuthenticatorAttachment) {
|
||||
mAuthenticatorAttachment(aAuthenticatorAttachment),
|
||||
mLargeBlobSupported(aLargeBlobSupported) {
|
||||
mAttestationObject.AppendElements(aAttestationObject);
|
||||
mCredentialId.AppendElements(aCredentialId);
|
||||
mTransports.AppendElements(aTransports);
|
||||
@@ -134,6 +136,12 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
|
||||
}
|
||||
}
|
||||
|
||||
if (aResponse->dwVersion >= WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4) {
|
||||
if (aResponse->bLargeBlobSupported) {
|
||||
mLargeBlobSupported = Some(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (aResponse->dwVersion >= WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_5) {
|
||||
if (aResponse->bPrfEnabled) {
|
||||
mPrf = Some(true);
|
||||
@@ -151,8 +159,9 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
|
||||
Maybe<nsCString> mClientDataJSON;
|
||||
Maybe<bool> mCredPropsRk;
|
||||
Maybe<bool> mHmacCreateSecret;
|
||||
Maybe<bool> mPrf;
|
||||
Maybe<nsString> mAuthenticatorAttachment;
|
||||
Maybe<bool> mLargeBlobSupported;
|
||||
Maybe<bool> mPrf;
|
||||
};
|
||||
|
||||
class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
||||
@@ -165,13 +174,20 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
||||
const nsTArray<uint8_t>& aCredentialId,
|
||||
const nsTArray<uint8_t>& aSignature,
|
||||
const nsTArray<uint8_t>& aUserHandle,
|
||||
const Maybe<nsString>& aAuthenticatorAttachment)
|
||||
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||
const Maybe<nsTArray<uint8_t>>& aLargeBlobValue,
|
||||
const Maybe<bool>& aLargeBlobWritten)
|
||||
: mClientDataJSON(aClientDataJSON),
|
||||
mAuthenticatorAttachment(aAuthenticatorAttachment) {
|
||||
mAuthenticatorAttachment(aAuthenticatorAttachment),
|
||||
mLargeBlobWritten(aLargeBlobWritten) {
|
||||
mAuthenticatorData.AppendElements(aAuthenticatorData);
|
||||
mCredentialId.AppendElements(aCredentialId);
|
||||
mSignature.AppendElements(aSignature);
|
||||
mUserHandle.AppendElements(aUserHandle);
|
||||
if (aLargeBlobValue.isSome()) {
|
||||
mLargeBlobValue.emplace(aLargeBlobValue->Length());
|
||||
mLargeBlobValue->Assign(aLargeBlobValue.ref());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
@@ -205,7 +221,8 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
WebAuthnSignResult(nsCString& aClientDataJSON, PCWEBAUTHN_ASSERTION aResponse)
|
||||
WebAuthnSignResult(nsCString& aClientDataJSON, DWORD aCredLargeBlobOperation,
|
||||
PCWEBAUTHN_ASSERTION aResponse)
|
||||
: mClientDataJSON(Some(aClientDataJSON)) {
|
||||
mSignature.AppendElements(aResponse->pbSignature, aResponse->cbSignature);
|
||||
|
||||
@@ -218,6 +235,26 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
||||
aResponse->cbAuthenticatorData);
|
||||
|
||||
mAuthenticatorAttachment = Nothing(); // not available
|
||||
|
||||
if (aCredLargeBlobOperation == WEBAUTHN_CRED_LARGE_BLOB_OPERATION_GET) {
|
||||
if (aResponse->dwVersion >= WEBAUTHN_ASSERTION_VERSION_2 &&
|
||||
aResponse->dwCredLargeBlobStatus ==
|
||||
WEBAUTHN_CRED_LARGE_BLOB_STATUS_SUCCESS) {
|
||||
mLargeBlobValue.emplace();
|
||||
mLargeBlobValue->AppendElements(aResponse->pbCredLargeBlob,
|
||||
aResponse->cbCredLargeBlob);
|
||||
}
|
||||
} else if (aCredLargeBlobOperation ==
|
||||
WEBAUTHN_CRED_LARGE_BLOB_OPERATION_SET) {
|
||||
if (aResponse->dwVersion >= WEBAUTHN_ASSERTION_VERSION_2 &&
|
||||
aResponse->dwCredLargeBlobStatus ==
|
||||
WEBAUTHN_CRED_LARGE_BLOB_STATUS_SUCCESS) {
|
||||
mLargeBlobWritten.emplace(true);
|
||||
} else {
|
||||
mLargeBlobWritten.emplace(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (aResponse->dwVersion >= WEBAUTHN_ASSERTION_VERSION_3) {
|
||||
if (aResponse->pHmacSecret) {
|
||||
if (aResponse->pHmacSecret->cbFirst > 0) {
|
||||
@@ -245,6 +282,8 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
||||
nsTArray<uint8_t> mUserHandle;
|
||||
Maybe<nsString> mAuthenticatorAttachment;
|
||||
Maybe<bool> mUsedAppId;
|
||||
Maybe<nsTArray<uint8_t>> mLargeBlobValue;
|
||||
Maybe<bool> mLargeBlobWritten;
|
||||
Maybe<nsTArray<uint8_t>> mPrfFirst;
|
||||
Maybe<nsTArray<uint8_t>> mPrfSecond;
|
||||
};
|
||||
|
||||
@@ -73,6 +73,18 @@ nsresult AssembleClientData(WindowGlobalParent* aManager,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool GetAssertionRequestIncludesLargeBlobRead(
|
||||
const WebAuthnGetAssertionInfo& aInfo) {
|
||||
for (const WebAuthnExtension& ext : aInfo.Extensions()) {
|
||||
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionLargeBlob) {
|
||||
if (ext.get_WebAuthnExtensionLargeBlob().flag().isSome()) {
|
||||
return ext.get_WebAuthnExtensionLargeBlob().flag().ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebAuthnTransactionParent::CompleteTransaction() {
|
||||
if (mTransactionId.isSome()) {
|
||||
if (mRegisterPromiseRequest.Exists()) {
|
||||
@@ -227,6 +239,17 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
|
||||
WebAuthnExtensionResultHmacSecret(hmacCreateSecret));
|
||||
}
|
||||
|
||||
bool largeBlobSupported;
|
||||
rv = registerResult->GetLargeBlobSupported(&largeBlobSupported);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
nsTArray<uint8_t> blob; // unused
|
||||
extensions.AppendElement(WebAuthnExtensionResultLargeBlob(
|
||||
largeBlobSupported, blob, false));
|
||||
}
|
||||
|
||||
{
|
||||
Maybe<bool> prfEnabledMaybe = Nothing();
|
||||
Maybe<WebAuthnExtensionPrfValues> prfResults = Nothing();
|
||||
@@ -342,6 +365,9 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
bool requestIncludesLargeBlobRead =
|
||||
GetAssertionRequestIncludesLargeBlobRead(aTransactionInfo);
|
||||
|
||||
RefPtr<WebAuthnSignPromiseHolder> promiseHolder =
|
||||
new WebAuthnSignPromiseHolder(GetCurrentSerialEventTarget());
|
||||
|
||||
@@ -350,7 +376,7 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[self = RefPtr{this}, inputClientData = clientDataJSON,
|
||||
resolver = std::move(aResolver)](
|
||||
requestIncludesLargeBlobRead, resolver = std::move(aResolver)](
|
||||
const WebAuthnSignPromise::ResolveOrRejectValue& aValue) {
|
||||
self->CompleteTransaction();
|
||||
|
||||
@@ -414,6 +440,32 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
|
||||
extensions.AppendElement(WebAuthnExtensionResultAppId(usedAppId));
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> largeBlobValue;
|
||||
rv = signResult->GetLargeBlobValue(largeBlobValue);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
extensions.AppendElement(WebAuthnExtensionResultLargeBlob(
|
||||
true, largeBlobValue, false));
|
||||
} else if (requestIncludesLargeBlobRead) {
|
||||
// Signal a read error by setting both flags.
|
||||
extensions.AppendElement(
|
||||
WebAuthnExtensionResultLargeBlob(true, largeBlobValue, true));
|
||||
} else {
|
||||
// Read and write operations are mutually exclusive, so we only
|
||||
// check for a write result if the read result is not available.
|
||||
bool largeBlobWritten;
|
||||
rv = signResult->GetLargeBlobWritten(&largeBlobWritten);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
extensions.AppendElement(WebAuthnExtensionResultLargeBlob(
|
||||
false, largeBlobValue, largeBlobWritten));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Maybe<WebAuthnExtensionPrfValues> prfResults;
|
||||
bool prfMaybe = false;
|
||||
|
||||
@@ -308,6 +308,23 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
// AttestationConveyance
|
||||
DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
|
||||
|
||||
// Large Blob
|
||||
DWORD largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_NONE;
|
||||
bool largeBlobSupportRequired;
|
||||
nsresult rv =
|
||||
aArgs->GetLargeBlobSupportRequired(&largeBlobSupportRequired);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
aPromise->Reject(rv);
|
||||
return;
|
||||
}
|
||||
if (largeBlobSupportRequired) {
|
||||
largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_REQUIRED;
|
||||
} else {
|
||||
largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_PREFERRED;
|
||||
}
|
||||
}
|
||||
|
||||
// Prf
|
||||
BOOL winEnablePrf = FALSE;
|
||||
|
||||
@@ -366,8 +383,7 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
// Attachment
|
||||
DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
|
||||
nsString authenticatorAttachment;
|
||||
nsresult rv =
|
||||
aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
|
||||
rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
aPromise->Reject(rv);
|
||||
@@ -553,7 +569,7 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
&cancellationId, // CancellationId
|
||||
pExcludeCredentialList,
|
||||
WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
|
||||
WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
|
||||
largeBlobSupport, // LargeBlobSupport
|
||||
winPreferResidentKey, // PreferResidentKey
|
||||
winPrivateBrowsing, // BrowserInPrivateMode
|
||||
winEnablePrf, // EnablePrf
|
||||
@@ -739,6 +755,32 @@ void WinWebAuthnService::DoGetAssertion(
|
||||
winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
|
||||
}
|
||||
|
||||
// Large Blob
|
||||
DWORD credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE;
|
||||
DWORD credLargeBlobSize = 0;
|
||||
PBYTE credLargeBlob = nullptr;
|
||||
nsTArray<uint8_t> largeBlobWrite;
|
||||
bool largeBlobRead;
|
||||
rv = aArgs->GetLargeBlobRead(&largeBlobRead);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
if (NS_FAILED(rv)) {
|
||||
aPromise->Reject(rv);
|
||||
return;
|
||||
}
|
||||
if (largeBlobRead) {
|
||||
credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_GET;
|
||||
} else {
|
||||
rv = aArgs->GetLargeBlobWrite(largeBlobWrite);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE && NS_FAILED(rv)) {
|
||||
aPromise->Reject(rv);
|
||||
return;
|
||||
}
|
||||
credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_SET;
|
||||
credLargeBlobSize = largeBlobWrite.Length();
|
||||
credLargeBlob = largeBlobWrite.Elements();
|
||||
}
|
||||
}
|
||||
|
||||
// PRF inputs
|
||||
WEBAUTHN_HMAC_SECRET_SALT_VALUES* pPrfInputs = nullptr;
|
||||
WEBAUTHN_HMAC_SECRET_SALT_VALUES prfInputs = {0};
|
||||
@@ -906,9 +948,9 @@ void WinWebAuthnService::DoGetAssertion(
|
||||
pbAppIdUsed,
|
||||
&aCancellationId, // CancellationId
|
||||
pAllowCredentialList,
|
||||
WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE,
|
||||
0, // Size of CredLargeBlob
|
||||
NULL, // CredLargeBlob
|
||||
credLargeBlobOperation, // CredLargeBlobOperation
|
||||
credLargeBlobSize, // Size of CredLargeBlob
|
||||
credLargeBlob, // CredLargeBlob
|
||||
pPrfInputs, // HmacSecretSaltValues
|
||||
winPrivateBrowsing, // BrowserInPrivateMode
|
||||
NULL, // LinkedDevice
|
||||
@@ -928,8 +970,8 @@ void WinWebAuthnService::DoGetAssertion(
|
||||
&pWebAuthNAssertion);
|
||||
|
||||
if (hr == S_OK) {
|
||||
RefPtr<WebAuthnSignResult> result =
|
||||
new WebAuthnSignResult(clientDataJSON, pWebAuthNAssertion);
|
||||
RefPtr<WebAuthnSignResult> result = new WebAuthnSignResult(
|
||||
clientDataJSON, credLargeBlobOperation, pWebAuthNAssertion);
|
||||
gWinWebauthnFreeAssertion(pWebAuthNAssertion);
|
||||
if (winAppIdentifier != nullptr) {
|
||||
// The gWinWebauthnGetAssertion call modified bAppIdUsed through
|
||||
|
||||
@@ -216,6 +216,11 @@ impl WebAuthnRegisterResult {
|
||||
Ok(hmac_create_secret)
|
||||
}
|
||||
|
||||
xpcom_method!(get_large_blob_supported => GetLargeBlobSupported() -> bool);
|
||||
fn get_large_blob_supported(&self) -> Result<bool, nsresult> {
|
||||
Err(NS_ERROR_NOT_AVAILABLE)
|
||||
}
|
||||
|
||||
xpcom_method!(get_prf_enabled => GetPrfEnabled() -> bool);
|
||||
fn get_prf_enabled(&self) -> Result<bool, nsresult> {
|
||||
match self.result.borrow().extensions.prf {
|
||||
@@ -412,6 +417,16 @@ impl WebAuthnSignResult {
|
||||
Err(NS_ERROR_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
xpcom_method!(get_large_blob_value => GetLargeBlobValue() -> ThinVec<u8>);
|
||||
fn get_large_blob_value(&self) -> Result<ThinVec<u8>, nsresult> {
|
||||
Err(NS_ERROR_NOT_AVAILABLE)
|
||||
}
|
||||
|
||||
xpcom_method!(get_large_blob_written => GetLargeBlobWritten() -> bool);
|
||||
fn get_large_blob_written(&self) -> Result<bool, nsresult> {
|
||||
Err(NS_ERROR_NOT_AVAILABLE)
|
||||
}
|
||||
|
||||
xpcom_method!(get_prf_maybe => GetPrfMaybe() -> bool);
|
||||
/// Return true if a PRF output is present, even if all attributes are absent.
|
||||
fn get_prf_maybe(&self) -> Result<bool, nsresult> {
|
||||
|
||||
@@ -46,6 +46,7 @@ interface nsIWebAuthnRegisterArgs : nsISupports {
|
||||
[must_use] readonly attribute boolean prf;
|
||||
[must_use] readonly attribute Array<octet> prfEvalFirst;
|
||||
[must_use] readonly attribute Array<octet> prfEvalSecond;
|
||||
[must_use] readonly attribute boolean largeBlobSupportRequired;
|
||||
|
||||
// Options.
|
||||
readonly attribute AString residentKey;
|
||||
@@ -97,6 +98,8 @@ interface nsIWebAuthnSignArgs : nsISupports {
|
||||
[must_use] readonly attribute Array<Array<octet> > prfEvalByCredentialEvalFirst;
|
||||
[must_use] readonly attribute Array<boolean> prfEvalByCredentialEvalSecondMaybe;
|
||||
[must_use] readonly attribute Array<Array<octet> > prfEvalByCredentialEvalSecond;
|
||||
[must_use] readonly attribute boolean largeBlobRead;
|
||||
[must_use] readonly attribute Array<octet> largeBlobWrite;
|
||||
|
||||
// Options
|
||||
[must_use] readonly attribute AString userVerification;
|
||||
|
||||
@@ -27,6 +27,8 @@ interface nsIWebAuthnRegisterResult : nsISupports {
|
||||
|
||||
[must_use] attribute boolean credPropsRk;
|
||||
|
||||
readonly attribute boolean largeBlobSupported;
|
||||
|
||||
readonly attribute boolean prfEnabled;
|
||||
readonly attribute Array<octet> prfResultsFirst;
|
||||
readonly attribute Array<octet> prfResultsSecond;
|
||||
@@ -65,6 +67,9 @@ interface nsIWebAuthnSignResult : nsISupports {
|
||||
// appId field of AuthenticationExtensionsClientOutputs (Optional)
|
||||
[must_use] attribute boolean usedAppId;
|
||||
|
||||
readonly attribute Array<octet> largeBlobValue;
|
||||
readonly attribute boolean largeBlobWritten;
|
||||
|
||||
readonly attribute boolean prfMaybe; // prf output can be present but empty
|
||||
readonly attribute Array<octet> prfResultsFirst;
|
||||
readonly attribute Array<octet> prfResultsSecond;
|
||||
|
||||
@@ -319,6 +319,47 @@ partial dictionary AuthenticationExtensionsClientOutputsJSON {
|
||||
boolean hmacCreateSecret;
|
||||
};
|
||||
|
||||
// largeBlob
|
||||
// <https://w3c.github.io/webauthn/#sctn-large-blob-extension>
|
||||
partial dictionary AuthenticationExtensionsClientInputs {
|
||||
AuthenticationExtensionsLargeBlobInputs largeBlob;
|
||||
};
|
||||
|
||||
partial dictionary AuthenticationExtensionsClientInputsJSON {
|
||||
AuthenticationExtensionsLargeBlobInputsJSON largeBlob;
|
||||
};
|
||||
|
||||
dictionary AuthenticationExtensionsLargeBlobInputs {
|
||||
DOMString support;
|
||||
boolean read;
|
||||
BufferSource write;
|
||||
};
|
||||
|
||||
dictionary AuthenticationExtensionsLargeBlobInputsJSON {
|
||||
DOMString support;
|
||||
boolean read;
|
||||
Base64URLString write;
|
||||
};
|
||||
|
||||
partial dictionary AuthenticationExtensionsClientOutputs {
|
||||
AuthenticationExtensionsLargeBlobOutputs largeBlob;
|
||||
};
|
||||
|
||||
partial dictionary AuthenticationExtensionsClientOutputsJSON {
|
||||
AuthenticationExtensionsLargeBlobOutputsJSON largeBlob;
|
||||
};
|
||||
|
||||
dictionary AuthenticationExtensionsLargeBlobOutputs {
|
||||
boolean supported;
|
||||
ArrayBuffer blob;
|
||||
boolean written;
|
||||
};
|
||||
|
||||
dictionary AuthenticationExtensionsLargeBlobOutputsJSON {
|
||||
boolean supported;
|
||||
Base64URLString blob;
|
||||
boolean written;
|
||||
};
|
||||
|
||||
// minPinLength
|
||||
// <https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-minpinlength-extension>
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
[createcredential-large-blob-not-supported.https.html]
|
||||
[navigator.credentials.create() with largeBlob.write set]
|
||||
expected: FAIL
|
||||
|
||||
[navigator.credentials.create() with largeBlob.read set]
|
||||
expected: FAIL
|
||||
|
||||
[navigator.credentials.create() with largeBlob.support set to preferred and not supported by authenticator]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
[getcredential-large-blob-not-supported.https.html]
|
||||
[navigator.credentials.get() with largeBlob.support set]
|
||||
expected: FAIL
|
||||
|
||||
[navigator.credentials.get() with largeBlob.read and largeBlob.write set]
|
||||
expected: FAIL
|
||||
|
||||
[navigator.credentials.get() with largeBlob.read set without authenticator support]
|
||||
expected: FAIL
|
||||
|
||||
[navigator.credentials.get() with largeBlob.write set without authenticator support]
|
||||
expected: FAIL
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
[getcredential-large-blob-supported.https.html]
|
||||
[navigator.credentials.get() with largeBlob.read set with no blob on authenticator]
|
||||
expected: FAIL
|
||||
|
||||
[navigator.credentials.get() read and write blob]
|
||||
expected: FAIL
|
||||
|
||||
Reference in New Issue
Block a user