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,
|
void FinishMakeCredential(const nsTArray<uint8_t>& aRawAttestationObject,
|
||||||
const nsTArray<uint8_t>& aCredentialId,
|
const nsTArray<uint8_t>& aCredentialId,
|
||||||
const nsTArray<nsString>& aTransports,
|
const nsTArray<nsString>& aTransports,
|
||||||
const Maybe<nsString>& aAuthenticatorAttachment);
|
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||||
|
const Maybe<bool>& aLargeBlobSupported);
|
||||||
|
|
||||||
void FinishGetAssertion(const nsTArray<uint8_t>& aCredentialId,
|
void FinishGetAssertion(const nsTArray<uint8_t>& aCredentialId,
|
||||||
const nsTArray<uint8_t>& aSignature,
|
const nsTArray<uint8_t>& aSignature,
|
||||||
const nsTArray<uint8_t>& aAuthenticatorData,
|
const nsTArray<uint8_t>& aAuthenticatorData,
|
||||||
const nsTArray<uint8_t>& aUserHandle,
|
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 ReleasePlatformResources();
|
||||||
void AbortTransaction(nsresult aError);
|
void AbortTransaction(nsresult aError);
|
||||||
|
|
||||||
@@ -314,18 +317,17 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
|||||||
nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID));
|
nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID));
|
||||||
nsTArray<nsString> transports;
|
nsTArray<nsString> transports;
|
||||||
mozilla::Maybe<nsString> authenticatorAttachment;
|
mozilla::Maybe<nsString> authenticatorAttachment;
|
||||||
|
mozilla::Maybe<bool> largeBlobSupported;
|
||||||
if ([credential isKindOfClass:
|
if ([credential isKindOfClass:
|
||||||
[ASAuthorizationPlatformPublicKeyCredentialRegistration
|
[ASAuthorizationPlatformPublicKeyCredentialRegistration
|
||||||
class]]) {
|
class]]) {
|
||||||
transports.AppendElement(u"hybrid"_ns);
|
transports.AppendElement(u"hybrid"_ns);
|
||||||
transports.AppendElement(u"internal"_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*
|
ASAuthorizationPlatformPublicKeyCredentialRegistration*
|
||||||
platformCredential =
|
platformCredential =
|
||||||
(ASAuthorizationPlatformPublicKeyCredentialRegistration*)
|
(ASAuthorizationPlatformPublicKeyCredentialRegistration*)
|
||||||
credential;
|
credential;
|
||||||
|
if (__builtin_available(macos 13.5, *)) {
|
||||||
switch (platformCredential.attachment) {
|
switch (platformCredential.attachment) {
|
||||||
case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
|
case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
|
||||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||||
@@ -337,7 +339,11 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
if (__builtin_available(macos 14.0, *)) {
|
||||||
|
if (platformCredential.largeBlob) {
|
||||||
|
largeBlobSupported.emplace(platformCredential.largeBlob.isSupported);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// The platform didn't tell us what transport was used, but we know it
|
// 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
|
// 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);
|
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||||
}
|
}
|
||||||
mCallback->FinishMakeCredential(rawAttestationObject, credentialId,
|
mCallback->FinishMakeCredential(rawAttestationObject, credentialId,
|
||||||
transports, authenticatorAttachment);
|
transports, authenticatorAttachment,
|
||||||
|
largeBlobSupported);
|
||||||
} else if ([authorization.credential
|
} else if ([authorization.credential
|
||||||
conformsToProtocol:
|
conformsToProtocol:
|
||||||
@protocol(ASAuthorizationPublicKeyCredentialAssertion)]) {
|
@protocol(ASAuthorizationPublicKeyCredentialAssertion)]) {
|
||||||
@@ -364,16 +371,14 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
|||||||
NSDataToArray(credential.rawAuthenticatorData));
|
NSDataToArray(credential.rawAuthenticatorData));
|
||||||
nsTArray<uint8_t> userHandle(NSDataToArray(credential.userID));
|
nsTArray<uint8_t> userHandle(NSDataToArray(credential.userID));
|
||||||
mozilla::Maybe<nsString> authenticatorAttachment;
|
mozilla::Maybe<nsString> authenticatorAttachment;
|
||||||
|
mozilla::Maybe<nsTArray<uint8_t>> largeBlobValue;
|
||||||
|
mozilla::Maybe<bool> largeBlobWritten;
|
||||||
if ([credential
|
if ([credential
|
||||||
isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialAssertion
|
isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialAssertion
|
||||||
class]]) {
|
class]]) {
|
||||||
#if defined(MAC_OS_VERSION_13_5) && \
|
ASAuthorizationPlatformPublicKeyCredentialAssertion* platformCredential =
|
||||||
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_5
|
(ASAuthorizationPlatformPublicKeyCredentialAssertion*)credential;
|
||||||
if (__builtin_available(macos 13.5, *)) {
|
if (__builtin_available(macos 13.5, *)) {
|
||||||
ASAuthorizationPlatformPublicKeyCredentialAssertion*
|
|
||||||
platformCredential =
|
|
||||||
(ASAuthorizationPlatformPublicKeyCredentialAssertion*)
|
|
||||||
credential;
|
|
||||||
switch (platformCredential.attachment) {
|
switch (platformCredential.attachment) {
|
||||||
case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
|
case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
|
||||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||||
@@ -385,12 +390,22 @@ nsTArray<uint8_t> NSDataToArray(NSData* data) {
|
|||||||
break;
|
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 {
|
} else {
|
||||||
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
authenticatorAttachment.emplace(u"cross-platform"_ns);
|
||||||
}
|
}
|
||||||
mCallback->FinishGetAssertion(credentialId, signature, rawAuthenticatorData,
|
mCallback->FinishGetAssertion(credentialId, signature, rawAuthenticatorData,
|
||||||
userHandle, authenticatorAttachment);
|
userHandle, authenticatorAttachment,
|
||||||
|
largeBlobValue, largeBlobWritten);
|
||||||
} else {
|
} else {
|
||||||
MOZ_LOG(
|
MOZ_LOG(
|
||||||
gMacOSWebAuthnServiceLog, mozilla::LogLevel::Error,
|
gMacOSWebAuthnServiceLog, mozilla::LogLevel::Error,
|
||||||
@@ -722,6 +737,27 @@ MacOSWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
|||||||
crossPlatformRegistrationRequest.userVerificationPreference =
|
crossPlatformRegistrationRequest.userVerificationPreference =
|
||||||
*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;
|
nsTArray<uint8_t> clientDataHash;
|
||||||
nsresult rv = aArgs->GetClientDataHash(clientDataHash);
|
nsresult rv = aArgs->GetClientDataHash(clientDataHash);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
@@ -800,7 +836,8 @@ void MacOSWebAuthnService::FinishMakeCredential(
|
|||||||
const nsTArray<uint8_t>& aRawAttestationObject,
|
const nsTArray<uint8_t>& aRawAttestationObject,
|
||||||
const nsTArray<uint8_t>& aCredentialId,
|
const nsTArray<uint8_t>& aCredentialId,
|
||||||
const nsTArray<nsString>& aTransports,
|
const nsTArray<nsString>& aTransports,
|
||||||
const Maybe<nsString>& aAuthenticatorAttachment) {
|
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||||
|
const Maybe<bool>& aLargeBlobSupported) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (!mRegisterPromise) {
|
if (!mRegisterPromise) {
|
||||||
return;
|
return;
|
||||||
@@ -808,7 +845,7 @@ void MacOSWebAuthnService::FinishMakeCredential(
|
|||||||
|
|
||||||
RefPtr<WebAuthnRegisterResult> result(new WebAuthnRegisterResult(
|
RefPtr<WebAuthnRegisterResult> result(new WebAuthnRegisterResult(
|
||||||
aRawAttestationObject, Nothing(), aCredentialId, aTransports,
|
aRawAttestationObject, Nothing(), aCredentialId, aTransports,
|
||||||
aAuthenticatorAttachment));
|
aAuthenticatorAttachment, aLargeBlobSupported));
|
||||||
Unused << mRegisterPromise->Resolve(result);
|
Unused << mRegisterPromise->Resolve(result);
|
||||||
mRegisterPromise = nullptr;
|
mRegisterPromise = nullptr;
|
||||||
}
|
}
|
||||||
@@ -1037,6 +1074,47 @@ void MacOSWebAuthnService::DoGetAssertion(
|
|||||||
crossPlatformAssertionRequest.userVerificationPreference =
|
crossPlatformAssertionRequest.userVerificationPreference =
|
||||||
*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;
|
nsTArray<uint8_t> clientDataHash;
|
||||||
nsresult rv = aArgs->GetClientDataHash(clientDataHash);
|
nsresult rv = aArgs->GetClientDataHash(clientDataHash);
|
||||||
if (NS_FAILED(rv)) {
|
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>& aCredentialId, const nsTArray<uint8_t>& aSignature,
|
||||||
const nsTArray<uint8_t>& aAuthenticatorData,
|
const nsTArray<uint8_t>& aAuthenticatorData,
|
||||||
const nsTArray<uint8_t>& aUserHandle,
|
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());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (!mSignPromise) {
|
if (!mSignPromise) {
|
||||||
return;
|
return;
|
||||||
@@ -1065,7 +1145,7 @@ void MacOSWebAuthnService::FinishGetAssertion(
|
|||||||
|
|
||||||
RefPtr<WebAuthnSignResult> result(new WebAuthnSignResult(
|
RefPtr<WebAuthnSignResult> result(new WebAuthnSignResult(
|
||||||
aAuthenticatorData, Nothing(), aCredentialId, aSignature, aUserHandle,
|
aAuthenticatorData, Nothing(), aCredentialId, aSignature, aUserHandle,
|
||||||
aAuthenticatorAttachment));
|
aAuthenticatorAttachment, aLargeBlobValue, aLargeBlobWritten));
|
||||||
Unused << mSignPromise->Resolve(result);
|
Unused << mSignPromise->Resolve(result);
|
||||||
mSignPromise = nullptr;
|
mSignPromise = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ struct WebAuthnExtensionHmacSecret {
|
|||||||
bool hmacCreateSecret;
|
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 {
|
struct WebAuthnExtensionMinPinLength {
|
||||||
bool minPinLength;
|
bool minPinLength;
|
||||||
};
|
};
|
||||||
@@ -64,6 +72,7 @@ struct WebAuthnExtensionPrfEvalByCredentialEntry {
|
|||||||
union WebAuthnExtension {
|
union WebAuthnExtension {
|
||||||
WebAuthnExtensionCredProps;
|
WebAuthnExtensionCredProps;
|
||||||
WebAuthnExtensionHmacSecret;
|
WebAuthnExtensionHmacSecret;
|
||||||
|
WebAuthnExtensionLargeBlob;
|
||||||
WebAuthnExtensionMinPinLength;
|
WebAuthnExtensionMinPinLength;
|
||||||
WebAuthnExtensionPrf;
|
WebAuthnExtensionPrf;
|
||||||
};
|
};
|
||||||
@@ -80,6 +89,13 @@ struct WebAuthnExtensionResultHmacSecret {
|
|||||||
bool hmacCreateSecret;
|
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 {
|
struct WebAuthnExtensionResultPrf {
|
||||||
bool? enabled;
|
bool? enabled;
|
||||||
WebAuthnExtensionPrfValues? results;
|
WebAuthnExtensionPrfValues? results;
|
||||||
@@ -89,6 +105,7 @@ union WebAuthnExtensionResult {
|
|||||||
WebAuthnExtensionResultAppId;
|
WebAuthnExtensionResultAppId;
|
||||||
WebAuthnExtensionResultCredProps;
|
WebAuthnExtensionResultCredProps;
|
||||||
WebAuthnExtensionResultHmacSecret;
|
WebAuthnExtensionResultHmacSecret;
|
||||||
|
WebAuthnExtensionResultLargeBlob;
|
||||||
WebAuthnExtensionResultPrf;
|
WebAuthnExtensionResultPrf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,14 @@ already_AddRefed<Promise> PublicKeyCredential::GetClientCapabilities(
|
|||||||
entry->mKey = u"extension:hmacCreateSecret"_ns;
|
entry->mKey = u"extension:hmacCreateSecret"_ns;
|
||||||
entry->mValue = true;
|
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 = capabilities.Entries().AppendElement();
|
||||||
entry->mKey = u"extension:minPinLength"_ns;
|
entry->mKey = u"extension:minPinLength"_ns;
|
||||||
entry->mValue = true;
|
entry->mValue = true;
|
||||||
@@ -282,6 +290,26 @@ void PublicKeyCredential::GetClientExtensionResults(
|
|||||||
mClientExtensionOutputs.mHmacCreateSecret.Value());
|
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()) {
|
if (mClientExtensionOutputs.mPrf.WasPassed()) {
|
||||||
AuthenticationExtensionsPRFOutputs& dest = aResult.mPrf.Construct();
|
AuthenticationExtensionsPRFOutputs& dest = aResult.mPrf.Construct();
|
||||||
|
|
||||||
@@ -336,6 +364,15 @@ void PublicKeyCredential::ToJSON(JSContext* aCx,
|
|||||||
mClientExtensionOutputs.mPrf.Value().mEnabled.Value());
|
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);
|
json.mType.Assign(u"public-key"_ns);
|
||||||
if (!ToJSValue(aCx, json, &value)) {
|
if (!ToJSValue(aCx, json, &value)) {
|
||||||
aError.StealExceptionFromJSContext(aCx);
|
aError.StealExceptionFromJSContext(aCx);
|
||||||
@@ -360,6 +397,27 @@ void PublicKeyCredential::ToJSON(JSContext* aCx,
|
|||||||
if (mClientExtensionOutputs.mPrf.WasPassed()) {
|
if (mClientExtensionOutputs.mPrf.WasPassed()) {
|
||||||
json.mClientExtensionResults.mPrf.Construct();
|
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);
|
json.mType.Assign(u"public-key"_ns);
|
||||||
if (!ToJSValue(aCx, json, &value)) {
|
if (!ToJSValue(aCx, json, &value)) {
|
||||||
aError.StealExceptionFromJSContext(aCx);
|
aError.StealExceptionFromJSContext(aCx);
|
||||||
@@ -390,6 +448,28 @@ void PublicKeyCredential::SetClientExtensionResultHmacSecret(
|
|||||||
mClientExtensionOutputs.mHmacCreateSecret.Value() = aHmacCreateSecret;
|
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() {
|
void PublicKeyCredential::InitClientExtensionResultPrf() {
|
||||||
mClientExtensionOutputs.mPrf.Construct();
|
mClientExtensionOutputs.mPrf.Construct();
|
||||||
}
|
}
|
||||||
@@ -474,6 +554,26 @@ bool DecodeAuthenticationExtensionsPRFInputsJSON(
|
|||||||
return true;
|
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(
|
void PublicKeyCredential::ParseCreationOptionsFromJSON(
|
||||||
GlobalObject& aGlobal,
|
GlobalObject& aGlobal,
|
||||||
const PublicKeyCredentialCreationOptionsJSON& aOptions,
|
const PublicKeyCredentialCreationOptionsJSON& aOptions,
|
||||||
@@ -547,6 +647,18 @@ void PublicKeyCredential::ParseCreationOptionsFromJSON(
|
|||||||
aResult.mExtensions.mMinPinLength.Construct(
|
aResult.mExtensions.mMinPinLength.Construct(
|
||||||
aOptions.mExtensions.Value().mMinPinLength.Value());
|
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()) {
|
if (aOptions.mExtensions.Value().mPrf.WasPassed()) {
|
||||||
const AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
|
const AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
|
||||||
aOptions.mExtensions.Value().mPrf.Value();
|
aOptions.mExtensions.Value().mPrf.Value();
|
||||||
@@ -615,6 +727,18 @@ void PublicKeyCredential::ParseRequestOptionsFromJSON(
|
|||||||
aResult.mExtensions.mHmacCreateSecret.Construct(
|
aResult.mExtensions.mHmacCreateSecret.Construct(
|
||||||
aOptions.mExtensions.Value().mHmacCreateSecret.Value());
|
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()) {
|
if (aOptions.mExtensions.Value().mMinPinLength.WasPassed()) {
|
||||||
aResult.mExtensions.mMinPinLength.Construct(
|
aResult.mExtensions.mMinPinLength.Construct(
|
||||||
aOptions.mExtensions.Value().mMinPinLength.Value());
|
aOptions.mExtensions.Value().mMinPinLength.Value());
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ class PublicKeyCredential final : public Credential {
|
|||||||
void SetClientExtensionResultCredPropsRk(bool aResult);
|
void SetClientExtensionResultCredPropsRk(bool aResult);
|
||||||
|
|
||||||
void SetClientExtensionResultHmacSecret(bool aHmacCreateSecret);
|
void SetClientExtensionResultHmacSecret(bool aHmacCreateSecret);
|
||||||
|
|
||||||
|
void InitClientExtensionResultLargeBlob();
|
||||||
|
void SetClientExtensionResultLargeBlobSupported(bool aSupported);
|
||||||
|
void SetClientExtensionResultLargeBlobValue(
|
||||||
|
const nsTArray<uint8_t>& aLargeBlobValue);
|
||||||
|
void SetClientExtensionResultLargeBlobWritten(bool aLargeBlobWritten);
|
||||||
|
|
||||||
void InitClientExtensionResultPrf();
|
void InitClientExtensionResultPrf();
|
||||||
void SetClientExtensionResultPrfEnabled(bool aPrfEnabled);
|
void SetClientExtensionResultPrfEnabled(bool aPrfEnabled);
|
||||||
void SetClientExtensionResultPrfResultsFirst(
|
void SetClientExtensionResultPrfResultsFirst(
|
||||||
@@ -99,6 +106,7 @@ class PublicKeyCredential final : public Credential {
|
|||||||
// We need a reference to JSContext in order to convert nsTArray to
|
// We need a reference to JSContext in order to convert nsTArray to
|
||||||
// BufferSource, so we need to store these outside mClientExtensionOutputs and
|
// BufferSource, so we need to store these outside mClientExtensionOutputs and
|
||||||
// defer the conversion until the GetClientExtensionResults call.
|
// defer the conversion until the GetClientExtensionResults call.
|
||||||
|
Maybe<nsTArray<uint8_t>> mLargeBlobValue;
|
||||||
Maybe<nsTArray<uint8_t>> mPrfResultsFirst;
|
Maybe<nsTArray<uint8_t>> mPrfResultsFirst;
|
||||||
Maybe<nsTArray<uint8_t>> mPrfResultsSecond;
|
Maybe<nsTArray<uint8_t>> mPrfResultsSecond;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -216,6 +216,16 @@ WebAuthnRegisterArgs::GetPrivateBrowsing(bool* aPrivateBrowsing) {
|
|||||||
return NS_OK;
|
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_IMPL_ISUPPORTS(WebAuthnSignArgs, nsIWebAuthnSignArgs)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@@ -437,4 +447,22 @@ WebAuthnSignArgs::GetPrivateBrowsing(bool* aPrivateBrowsing) {
|
|||||||
return NS_OK;
|
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
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
|
|||||||
mInfo(aInfo),
|
mInfo(aInfo),
|
||||||
mCredProps(false),
|
mCredProps(false),
|
||||||
mHmacCreateSecret(false),
|
mHmacCreateSecret(false),
|
||||||
|
mLargeBlobSupportRequired(Nothing()),
|
||||||
mMinPinLength(false),
|
mMinPinLength(false),
|
||||||
mPrf(false) {
|
mPrf(false) {
|
||||||
for (const WebAuthnExtension& ext : mInfo.Extensions()) {
|
for (const WebAuthnExtension& ext : mInfo.Extensions()) {
|
||||||
@@ -39,6 +40,10 @@ class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
|
|||||||
mHmacCreateSecret =
|
mHmacCreateSecret =
|
||||||
ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret();
|
ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret();
|
||||||
break;
|
break;
|
||||||
|
case WebAuthnExtension::TWebAuthnExtensionLargeBlob:
|
||||||
|
mLargeBlobSupportRequired =
|
||||||
|
ext.get_WebAuthnExtensionLargeBlob().flag();
|
||||||
|
break;
|
||||||
case WebAuthnExtension::TWebAuthnExtensionMinPinLength:
|
case WebAuthnExtension::TWebAuthnExtensionMinPinLength:
|
||||||
mMinPinLength =
|
mMinPinLength =
|
||||||
ext.get_WebAuthnExtensionMinPinLength().minPinLength();
|
ext.get_WebAuthnExtensionMinPinLength().minPinLength();
|
||||||
@@ -63,6 +68,7 @@ class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
|
|||||||
// Flags to indicate whether an extension is being requested.
|
// Flags to indicate whether an extension is being requested.
|
||||||
bool mCredProps;
|
bool mCredProps;
|
||||||
bool mHmacCreateSecret;
|
bool mHmacCreateSecret;
|
||||||
|
Maybe<bool> mLargeBlobSupportRequired;
|
||||||
bool mMinPinLength;
|
bool mMinPinLength;
|
||||||
bool mPrf;
|
bool mPrf;
|
||||||
};
|
};
|
||||||
@@ -89,6 +95,16 @@ class WebAuthnSignArgs final : public nsIWebAuthnSignArgs {
|
|||||||
break;
|
break;
|
||||||
case WebAuthnExtension::TWebAuthnExtensionMinPinLength:
|
case WebAuthnExtension::TWebAuthnExtensionMinPinLength:
|
||||||
break;
|
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:
|
case WebAuthnExtension::TWebAuthnExtensionPrf:
|
||||||
mPrf = ext.get_WebAuthnExtensionPrf().eval().isSome() ||
|
mPrf = ext.get_WebAuthnExtensionPrf().eval().isSome() ||
|
||||||
ext.get_WebAuthnExtensionPrf().evalByCredentialMaybe();
|
ext.get_WebAuthnExtensionPrf().evalByCredentialMaybe();
|
||||||
@@ -106,6 +122,8 @@ class WebAuthnSignArgs final : public nsIWebAuthnSignArgs {
|
|||||||
const nsCString mClientDataJSON;
|
const nsCString mClientDataJSON;
|
||||||
const bool mPrivateBrowsing;
|
const bool mPrivateBrowsing;
|
||||||
const WebAuthnGetAssertionInfo mInfo;
|
const WebAuthnGetAssertionInfo mInfo;
|
||||||
|
Maybe<bool> mLargeBlobRead;
|
||||||
|
nsTArray<uint8_t> mLargeBlobWrite;
|
||||||
bool mPrf;
|
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>
|
// <https://w3c.github.io/webauthn/#prf-extension>
|
||||||
if (aOptions.mExtensions.mPrf.WasPassed()) {
|
if (aOptions.mExtensions.mPrf.WasPassed()) {
|
||||||
const AuthenticationExtensionsPRFInputs& prf =
|
const AuthenticationExtensionsPRFInputs& prf =
|
||||||
@@ -533,6 +551,30 @@ already_AddRefed<Promise> WebAuthnHandler::GetAssertion(
|
|||||||
maybeAppId.emplace(std::move(appId));
|
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>
|
// <https://w3c.github.io/webauthn/#prf-extension>
|
||||||
if (aOptions.mExtensions.mPrf.WasPassed()) {
|
if (aOptions.mExtensions.mPrf.WasPassed()) {
|
||||||
const AuthenticationExtensionsPRFInputs& prf =
|
const AuthenticationExtensionsPRFInputs& prf =
|
||||||
@@ -753,6 +795,12 @@ void WebAuthnHandler::FinishMakeCredential(
|
|||||||
ext.get_WebAuthnExtensionResultHmacSecret().hmacCreateSecret();
|
ext.get_WebAuthnExtensionResultHmacSecret().hmacCreateSecret();
|
||||||
credential->SetClientExtensionResultHmacSecret(hmacCreateSecret);
|
credential->SetClientExtensionResultHmacSecret(hmacCreateSecret);
|
||||||
}
|
}
|
||||||
|
if (ext.type() ==
|
||||||
|
WebAuthnExtensionResult::TWebAuthnExtensionResultLargeBlob) {
|
||||||
|
credential->InitClientExtensionResultLargeBlob();
|
||||||
|
credential->SetClientExtensionResultLargeBlobSupported(
|
||||||
|
ext.get_WebAuthnExtensionResultLargeBlob().flag());
|
||||||
|
}
|
||||||
if (ext.type() == WebAuthnExtensionResult::TWebAuthnExtensionResultPrf) {
|
if (ext.type() == WebAuthnExtensionResult::TWebAuthnExtensionResultPrf) {
|
||||||
credential->InitClientExtensionResultPrf();
|
credential->InitClientExtensionResultPrf();
|
||||||
const Maybe<bool> prfEnabled =
|
const Maybe<bool> prfEnabled =
|
||||||
@@ -823,6 +871,24 @@ void WebAuthnHandler::FinishGetAssertion(
|
|||||||
bool appid = ext.get_WebAuthnExtensionResultAppId().AppId();
|
bool appid = ext.get_WebAuthnExtensionResultAppId().AppId();
|
||||||
credential->SetClientExtensionResultAppId(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) {
|
if (ext.type() == WebAuthnExtensionResult::TWebAuthnExtensionResultPrf) {
|
||||||
credential->InitClientExtensionResultPrf();
|
credential->InitClientExtensionResultPrf();
|
||||||
Maybe<WebAuthnExtensionPrfValues> prfResults =
|
Maybe<WebAuthnExtensionPrfValues> prfResults =
|
||||||
|
|||||||
@@ -90,6 +90,15 @@ WebAuthnRegisterResult::SetCredPropsRk(bool aCredPropsRk) {
|
|||||||
return NS_OK;
|
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
|
NS_IMETHODIMP
|
||||||
WebAuthnRegisterResult::GetPrfEnabled(bool* aPrfEnabled) {
|
WebAuthnRegisterResult::GetPrfEnabled(bool* aPrfEnabled) {
|
||||||
if (mPrf.isSome()) {
|
if (mPrf.isSome()) {
|
||||||
@@ -218,6 +227,24 @@ WebAuthnSignResult::SetUsedAppId(bool aUsedAppId) {
|
|||||||
return NS_OK;
|
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
|
NS_IMETHODIMP
|
||||||
WebAuthnSignResult::GetPrfMaybe(bool* aPrfMaybe) {
|
WebAuthnSignResult::GetPrfMaybe(bool* aPrfMaybe) {
|
||||||
*aPrfMaybe = mPrfFirst.isSome();
|
*aPrfMaybe = mPrfFirst.isSome();
|
||||||
|
|||||||
@@ -33,10 +33,12 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
|
|||||||
const Maybe<nsCString>& aClientDataJSON,
|
const Maybe<nsCString>& aClientDataJSON,
|
||||||
const nsTArray<uint8_t>& aCredentialId,
|
const nsTArray<uint8_t>& aCredentialId,
|
||||||
const nsTArray<nsString>& aTransports,
|
const nsTArray<nsString>& aTransports,
|
||||||
const Maybe<nsString>& aAuthenticatorAttachment)
|
const Maybe<nsString>& aAuthenticatorAttachment,
|
||||||
|
const Maybe<bool>& aLargeBlobSupported)
|
||||||
: mClientDataJSON(aClientDataJSON),
|
: mClientDataJSON(aClientDataJSON),
|
||||||
mCredPropsRk(Nothing()),
|
mCredPropsRk(Nothing()),
|
||||||
mAuthenticatorAttachment(aAuthenticatorAttachment) {
|
mAuthenticatorAttachment(aAuthenticatorAttachment),
|
||||||
|
mLargeBlobSupported(aLargeBlobSupported) {
|
||||||
mAttestationObject.AppendElements(aAttestationObject);
|
mAttestationObject.AppendElements(aAttestationObject);
|
||||||
mCredentialId.AppendElements(aCredentialId);
|
mCredentialId.AppendElements(aCredentialId);
|
||||||
mTransports.AppendElements(aTransports);
|
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->dwVersion >= WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_5) {
|
||||||
if (aResponse->bPrfEnabled) {
|
if (aResponse->bPrfEnabled) {
|
||||||
mPrf = Some(true);
|
mPrf = Some(true);
|
||||||
@@ -151,8 +159,9 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
|
|||||||
Maybe<nsCString> mClientDataJSON;
|
Maybe<nsCString> mClientDataJSON;
|
||||||
Maybe<bool> mCredPropsRk;
|
Maybe<bool> mCredPropsRk;
|
||||||
Maybe<bool> mHmacCreateSecret;
|
Maybe<bool> mHmacCreateSecret;
|
||||||
Maybe<bool> mPrf;
|
|
||||||
Maybe<nsString> mAuthenticatorAttachment;
|
Maybe<nsString> mAuthenticatorAttachment;
|
||||||
|
Maybe<bool> mLargeBlobSupported;
|
||||||
|
Maybe<bool> mPrf;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
||||||
@@ -165,13 +174,20 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
|||||||
const nsTArray<uint8_t>& aCredentialId,
|
const nsTArray<uint8_t>& aCredentialId,
|
||||||
const nsTArray<uint8_t>& aSignature,
|
const nsTArray<uint8_t>& aSignature,
|
||||||
const nsTArray<uint8_t>& aUserHandle,
|
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),
|
: mClientDataJSON(aClientDataJSON),
|
||||||
mAuthenticatorAttachment(aAuthenticatorAttachment) {
|
mAuthenticatorAttachment(aAuthenticatorAttachment),
|
||||||
|
mLargeBlobWritten(aLargeBlobWritten) {
|
||||||
mAuthenticatorData.AppendElements(aAuthenticatorData);
|
mAuthenticatorData.AppendElements(aAuthenticatorData);
|
||||||
mCredentialId.AppendElements(aCredentialId);
|
mCredentialId.AppendElements(aCredentialId);
|
||||||
mSignature.AppendElements(aSignature);
|
mSignature.AppendElements(aSignature);
|
||||||
mUserHandle.AppendElements(aUserHandle);
|
mUserHandle.AppendElements(aUserHandle);
|
||||||
|
if (aLargeBlobValue.isSome()) {
|
||||||
|
mLargeBlobValue.emplace(aLargeBlobValue->Length());
|
||||||
|
mLargeBlobValue->Assign(aLargeBlobValue.ref());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_ANDROID
|
#ifdef MOZ_WIDGET_ANDROID
|
||||||
@@ -205,7 +221,8 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
WebAuthnSignResult(nsCString& aClientDataJSON, PCWEBAUTHN_ASSERTION aResponse)
|
WebAuthnSignResult(nsCString& aClientDataJSON, DWORD aCredLargeBlobOperation,
|
||||||
|
PCWEBAUTHN_ASSERTION aResponse)
|
||||||
: mClientDataJSON(Some(aClientDataJSON)) {
|
: mClientDataJSON(Some(aClientDataJSON)) {
|
||||||
mSignature.AppendElements(aResponse->pbSignature, aResponse->cbSignature);
|
mSignature.AppendElements(aResponse->pbSignature, aResponse->cbSignature);
|
||||||
|
|
||||||
@@ -218,6 +235,26 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
|||||||
aResponse->cbAuthenticatorData);
|
aResponse->cbAuthenticatorData);
|
||||||
|
|
||||||
mAuthenticatorAttachment = Nothing(); // not available
|
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->dwVersion >= WEBAUTHN_ASSERTION_VERSION_3) {
|
||||||
if (aResponse->pHmacSecret) {
|
if (aResponse->pHmacSecret) {
|
||||||
if (aResponse->pHmacSecret->cbFirst > 0) {
|
if (aResponse->pHmacSecret->cbFirst > 0) {
|
||||||
@@ -245,6 +282,8 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
|||||||
nsTArray<uint8_t> mUserHandle;
|
nsTArray<uint8_t> mUserHandle;
|
||||||
Maybe<nsString> mAuthenticatorAttachment;
|
Maybe<nsString> mAuthenticatorAttachment;
|
||||||
Maybe<bool> mUsedAppId;
|
Maybe<bool> mUsedAppId;
|
||||||
|
Maybe<nsTArray<uint8_t>> mLargeBlobValue;
|
||||||
|
Maybe<bool> mLargeBlobWritten;
|
||||||
Maybe<nsTArray<uint8_t>> mPrfFirst;
|
Maybe<nsTArray<uint8_t>> mPrfFirst;
|
||||||
Maybe<nsTArray<uint8_t>> mPrfSecond;
|
Maybe<nsTArray<uint8_t>> mPrfSecond;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,6 +73,18 @@ nsresult AssembleClientData(WindowGlobalParent* aManager,
|
|||||||
return NS_OK;
|
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() {
|
void WebAuthnTransactionParent::CompleteTransaction() {
|
||||||
if (mTransactionId.isSome()) {
|
if (mTransactionId.isSome()) {
|
||||||
if (mRegisterPromiseRequest.Exists()) {
|
if (mRegisterPromiseRequest.Exists()) {
|
||||||
@@ -227,6 +239,17 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
|
|||||||
WebAuthnExtensionResultHmacSecret(hmacCreateSecret));
|
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<bool> prfEnabledMaybe = Nothing();
|
||||||
Maybe<WebAuthnExtensionPrfValues> prfResults = Nothing();
|
Maybe<WebAuthnExtensionPrfValues> prfResults = Nothing();
|
||||||
@@ -342,6 +365,9 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
|
|||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool requestIncludesLargeBlobRead =
|
||||||
|
GetAssertionRequestIncludesLargeBlobRead(aTransactionInfo);
|
||||||
|
|
||||||
RefPtr<WebAuthnSignPromiseHolder> promiseHolder =
|
RefPtr<WebAuthnSignPromiseHolder> promiseHolder =
|
||||||
new WebAuthnSignPromiseHolder(GetCurrentSerialEventTarget());
|
new WebAuthnSignPromiseHolder(GetCurrentSerialEventTarget());
|
||||||
|
|
||||||
@@ -350,7 +376,7 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
|
|||||||
->Then(
|
->Then(
|
||||||
GetCurrentSerialEventTarget(), __func__,
|
GetCurrentSerialEventTarget(), __func__,
|
||||||
[self = RefPtr{this}, inputClientData = clientDataJSON,
|
[self = RefPtr{this}, inputClientData = clientDataJSON,
|
||||||
resolver = std::move(aResolver)](
|
requestIncludesLargeBlobRead, resolver = std::move(aResolver)](
|
||||||
const WebAuthnSignPromise::ResolveOrRejectValue& aValue) {
|
const WebAuthnSignPromise::ResolveOrRejectValue& aValue) {
|
||||||
self->CompleteTransaction();
|
self->CompleteTransaction();
|
||||||
|
|
||||||
@@ -414,6 +440,32 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
|
|||||||
extensions.AppendElement(WebAuthnExtensionResultAppId(usedAppId));
|
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;
|
Maybe<WebAuthnExtensionPrfValues> prfResults;
|
||||||
bool prfMaybe = false;
|
bool prfMaybe = false;
|
||||||
|
|||||||
@@ -308,6 +308,23 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
|||||||
// AttestationConveyance
|
// AttestationConveyance
|
||||||
DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
|
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
|
// Prf
|
||||||
BOOL winEnablePrf = FALSE;
|
BOOL winEnablePrf = FALSE;
|
||||||
|
|
||||||
@@ -366,8 +383,7 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
|||||||
// Attachment
|
// Attachment
|
||||||
DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
|
DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
|
||||||
nsString authenticatorAttachment;
|
nsString authenticatorAttachment;
|
||||||
nsresult rv =
|
rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
|
||||||
aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
|
|
||||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
aPromise->Reject(rv);
|
aPromise->Reject(rv);
|
||||||
@@ -553,7 +569,7 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
|||||||
&cancellationId, // CancellationId
|
&cancellationId, // CancellationId
|
||||||
pExcludeCredentialList,
|
pExcludeCredentialList,
|
||||||
WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
|
WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
|
||||||
WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
|
largeBlobSupport, // LargeBlobSupport
|
||||||
winPreferResidentKey, // PreferResidentKey
|
winPreferResidentKey, // PreferResidentKey
|
||||||
winPrivateBrowsing, // BrowserInPrivateMode
|
winPrivateBrowsing, // BrowserInPrivateMode
|
||||||
winEnablePrf, // EnablePrf
|
winEnablePrf, // EnablePrf
|
||||||
@@ -739,6 +755,32 @@ void WinWebAuthnService::DoGetAssertion(
|
|||||||
winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
|
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
|
// PRF inputs
|
||||||
WEBAUTHN_HMAC_SECRET_SALT_VALUES* pPrfInputs = nullptr;
|
WEBAUTHN_HMAC_SECRET_SALT_VALUES* pPrfInputs = nullptr;
|
||||||
WEBAUTHN_HMAC_SECRET_SALT_VALUES prfInputs = {0};
|
WEBAUTHN_HMAC_SECRET_SALT_VALUES prfInputs = {0};
|
||||||
@@ -906,9 +948,9 @@ void WinWebAuthnService::DoGetAssertion(
|
|||||||
pbAppIdUsed,
|
pbAppIdUsed,
|
||||||
&aCancellationId, // CancellationId
|
&aCancellationId, // CancellationId
|
||||||
pAllowCredentialList,
|
pAllowCredentialList,
|
||||||
WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE,
|
credLargeBlobOperation, // CredLargeBlobOperation
|
||||||
0, // Size of CredLargeBlob
|
credLargeBlobSize, // Size of CredLargeBlob
|
||||||
NULL, // CredLargeBlob
|
credLargeBlob, // CredLargeBlob
|
||||||
pPrfInputs, // HmacSecretSaltValues
|
pPrfInputs, // HmacSecretSaltValues
|
||||||
winPrivateBrowsing, // BrowserInPrivateMode
|
winPrivateBrowsing, // BrowserInPrivateMode
|
||||||
NULL, // LinkedDevice
|
NULL, // LinkedDevice
|
||||||
@@ -928,8 +970,8 @@ void WinWebAuthnService::DoGetAssertion(
|
|||||||
&pWebAuthNAssertion);
|
&pWebAuthNAssertion);
|
||||||
|
|
||||||
if (hr == S_OK) {
|
if (hr == S_OK) {
|
||||||
RefPtr<WebAuthnSignResult> result =
|
RefPtr<WebAuthnSignResult> result = new WebAuthnSignResult(
|
||||||
new WebAuthnSignResult(clientDataJSON, pWebAuthNAssertion);
|
clientDataJSON, credLargeBlobOperation, pWebAuthNAssertion);
|
||||||
gWinWebauthnFreeAssertion(pWebAuthNAssertion);
|
gWinWebauthnFreeAssertion(pWebAuthNAssertion);
|
||||||
if (winAppIdentifier != nullptr) {
|
if (winAppIdentifier != nullptr) {
|
||||||
// The gWinWebauthnGetAssertion call modified bAppIdUsed through
|
// The gWinWebauthnGetAssertion call modified bAppIdUsed through
|
||||||
|
|||||||
@@ -216,6 +216,11 @@ impl WebAuthnRegisterResult {
|
|||||||
Ok(hmac_create_secret)
|
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);
|
xpcom_method!(get_prf_enabled => GetPrfEnabled() -> bool);
|
||||||
fn get_prf_enabled(&self) -> Result<bool, nsresult> {
|
fn get_prf_enabled(&self) -> Result<bool, nsresult> {
|
||||||
match self.result.borrow().extensions.prf {
|
match self.result.borrow().extensions.prf {
|
||||||
@@ -412,6 +417,16 @@ impl WebAuthnSignResult {
|
|||||||
Err(NS_ERROR_NOT_IMPLEMENTED)
|
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);
|
xpcom_method!(get_prf_maybe => GetPrfMaybe() -> bool);
|
||||||
/// Return true if a PRF output is present, even if all attributes are absent.
|
/// Return true if a PRF output is present, even if all attributes are absent.
|
||||||
fn get_prf_maybe(&self) -> Result<bool, nsresult> {
|
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 boolean prf;
|
||||||
[must_use] readonly attribute Array<octet> prfEvalFirst;
|
[must_use] readonly attribute Array<octet> prfEvalFirst;
|
||||||
[must_use] readonly attribute Array<octet> prfEvalSecond;
|
[must_use] readonly attribute Array<octet> prfEvalSecond;
|
||||||
|
[must_use] readonly attribute boolean largeBlobSupportRequired;
|
||||||
|
|
||||||
// Options.
|
// Options.
|
||||||
readonly attribute AString residentKey;
|
readonly attribute AString residentKey;
|
||||||
@@ -97,6 +98,8 @@ interface nsIWebAuthnSignArgs : nsISupports {
|
|||||||
[must_use] readonly attribute Array<Array<octet> > prfEvalByCredentialEvalFirst;
|
[must_use] readonly attribute Array<Array<octet> > prfEvalByCredentialEvalFirst;
|
||||||
[must_use] readonly attribute Array<boolean> prfEvalByCredentialEvalSecondMaybe;
|
[must_use] readonly attribute Array<boolean> prfEvalByCredentialEvalSecondMaybe;
|
||||||
[must_use] readonly attribute Array<Array<octet> > prfEvalByCredentialEvalSecond;
|
[must_use] readonly attribute Array<Array<octet> > prfEvalByCredentialEvalSecond;
|
||||||
|
[must_use] readonly attribute boolean largeBlobRead;
|
||||||
|
[must_use] readonly attribute Array<octet> largeBlobWrite;
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
[must_use] readonly attribute AString userVerification;
|
[must_use] readonly attribute AString userVerification;
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ interface nsIWebAuthnRegisterResult : nsISupports {
|
|||||||
|
|
||||||
[must_use] attribute boolean credPropsRk;
|
[must_use] attribute boolean credPropsRk;
|
||||||
|
|
||||||
|
readonly attribute boolean largeBlobSupported;
|
||||||
|
|
||||||
readonly attribute boolean prfEnabled;
|
readonly attribute boolean prfEnabled;
|
||||||
readonly attribute Array<octet> prfResultsFirst;
|
readonly attribute Array<octet> prfResultsFirst;
|
||||||
readonly attribute Array<octet> prfResultsSecond;
|
readonly attribute Array<octet> prfResultsSecond;
|
||||||
@@ -65,6 +67,9 @@ interface nsIWebAuthnSignResult : nsISupports {
|
|||||||
// appId field of AuthenticationExtensionsClientOutputs (Optional)
|
// appId field of AuthenticationExtensionsClientOutputs (Optional)
|
||||||
[must_use] attribute boolean usedAppId;
|
[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 boolean prfMaybe; // prf output can be present but empty
|
||||||
readonly attribute Array<octet> prfResultsFirst;
|
readonly attribute Array<octet> prfResultsFirst;
|
||||||
readonly attribute Array<octet> prfResultsSecond;
|
readonly attribute Array<octet> prfResultsSecond;
|
||||||
|
|||||||
@@ -319,6 +319,47 @@ partial dictionary AuthenticationExtensionsClientOutputsJSON {
|
|||||||
boolean hmacCreateSecret;
|
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
|
// minPinLength
|
||||||
// <https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-minpinlength-extension>
|
// <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]
|
[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]
|
[navigator.credentials.create() with largeBlob.support set to preferred and not supported by authenticator]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
[getcredential-large-blob-not-supported.https.html]
|
[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]
|
[navigator.credentials.get() with largeBlob.write set without authenticator support]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
[getcredential-large-blob-supported.https.html]
|
[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]
|
[navigator.credentials.get() read and write blob]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|||||||
Reference in New Issue
Block a user