зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1464015 - Web Authentication - Rework IPC layer for future Android/Windows support r=jcj
Reviewers: jcj Reviewed By: jcj Subscribers: mgoodwin Bug #: 1464015 Differential Revision: https://phabricator.services.mozilla.com/D1378
This commit is contained in:
Родитель
e09ad9e15c
Коммит
2a252e45a4
|
@ -7109,7 +7109,7 @@ var WebAuthnPromptHelper = {
|
||||||
label: gNavigatorBundle.getString("webauthn.proceed"),
|
label: gNavigatorBundle.getString("webauthn.proceed"),
|
||||||
accessKey: gNavigatorBundle.getString("webauthn.proceed.accesskey"),
|
accessKey: gNavigatorBundle.getString("webauthn.proceed.accesskey"),
|
||||||
callback(state) {
|
callback(state) {
|
||||||
mgr.resumeRegister(tid, !state.checkboxChecked);
|
mgr.resumeRegister(tid, state.checkboxChecked);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
145
dom/u2f/U2F.cpp
145
dom/u2f/U2F.cpp
|
@ -11,13 +11,9 @@
|
||||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||||
#include "mozilla/dom/WebAuthnUtil.h"
|
#include "mozilla/dom/WebAuthnUtil.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsICryptoHash.h"
|
|
||||||
#include "nsIEffectiveTLDService.h"
|
#include "nsIEffectiveTLDService.h"
|
||||||
#include "nsNetCID.h"
|
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsURLParsers.h"
|
#include "nsURLParsers.h"
|
||||||
#include "U2FUtil.h"
|
|
||||||
#include "hasht.h"
|
|
||||||
|
|
||||||
using namespace mozilla::ipc;
|
using namespace mozilla::ipc;
|
||||||
|
|
||||||
|
@ -129,59 +125,6 @@ RegisteredKeysToScopedCredentialList(const nsAString& aAppId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static nsresult
|
|
||||||
BuildTransactionHashes(const nsCString& aRpId,
|
|
||||||
const nsCString& aClientDataJSON,
|
|
||||||
/* out */ CryptoBuffer& aRpIdHash,
|
|
||||||
/* out */ CryptoBuffer& aClientDataHash)
|
|
||||||
{
|
|
||||||
nsresult srv;
|
|
||||||
nsCOMPtr<nsICryptoHash> hashService =
|
|
||||||
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
|
|
||||||
if (NS_FAILED(srv)) {
|
|
||||||
return srv;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aRpIdHash.SetLength(SHA256_LENGTH, fallible)) {
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
srv = HashCString(hashService, aRpId, aRpIdHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aClientDataHash.SetLength(SHA256_LENGTH, fallible)) {
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
srv = HashCString(hashService, aClientDataJSON, aClientDataHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MOZ_LOG_TEST(gU2FLog, LogLevel::Debug)) {
|
|
||||||
nsString base64;
|
|
||||||
Unused << NS_WARN_IF(NS_FAILED(aRpIdHash.ToJwkBase64(base64)));
|
|
||||||
|
|
||||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
|
||||||
("dom::U2FManager::RpID: %s", aRpId.get()));
|
|
||||||
|
|
||||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
|
||||||
("dom::U2FManager::Rp ID Hash (base64): %s",
|
|
||||||
NS_ConvertUTF16toUTF8(base64).get()));
|
|
||||||
|
|
||||||
Unused << NS_WARN_IF(NS_FAILED(aClientDataHash.ToJwkBase64(base64)));
|
|
||||||
|
|
||||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
|
||||||
("dom::U2FManager::Client Data JSON: %s", aClientDataJSON.get()));
|
|
||||||
|
|
||||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
|
||||||
("dom::U2FManager::Client Data Hash (base64): %s",
|
|
||||||
NS_ConvertUTF16toUTF8(base64).get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* U2F JavaScript API Implementation
|
* U2F JavaScript API Implementation
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
@ -280,17 +223,18 @@ U2F::Register(const nsAString& aAppId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce the AppParam from the current AppID
|
|
||||||
nsCString cAppId = NS_ConvertUTF16toUTF8(adjustedAppId);
|
|
||||||
|
|
||||||
nsAutoString clientDataJSON;
|
nsAutoString clientDataJSON;
|
||||||
|
|
||||||
// Pick the first valid RegisterRequest; we can only work with one.
|
// Pick the first valid RegisterRequest; we can only work with one.
|
||||||
|
CryptoBuffer challenge;
|
||||||
for (const RegisterRequest& req : aRegisterRequests) {
|
for (const RegisterRequest& req : aRegisterRequests) {
|
||||||
if (!req.mChallenge.WasPassed() || !req.mVersion.WasPassed() ||
|
if (!req.mChallenge.WasPassed() || !req.mVersion.WasPassed() ||
|
||||||
req.mVersion.Value() != kRequiredU2FVersion) {
|
req.mVersion.Value() != kRequiredU2FVersion) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!challenge.Assign(NS_ConvertUTF16toUTF8(req.mChallenge.Value()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult rv = AssembleClientData(mOrigin, kFinishEnrollment,
|
nsresult rv = AssembleClientData(mOrigin, kFinishEnrollment,
|
||||||
req.mChallenge.Value(), clientDataJSON);
|
req.mChallenge.Value(), clientDataJSON);
|
||||||
|
@ -312,17 +256,6 @@ U2F::Register(const nsAString& aAppId,
|
||||||
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
|
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
|
||||||
excludeList);
|
excludeList);
|
||||||
|
|
||||||
auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
|
|
||||||
|
|
||||||
CryptoBuffer rpIdHash, clientDataHash;
|
|
||||||
if (NS_FAILED(BuildTransactionHashes(cAppId, clientData,
|
|
||||||
rpIdHash, clientDataHash))) {
|
|
||||||
RegisterResponse response;
|
|
||||||
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
|
||||||
ExecuteCallback(response, callback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MaybeCreateBackgroundActor()) {
|
if (!MaybeCreateBackgroundActor()) {
|
||||||
RegisterResponse response;
|
RegisterResponse response;
|
||||||
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
||||||
|
@ -332,27 +265,19 @@ U2F::Register(const nsAString& aAppId,
|
||||||
|
|
||||||
ListenForVisibilityEvents();
|
ListenForVisibilityEvents();
|
||||||
|
|
||||||
// Always blank for U2F
|
NS_ConvertUTF16toUTF8 clientData(clientDataJSON);
|
||||||
nsTArray<WebAuthnExtension> extensions;
|
|
||||||
|
|
||||||
// Default values for U2F.
|
|
||||||
WebAuthnAuthenticatorSelection authSelection(false /* requireResidentKey */,
|
|
||||||
false /* requireUserVerification */,
|
|
||||||
false /* requirePlatformAttachment */);
|
|
||||||
|
|
||||||
uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
|
uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
|
||||||
|
|
||||||
WebAuthnMakeCredentialInfo info(mOrigin,
|
WebAuthnMakeCredentialInfo info(mOrigin,
|
||||||
rpIdHash,
|
adjustedAppId,
|
||||||
clientDataHash,
|
challenge,
|
||||||
|
clientData,
|
||||||
adjustedTimeoutMillis,
|
adjustedTimeoutMillis,
|
||||||
excludeList,
|
excludeList,
|
||||||
extensions,
|
null_t() /* no extra info for U2F */);
|
||||||
authSelection,
|
|
||||||
false /* RequestDirectAttestation */);
|
|
||||||
|
|
||||||
MOZ_ASSERT(mTransaction.isNothing());
|
MOZ_ASSERT(mTransaction.isNothing());
|
||||||
mTransaction = Some(U2FTransaction(clientData, Move(AsVariant(callback))));
|
mTransaction = Some(U2FTransaction(Move(AsVariant(callback))));
|
||||||
mChild->SendRequestRegister(mTransaction.ref().mId, info);
|
mChild->SendRequestRegister(mTransaction.ref().mId, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,14 +297,20 @@ U2F::FinishMakeCredential(const uint64_t& aTransactionId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A CTAP2 response.
|
||||||
|
if (aResult.RegistrationData().Length() == 0) {
|
||||||
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CryptoBuffer clientDataBuf;
|
CryptoBuffer clientDataBuf;
|
||||||
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
|
if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
|
||||||
RejectTransaction(NS_ERROR_ABORT);
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer regBuf;
|
CryptoBuffer regBuf;
|
||||||
if (NS_WARN_IF(!regBuf.Assign(aResult.RegBuffer()))) {
|
if (NS_WARN_IF(!regBuf.Assign(aResult.RegistrationData()))) {
|
||||||
RejectTransaction(NS_ERROR_ABORT);
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -455,22 +386,19 @@ U2F::Sign(const nsAString& aAppId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the key list, if any
|
CryptoBuffer challenge;
|
||||||
nsTArray<WebAuthnScopedCredential> permittedList;
|
if (!challenge.Assign(NS_ConvertUTF16toUTF8(aChallenge))) {
|
||||||
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
|
|
||||||
permittedList);
|
|
||||||
|
|
||||||
auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
|
|
||||||
|
|
||||||
CryptoBuffer rpIdHash, clientDataHash;
|
|
||||||
if (NS_FAILED(BuildTransactionHashes(cAppId, clientData,
|
|
||||||
rpIdHash, clientDataHash))) {
|
|
||||||
SignResponse response;
|
SignResponse response;
|
||||||
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
||||||
ExecuteCallback(response, callback);
|
ExecuteCallback(response, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the key list, if any
|
||||||
|
nsTArray<WebAuthnScopedCredential> permittedList;
|
||||||
|
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
|
||||||
|
permittedList);
|
||||||
|
|
||||||
if (!MaybeCreateBackgroundActor()) {
|
if (!MaybeCreateBackgroundActor()) {
|
||||||
SignResponse response;
|
SignResponse response;
|
||||||
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
|
||||||
|
@ -483,18 +411,19 @@ U2F::Sign(const nsAString& aAppId,
|
||||||
// Always blank for U2F
|
// Always blank for U2F
|
||||||
nsTArray<WebAuthnExtension> extensions;
|
nsTArray<WebAuthnExtension> extensions;
|
||||||
|
|
||||||
|
NS_ConvertUTF16toUTF8 clientData(clientDataJSON);
|
||||||
uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
|
uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
|
||||||
|
|
||||||
WebAuthnGetAssertionInfo info(mOrigin,
|
WebAuthnGetAssertionInfo info(mOrigin,
|
||||||
rpIdHash,
|
adjustedAppId,
|
||||||
clientDataHash,
|
challenge,
|
||||||
|
clientData,
|
||||||
adjustedTimeoutMillis,
|
adjustedTimeoutMillis,
|
||||||
permittedList,
|
permittedList,
|
||||||
false, /* requireUserVerification */
|
null_t() /* no extra info for U2F */);
|
||||||
extensions);
|
|
||||||
|
|
||||||
MOZ_ASSERT(mTransaction.isNothing());
|
MOZ_ASSERT(mTransaction.isNothing());
|
||||||
mTransaction = Some(U2FTransaction(clientData, Move(AsVariant(callback))));
|
mTransaction = Some(U2FTransaction(Move(AsVariant(callback))));
|
||||||
mChild->SendRequestSign(mTransaction.ref().mId, info);
|
mChild->SendRequestSign(mTransaction.ref().mId, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,20 +443,26 @@ U2F::FinishGetAssertion(const uint64_t& aTransactionId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A CTAP2 response.
|
||||||
|
if (aResult.SignatureData().Length() == 0) {
|
||||||
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CryptoBuffer clientDataBuf;
|
CryptoBuffer clientDataBuf;
|
||||||
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
|
if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
|
||||||
RejectTransaction(NS_ERROR_ABORT);
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer credBuf;
|
CryptoBuffer credBuf;
|
||||||
if (NS_WARN_IF(!credBuf.Assign(aResult.CredentialID()))) {
|
if (NS_WARN_IF(!credBuf.Assign(aResult.KeyHandle()))) {
|
||||||
RejectTransaction(NS_ERROR_ABORT);
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer sigBuf;
|
CryptoBuffer sigBuf;
|
||||||
if (NS_WARN_IF(!sigBuf.Assign(aResult.SigBuffer()))) {
|
if (NS_WARN_IF(!sigBuf.Assign(aResult.SignatureData()))) {
|
||||||
RejectTransaction(NS_ERROR_ABORT);
|
RejectTransaction(NS_ERROR_ABORT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,8 @@ class U2FTransaction
|
||||||
nsMainThreadPtrHandle<U2FSignCallback>> U2FCallback;
|
nsMainThreadPtrHandle<U2FSignCallback>> U2FCallback;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit U2FTransaction(const nsCString& aClientData,
|
explicit U2FTransaction(const U2FCallback&& aCallback)
|
||||||
const U2FCallback&& aCallback)
|
: mCallback(Move(aCallback))
|
||||||
: mClientData(aClientData)
|
|
||||||
, mCallback(Move(aCallback))
|
|
||||||
, mId(NextId())
|
, mId(NextId())
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mId > 0);
|
MOZ_ASSERT(mId > 0);
|
||||||
|
@ -63,9 +61,6 @@ public:
|
||||||
return mCallback.as<nsMainThreadPtrHandle<U2FSignCallback>>();
|
return mCallback.as<nsMainThreadPtrHandle<U2FSignCallback>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client data used to assemble reply objects.
|
|
||||||
nsCString mClientData;
|
|
||||||
|
|
||||||
// The callback passed to the API.
|
// The callback passed to the API.
|
||||||
U2FCallback mCallback;
|
U2FCallback mCallback;
|
||||||
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef mozilla_dom_U2FUtil_h
|
|
||||||
#define mozilla_dom_U2FUtil_h
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
namespace dom {
|
|
||||||
|
|
||||||
static nsresult
|
|
||||||
HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
|
|
||||||
/* out */ CryptoBuffer& aOut)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aHashService);
|
|
||||||
|
|
||||||
nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = aHashService->Update(
|
|
||||||
reinterpret_cast<const uint8_t*>(aIn.BeginReading()), aIn.Length());
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoCString fullHash;
|
|
||||||
// Passing false below means we will get a binary result rather than a
|
|
||||||
// base64-encoded string.
|
|
||||||
rv = aHashService->Finish(false, fullHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
aOut.Assign(fullHash);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dom
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_dom_U2FUtil_h
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ with Files("**"):
|
||||||
EXPORTS.mozilla.dom += [
|
EXPORTS.mozilla.dom += [
|
||||||
'U2F.h',
|
'U2F.h',
|
||||||
'U2FAuthenticator.h',
|
'U2FAuthenticator.h',
|
||||||
'U2FUtil.h',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
include protocol PBackground;
|
include protocol PBackground;
|
||||||
|
|
||||||
|
using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
@ -46,37 +48,63 @@ union WebAuthnExtensionResult {
|
||||||
WebAuthnExtensionResultAppId;
|
WebAuthnExtensionResultAppId;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WebAuthnMakeCredentialInfo {
|
struct WebAuthnMakeCredentialExtraInfo {
|
||||||
nsString Origin;
|
|
||||||
uint8_t[] RpIdHash;
|
|
||||||
uint8_t[] ClientDataHash;
|
|
||||||
uint32_t TimeoutMS;
|
|
||||||
WebAuthnScopedCredential[] ExcludeList;
|
|
||||||
WebAuthnExtension[] Extensions;
|
WebAuthnExtension[] Extensions;
|
||||||
WebAuthnAuthenticatorSelection AuthenticatorSelection;
|
WebAuthnAuthenticatorSelection AuthenticatorSelection;
|
||||||
bool RequestDirectAttestation;
|
bool RequestDirectAttestation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union WebAuthnMaybeMakeCredentialExtraInfo {
|
||||||
|
WebAuthnMakeCredentialExtraInfo;
|
||||||
|
null_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebAuthnMakeCredentialInfo {
|
||||||
|
nsString Origin;
|
||||||
|
nsString RpId;
|
||||||
|
uint8_t[] Challenge;
|
||||||
|
nsCString ClientDataJSON;
|
||||||
|
uint32_t TimeoutMS;
|
||||||
|
WebAuthnScopedCredential[] ExcludeList;
|
||||||
|
WebAuthnMaybeMakeCredentialExtraInfo Extra;
|
||||||
|
};
|
||||||
|
|
||||||
struct WebAuthnMakeCredentialResult {
|
struct WebAuthnMakeCredentialResult {
|
||||||
uint8_t[] RegBuffer;
|
nsCString ClientDataJSON;
|
||||||
bool DirectAttestationPermitted;
|
uint8_t[] AttestationObject;
|
||||||
|
uint8_t[] KeyHandle;
|
||||||
|
/* Might be empty if the token implementation doesn't support CTAP1. */
|
||||||
|
uint8_t[] RegistrationData;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WebAuthnGetAssertionExtraInfo {
|
||||||
|
WebAuthnExtension[] Extensions;
|
||||||
|
bool RequireUserVerification;
|
||||||
|
};
|
||||||
|
|
||||||
|
union WebAuthnMaybeGetAssertionExtraInfo {
|
||||||
|
WebAuthnGetAssertionExtraInfo;
|
||||||
|
null_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WebAuthnGetAssertionInfo {
|
struct WebAuthnGetAssertionInfo {
|
||||||
nsString Origin;
|
nsString Origin;
|
||||||
uint8_t[] RpIdHash;
|
nsString RpId;
|
||||||
uint8_t[] ClientDataHash;
|
uint8_t[] Challenge;
|
||||||
|
nsCString ClientDataJSON;
|
||||||
uint32_t TimeoutMS;
|
uint32_t TimeoutMS;
|
||||||
WebAuthnScopedCredential[] AllowList;
|
WebAuthnScopedCredential[] AllowList;
|
||||||
bool RequireUserVerification;
|
WebAuthnMaybeGetAssertionExtraInfo Extra;
|
||||||
WebAuthnExtension[] Extensions;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WebAuthnGetAssertionResult {
|
struct WebAuthnGetAssertionResult {
|
||||||
uint8_t[] RpIdHash;
|
nsCString ClientDataJSON;
|
||||||
uint8_t[] CredentialID;
|
uint8_t[] KeyHandle;
|
||||||
uint8_t[] SigBuffer;
|
uint8_t[] Signature;
|
||||||
|
uint8_t[] AuthenticatorData;
|
||||||
WebAuthnExtensionResult[] Extensions;
|
WebAuthnExtensionResult[] Extensions;
|
||||||
|
/* Might be empty if the token implementation doesn't support CTAP1. */
|
||||||
|
uint8_t[] SignatureData;
|
||||||
};
|
};
|
||||||
|
|
||||||
async protocol PWebAuthnTransaction {
|
async protocol PWebAuthnTransaction {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/dom/U2FHIDTokenManager.h"
|
#include "mozilla/dom/U2FHIDTokenManager.h"
|
||||||
|
#include "mozilla/dom/WebAuthnUtil.h"
|
||||||
#include "mozilla/ipc/BackgroundParent.h"
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
#include "mozilla/StaticMutex.h"
|
#include "mozilla/StaticMutex.h"
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ u2f_sign_callback(uint64_t aTransactionId, rust_u2f_result* aResult)
|
||||||
NS_DISPATCH_NORMAL));
|
NS_DISPATCH_NORMAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
U2FHIDTokenManager::U2FHIDTokenManager() : mTransactionId(0)
|
U2FHIDTokenManager::U2FHIDTokenManager()
|
||||||
{
|
{
|
||||||
StaticMutexAutoLock lock(gInstanceMutex);
|
StaticMutexAutoLock lock(gInstanceMutex);
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
@ -84,7 +85,7 @@ U2FHIDTokenManager::Drop()
|
||||||
mU2FManager = nullptr;
|
mU2FManager = nullptr;
|
||||||
|
|
||||||
// Reset transaction ID so that queued runnables exit early.
|
// Reset transaction ID so that queued runnables exit early.
|
||||||
mTransactionId = 0;
|
mTransaction.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// A U2F Register operation causes a new key pair to be generated by the token.
|
// A U2F Register operation causes a new key pair to be generated by the token.
|
||||||
|
@ -107,13 +108,16 @@ U2FHIDTokenManager::Drop()
|
||||||
// * attestation signature
|
// * attestation signature
|
||||||
//
|
//
|
||||||
RefPtr<U2FRegisterPromise>
|
RefPtr<U2FRegisterPromise>
|
||||||
U2FHIDTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
U2FHIDTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation)
|
||||||
{
|
{
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
uint64_t registerFlags = 0;
|
uint64_t registerFlags = 0;
|
||||||
|
|
||||||
const WebAuthnAuthenticatorSelection& sel = aInfo.AuthenticatorSelection();
|
if (aInfo.Extra().type() != WebAuthnMaybeMakeCredentialExtraInfo::Tnull_t) {
|
||||||
|
const auto& extra = aInfo.Extra().get_WebAuthnMakeCredentialExtraInfo();
|
||||||
|
const WebAuthnAuthenticatorSelection& sel = extra.AuthenticatorSelection();
|
||||||
|
|
||||||
// Set flags for credential creation.
|
// Set flags for credential creation.
|
||||||
if (sel.requireResidentKey()) {
|
if (sel.requireResidentKey()) {
|
||||||
|
@ -125,23 +129,35 @@ U2FHIDTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
if (sel.requirePlatformAttachment()) {
|
if (sel.requirePlatformAttachment()) {
|
||||||
registerFlags |= U2F_FLAG_REQUIRE_PLATFORM_ATTACHMENT;
|
registerFlags |= U2F_FLAG_REQUIRE_PLATFORM_ATTACHMENT;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer rpIdHash, clientDataHash;
|
||||||
|
NS_ConvertUTF16toUTF8 rpId(aInfo.RpId());
|
||||||
|
nsresult rv = BuildTransactionHashes(rpId, aInfo.ClientDataJSON(),
|
||||||
|
rpIdHash, clientDataHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
ClearPromises();
|
ClearPromises();
|
||||||
mCurrentAppId = aInfo.RpIdHash();
|
mTransaction.reset();
|
||||||
mTransactionId = rust_u2f_mgr_register(mU2FManager,
|
uint64_t tid = rust_u2f_mgr_register(mU2FManager,
|
||||||
registerFlags,
|
registerFlags,
|
||||||
(uint64_t)aInfo.TimeoutMS(),
|
(uint64_t)aInfo.TimeoutMS(),
|
||||||
u2f_register_callback,
|
u2f_register_callback,
|
||||||
aInfo.ClientDataHash().Elements(),
|
clientDataHash.Elements(),
|
||||||
aInfo.ClientDataHash().Length(),
|
clientDataHash.Length(),
|
||||||
aInfo.RpIdHash().Elements(),
|
rpIdHash.Elements(),
|
||||||
aInfo.RpIdHash().Length(),
|
rpIdHash.Length(),
|
||||||
U2FKeyHandles(aInfo.ExcludeList()).Get());
|
U2FKeyHandles(aInfo.ExcludeList()).Get());
|
||||||
|
|
||||||
if (mTransactionId == 0) {
|
if (tid == 0) {
|
||||||
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mTransaction = Some(Transaction(tid, rpIdHash, aInfo.ClientDataJSON(),
|
||||||
|
aForceNoneAttestation));
|
||||||
|
|
||||||
return mRegisterPromise.Ensure(__func__);
|
return mRegisterPromise.Ensure(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,37 +182,50 @@ U2FHIDTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
|
||||||
{
|
{
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
|
CryptoBuffer rpIdHash, clientDataHash;
|
||||||
|
NS_ConvertUTF16toUTF8 rpId(aInfo.RpId());
|
||||||
|
nsresult rv = BuildTransactionHashes(rpId, aInfo.ClientDataJSON(),
|
||||||
|
rpIdHash, clientDataHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t signFlags = 0;
|
uint64_t signFlags = 0;
|
||||||
|
nsTArray<nsTArray<uint8_t>> appIds;
|
||||||
|
appIds.AppendElement(rpIdHash);
|
||||||
|
|
||||||
|
if (aInfo.Extra().type() != WebAuthnMaybeGetAssertionExtraInfo::Tnull_t) {
|
||||||
|
const auto& extra = aInfo.Extra().get_WebAuthnGetAssertionExtraInfo();
|
||||||
|
|
||||||
// Set flags for credential requests.
|
// Set flags for credential requests.
|
||||||
if (aInfo.RequireUserVerification()) {
|
if (extra.RequireUserVerification()) {
|
||||||
signFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
|
signFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
mCurrentAppId = aInfo.RpIdHash();
|
|
||||||
nsTArray<nsTArray<uint8_t>> appIds;
|
|
||||||
appIds.AppendElement(mCurrentAppId);
|
|
||||||
|
|
||||||
// Process extensions.
|
// Process extensions.
|
||||||
for (const WebAuthnExtension& ext: aInfo.Extensions()) {
|
for (const WebAuthnExtension& ext: extra.Extensions()) {
|
||||||
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
|
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
|
||||||
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
|
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ClearPromises();
|
ClearPromises();
|
||||||
mTransactionId = rust_u2f_mgr_sign(mU2FManager,
|
mTransaction.reset();
|
||||||
|
uint64_t tid = rust_u2f_mgr_sign(mU2FManager,
|
||||||
signFlags,
|
signFlags,
|
||||||
(uint64_t)aInfo.TimeoutMS(),
|
(uint64_t)aInfo.TimeoutMS(),
|
||||||
u2f_sign_callback,
|
u2f_sign_callback,
|
||||||
aInfo.ClientDataHash().Elements(),
|
clientDataHash.Elements(),
|
||||||
aInfo.ClientDataHash().Length(),
|
clientDataHash.Length(),
|
||||||
U2FAppIds(appIds).Get(),
|
U2FAppIds(appIds).Get(),
|
||||||
U2FKeyHandles(aInfo.AllowList()).Get());
|
U2FKeyHandles(aInfo.AllowList()).Get());
|
||||||
if (mTransactionId == 0) {
|
if (tid == 0) {
|
||||||
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mTransaction = Some(Transaction(tid, rpIdHash, aInfo.ClientDataJSON()));
|
||||||
|
|
||||||
return mSignPromise.Ensure(__func__);
|
return mSignPromise.Ensure(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +235,8 @@ U2FHIDTokenManager::Cancel()
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
ClearPromises();
|
ClearPromises();
|
||||||
mTransactionId = rust_u2f_mgr_cancel(mU2FManager);
|
rust_u2f_mgr_cancel(mU2FManager);
|
||||||
|
mTransaction.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -214,7 +244,8 @@ U2FHIDTokenManager::HandleRegisterResult(UniquePtr<U2FResult>&& aResult)
|
||||||
{
|
{
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
if (aResult->GetTransactionId() != mTransactionId) {
|
if (mTransaction.isNothing() ||
|
||||||
|
aResult->GetTransactionId() != mTransaction.ref().mId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +262,41 @@ U2FHIDTokenManager::HandleRegisterResult(UniquePtr<U2FResult>&& aResult)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will be set by the U2FTokenManager.
|
// Decompose the U2F registration packet
|
||||||
bool directAttestationPermitted = false;
|
CryptoBuffer pubKeyBuf;
|
||||||
WebAuthnMakeCredentialResult result(registration, directAttestationPermitted);
|
CryptoBuffer keyHandle;
|
||||||
|
CryptoBuffer attestationCertBuf;
|
||||||
|
CryptoBuffer signatureBuf;
|
||||||
|
|
||||||
|
CryptoBuffer regData;
|
||||||
|
regData.Assign(registration);
|
||||||
|
|
||||||
|
// Only handles attestation cert chains of length=1.
|
||||||
|
nsresult rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandle,
|
||||||
|
attestationCertBuf, signatureBuf);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
mRegisterPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer rpIdHashBuf;
|
||||||
|
if (!rpIdHashBuf.Assign(mTransaction.ref().mRpIdHash)) {
|
||||||
|
mRegisterPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer attObj;
|
||||||
|
rv = AssembleAttestationObject(rpIdHashBuf, pubKeyBuf, keyHandle,
|
||||||
|
attestationCertBuf, signatureBuf,
|
||||||
|
mTransaction.ref().mForceNoneAttestation,
|
||||||
|
attObj);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mRegisterPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAuthnMakeCredentialResult result(mTransaction.ref().mClientDataJSON,
|
||||||
|
attObj, keyHandle, regData);
|
||||||
mRegisterPromise.Resolve(Move(result), __func__);
|
mRegisterPromise.Resolve(Move(result), __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +305,8 @@ U2FHIDTokenManager::HandleSignResult(UniquePtr<U2FResult>&& aResult)
|
||||||
{
|
{
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
if (aResult->GetTransactionId() != mTransactionId) {
|
if (mTransaction.isNothing() ||
|
||||||
|
aResult->GetTransactionId() != mTransaction.ref().mId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,14 +335,51 @@ U2FHIDTokenManager::HandleSignResult(UniquePtr<U2FResult>&& aResult)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CryptoBuffer rawSignatureBuf;
|
||||||
|
if (!rawSignatureBuf.Assign(signature)) {
|
||||||
|
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsTArray<WebAuthnExtensionResult> extensions;
|
nsTArray<WebAuthnExtensionResult> extensions;
|
||||||
|
|
||||||
if (appId != mCurrentAppId) {
|
if (appId != mTransaction.ref().mRpIdHash) {
|
||||||
// Indicate to the RP that we used the FIDO appId.
|
// Indicate to the RP that we used the FIDO appId.
|
||||||
extensions.AppendElement(WebAuthnExtensionResultAppId(true));
|
extensions.AppendElement(WebAuthnExtensionResultAppId(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
WebAuthnGetAssertionResult result(appId, keyHandle, signature, extensions);
|
CryptoBuffer signatureBuf;
|
||||||
|
CryptoBuffer counterBuf;
|
||||||
|
uint8_t flags = 0;
|
||||||
|
nsresult rv = U2FDecomposeSignResponse(rawSignatureBuf, flags, counterBuf,
|
||||||
|
signatureBuf);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer chosenAppIdBuf;
|
||||||
|
if (!chosenAppIdBuf.Assign(appId)) {
|
||||||
|
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preserve the two LSBs of the flags byte, UP and RFU1.
|
||||||
|
// See <https://github.com/fido-alliance/fido-2-specs/pull/519>
|
||||||
|
flags &= 0b11;
|
||||||
|
|
||||||
|
CryptoBuffer emptyAttestationData;
|
||||||
|
CryptoBuffer authenticatorData;
|
||||||
|
rv = AssembleAuthenticatorData(chosenAppIdBuf, flags, counterBuf,
|
||||||
|
emptyAttestationData, authenticatorData);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAuthnGetAssertionResult result(mTransaction.ref().mClientDataJSON,
|
||||||
|
keyHandle, signatureBuf, authenticatorData,
|
||||||
|
extensions, rawSignatureBuf);
|
||||||
mSignPromise.Resolve(Move(result), __func__);
|
mSignPromise.Resolve(Move(result), __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,8 @@ public:
|
||||||
explicit U2FHIDTokenManager();
|
explicit U2FHIDTokenManager();
|
||||||
|
|
||||||
RefPtr<U2FRegisterPromise>
|
RefPtr<U2FRegisterPromise>
|
||||||
Register(const WebAuthnMakeCredentialInfo& aInfo) override;
|
Register(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation) override;
|
||||||
|
|
||||||
RefPtr<U2FSignPromise>
|
RefPtr<U2FSignPromise>
|
||||||
Sign(const WebAuthnGetAssertionInfo& aInfo) override;
|
Sign(const WebAuthnGetAssertionInfo& aInfo) override;
|
||||||
|
@ -153,9 +154,34 @@ private:
|
||||||
mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Transaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Transaction(uint64_t aId,
|
||||||
|
const nsTArray<uint8_t>& aRpIdHash,
|
||||||
|
const nsCString& aClientDataJSON,
|
||||||
|
bool aForceNoneAttestation = false)
|
||||||
|
: mId(aId)
|
||||||
|
, mRpIdHash(aRpIdHash)
|
||||||
|
, mClientDataJSON(aClientDataJSON)
|
||||||
|
, mForceNoneAttestation(aForceNoneAttestation)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// The transaction ID.
|
||||||
|
uint64_t mId;
|
||||||
|
|
||||||
|
// The RP ID hash.
|
||||||
|
nsTArray<uint8_t> mRpIdHash;
|
||||||
|
|
||||||
|
// The clientData JSON.
|
||||||
|
nsCString mClientDataJSON;
|
||||||
|
|
||||||
|
// Whether we'll force "none" attestation.
|
||||||
|
bool mForceNoneAttestation;
|
||||||
|
};
|
||||||
|
|
||||||
rust_u2f_manager* mU2FManager;
|
rust_u2f_manager* mU2FManager;
|
||||||
uint64_t mTransactionId;
|
Maybe<Transaction> mTransaction;
|
||||||
nsTArray<uint8_t> mCurrentAppId;
|
|
||||||
MozPromiseHolder<U2FRegisterPromise> mRegisterPromise;
|
MozPromiseHolder<U2FRegisterPromise> mRegisterPromise;
|
||||||
MozPromiseHolder<U2FSignPromise> mSignPromise;
|
MozPromiseHolder<U2FSignPromise> mSignPromise;
|
||||||
};
|
};
|
||||||
|
|
|
@ -583,7 +583,8 @@ U2FSoftTokenManager::IsRegistered(const nsTArray<uint8_t>& aKeyHandle,
|
||||||
// * attestation signature
|
// * attestation signature
|
||||||
//
|
//
|
||||||
RefPtr<U2FRegisterPromise>
|
RefPtr<U2FRegisterPromise>
|
||||||
U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation)
|
||||||
{
|
{
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
nsresult rv = Init();
|
nsresult rv = Init();
|
||||||
|
@ -592,7 +593,9 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebAuthnAuthenticatorSelection& sel = aInfo.AuthenticatorSelection();
|
if (aInfo.Extra().type() != WebAuthnMaybeMakeCredentialExtraInfo::Tnull_t) {
|
||||||
|
const auto& extra = aInfo.Extra().get_WebAuthnMakeCredentialExtraInfo();
|
||||||
|
const WebAuthnAuthenticatorSelection& sel = extra.AuthenticatorSelection();
|
||||||
|
|
||||||
// The U2F softtoken neither supports resident keys or
|
// The U2F softtoken neither supports resident keys or
|
||||||
// user verification, nor is it a platform authenticator.
|
// user verification, nor is it a platform authenticator.
|
||||||
|
@ -601,11 +604,20 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
sel.requirePlatformAttachment()) {
|
sel.requirePlatformAttachment()) {
|
||||||
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer rpIdHash, clientDataHash;
|
||||||
|
NS_ConvertUTF16toUTF8 rpId(aInfo.RpId());
|
||||||
|
nsresult rv = BuildTransactionHashes(rpId, aInfo.ClientDataJSON(),
|
||||||
|
rpIdHash, clientDataHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
// Optional exclusion list.
|
// Optional exclusion list.
|
||||||
for (const WebAuthnScopedCredential& cred: aInfo.ExcludeList()) {
|
for (const WebAuthnScopedCredential& cred: aInfo.ExcludeList()) {
|
||||||
bool isRegistered = false;
|
bool isRegistered = false;
|
||||||
nsresult rv = IsRegistered(cred.id(), aInfo.RpIdHash(), isRegistered);
|
nsresult rv = IsRegistered(cred.id(), rpIdHash, isRegistered);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return U2FRegisterPromise::CreateAndReject(rv, __func__);
|
return U2FRegisterPromise::CreateAndReject(rv, __func__);
|
||||||
}
|
}
|
||||||
|
@ -623,7 +635,7 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
// Construct a one-time-use Attestation Certificate
|
// Construct a one-time-use Attestation Certificate
|
||||||
UniqueSECKEYPrivateKey attestPrivKey;
|
UniqueSECKEYPrivateKey attestPrivKey;
|
||||||
UniqueCERTCertificate attestCert;
|
UniqueCERTCertificate attestCert;
|
||||||
nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert);
|
rv = GetAttestationCertificate(slot, attestPrivKey, attestCert);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
}
|
}
|
||||||
|
@ -641,16 +653,16 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
// The key handle will be the result of keywrap(privKey, key=mWrappingKey)
|
// The key handle will be the result of keywrap(privKey, key=mWrappingKey)
|
||||||
UniqueSECItem keyHandleItem =
|
UniqueSECItem keyHandleItem =
|
||||||
KeyHandleFromPrivateKey(slot, mWrappingKey,
|
KeyHandleFromPrivateKey(slot, mWrappingKey,
|
||||||
const_cast<uint8_t*>(aInfo.RpIdHash().Elements()),
|
const_cast<uint8_t*>(rpIdHash.Elements()),
|
||||||
aInfo.RpIdHash().Length(), privKey);
|
rpIdHash.Length(), privKey);
|
||||||
if (NS_WARN_IF(!keyHandleItem.get())) {
|
if (NS_WARN_IF(!keyHandleItem.get())) {
|
||||||
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign the challenge using the Attestation privkey (from attestCert)
|
// Sign the challenge using the Attestation privkey (from attestCert)
|
||||||
mozilla::dom::CryptoBuffer signedDataBuf;
|
mozilla::dom::CryptoBuffer signedDataBuf;
|
||||||
if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aInfo.RpIdHash().Length() +
|
if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + rpIdHash.Length() +
|
||||||
aInfo.ClientDataHash().Length() +
|
clientDataHash.Length() +
|
||||||
keyHandleItem->len + kPublicKeyLen,
|
keyHandleItem->len + kPublicKeyLen,
|
||||||
mozilla::fallible))) {
|
mozilla::fallible))) {
|
||||||
return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||||
|
@ -659,8 +671,8 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
// // It's OK to ignore the return values here because we're writing into
|
// // It's OK to ignore the return values here because we're writing into
|
||||||
// // pre-allocated space
|
// // pre-allocated space
|
||||||
signedDataBuf.AppendElement(0x00, mozilla::fallible);
|
signedDataBuf.AppendElement(0x00, mozilla::fallible);
|
||||||
signedDataBuf.AppendElements(aInfo.RpIdHash(), mozilla::fallible);
|
signedDataBuf.AppendElements(rpIdHash, mozilla::fallible);
|
||||||
signedDataBuf.AppendElements(aInfo.ClientDataHash(), mozilla::fallible);
|
signedDataBuf.AppendElements(clientDataHash, mozilla::fallible);
|
||||||
signedDataBuf.AppendSECItem(keyHandleItem.get());
|
signedDataBuf.AppendSECItem(keyHandleItem.get());
|
||||||
signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue);
|
signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue);
|
||||||
|
|
||||||
|
@ -688,10 +700,36 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
registrationBuf.AppendSECItem(attestCert.get()->derCert);
|
registrationBuf.AppendSECItem(attestCert.get()->derCert);
|
||||||
registrationBuf.AppendSECItem(signatureItem);
|
registrationBuf.AppendSECItem(signatureItem);
|
||||||
|
|
||||||
// Will be set by the U2FTokenManager.
|
CryptoBuffer keyHandleBuf;
|
||||||
bool directAttestationPermitted = false;
|
if (!keyHandleBuf.AppendSECItem(keyHandleItem.get())) {
|
||||||
WebAuthnMakeCredentialResult result((nsTArray<uint8_t>(registrationBuf)),
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
directAttestationPermitted);
|
}
|
||||||
|
|
||||||
|
CryptoBuffer attestCertBuf;
|
||||||
|
if (!attestCertBuf.AppendSECItem(attestCert.get()->derCert)) {
|
||||||
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer signatureBuf;
|
||||||
|
if (!signatureBuf.AppendSECItem(signatureItem)) {
|
||||||
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer pubKeyBuf;
|
||||||
|
if (!pubKeyBuf.AppendSECItem(pubKey->u.ec.publicValue)) {
|
||||||
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer attObj;
|
||||||
|
rv = AssembleAttestationObject(rpIdHash, pubKeyBuf, keyHandleBuf,
|
||||||
|
attestCertBuf, signatureBuf,
|
||||||
|
aForceNoneAttestation, attObj);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObj,
|
||||||
|
keyHandleBuf, registrationBuf);
|
||||||
return U2FRegisterPromise::CreateAndResolve(Move(result), __func__);
|
return U2FRegisterPromise::CreateAndResolve(Move(result), __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,20 +780,32 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The U2F softtoken doesn't support user verification.
|
CryptoBuffer rpIdHash, clientDataHash;
|
||||||
if (aInfo.RequireUserVerification()) {
|
NS_ConvertUTF16toUTF8 rpId(aInfo.RpId());
|
||||||
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
|
nsresult rv = BuildTransactionHashes(rpId, aInfo.ClientDataJSON(),
|
||||||
|
rpIdHash, clientDataHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsTArray<nsTArray<uint8_t>> appIds;
|
nsTArray<nsTArray<uint8_t>> appIds;
|
||||||
appIds.AppendElement(aInfo.RpIdHash());
|
appIds.AppendElement(rpIdHash);
|
||||||
|
|
||||||
|
if (aInfo.Extra().type() != WebAuthnMaybeGetAssertionExtraInfo::Tnull_t) {
|
||||||
|
const auto& extra = aInfo.Extra().get_WebAuthnGetAssertionExtraInfo();
|
||||||
|
|
||||||
|
// The U2F softtoken doesn't support user verification.
|
||||||
|
if (extra.RequireUserVerification()) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
// Process extensions.
|
// Process extensions.
|
||||||
for (const WebAuthnExtension& ext: aInfo.Extensions()) {
|
for (const WebAuthnExtension& ext: extra.Extensions()) {
|
||||||
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
|
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
|
||||||
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
|
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nsTArray<uint8_t> chosenAppId;
|
nsTArray<uint8_t> chosenAppId;
|
||||||
nsTArray<uint8_t> keyHandle;
|
nsTArray<uint8_t> keyHandle;
|
||||||
|
@ -770,11 +820,11 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
|
||||||
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||||
MOZ_ASSERT(slot.get());
|
MOZ_ASSERT(slot.get());
|
||||||
|
|
||||||
if (NS_WARN_IF((aInfo.ClientDataHash().Length() != kParamLen) ||
|
if (NS_WARN_IF((clientDataHash.Length() != kParamLen) ||
|
||||||
(chosenAppId.Length() != kParamLen))) {
|
(chosenAppId.Length() != kParamLen))) {
|
||||||
MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
|
MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
|
||||||
("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
|
("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
|
||||||
(uint32_t)aInfo.ClientDataHash().Length(),
|
(uint32_t)clientDataHash.Length(),
|
||||||
(uint32_t)chosenAppId.Length(), kParamLen));
|
(uint32_t)chosenAppId.Length(), kParamLen));
|
||||||
|
|
||||||
return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
|
return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
|
||||||
|
@ -820,8 +870,8 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
|
||||||
mozilla::fallible);
|
mozilla::fallible);
|
||||||
signedDataBuf.AppendElement(0x01, mozilla::fallible);
|
signedDataBuf.AppendElement(0x01, mozilla::fallible);
|
||||||
signedDataBuf.AppendSECItem(counterItem);
|
signedDataBuf.AppendSECItem(counterItem);
|
||||||
signedDataBuf.AppendElements(aInfo.ClientDataHash().Elements(),
|
signedDataBuf.AppendElements(clientDataHash.Elements(),
|
||||||
aInfo.ClientDataHash().Length(),
|
clientDataHash.Length(),
|
||||||
mozilla::fallible);
|
mozilla::fallible);
|
||||||
|
|
||||||
if (MOZ_LOG_TEST(gNSSTokenLog, LogLevel::Debug)) {
|
if (MOZ_LOG_TEST(gNSSTokenLog, LogLevel::Debug)) {
|
||||||
|
@ -847,27 +897,51 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the signature data into a buffer for return
|
// Assemble the signature data into a buffer for return
|
||||||
mozilla::dom::CryptoBuffer signatureBuf;
|
mozilla::dom::CryptoBuffer signatureDataBuf;
|
||||||
if (NS_WARN_IF(!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
|
if (NS_WARN_IF(!signatureDataBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
|
||||||
mozilla::fallible))) {
|
mozilla::fallible))) {
|
||||||
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's OK to ignore the return values here because we're writing into
|
// It's OK to ignore the return values here because we're writing into
|
||||||
// pre-allocated space
|
// pre-allocated space
|
||||||
signatureBuf.AppendElement(0x01, mozilla::fallible);
|
signatureDataBuf.AppendElement(0x01, mozilla::fallible);
|
||||||
signatureBuf.AppendSECItem(counterItem);
|
signatureDataBuf.AppendSECItem(counterItem);
|
||||||
signatureBuf.AppendSECItem(signatureItem);
|
signatureDataBuf.AppendSECItem(signatureItem);
|
||||||
|
|
||||||
nsTArray<uint8_t> signature(signatureBuf);
|
|
||||||
nsTArray<WebAuthnExtensionResult> extensions;
|
nsTArray<WebAuthnExtensionResult> extensions;
|
||||||
|
|
||||||
if (chosenAppId != aInfo.RpIdHash()) {
|
if (chosenAppId != rpIdHash) {
|
||||||
// Indicate to the RP that we used the FIDO appId.
|
// Indicate to the RP that we used the FIDO appId.
|
||||||
extensions.AppendElement(WebAuthnExtensionResultAppId(true));
|
extensions.AppendElement(WebAuthnExtensionResultAppId(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
WebAuthnGetAssertionResult result(chosenAppId, keyHandle, signature, extensions);
|
CryptoBuffer counterBuf;
|
||||||
|
if (!counterBuf.AppendSECItem(counterItem)) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer signatureBuf;
|
||||||
|
if (!signatureBuf.AppendSECItem(signatureItem)) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer chosenAppIdBuf;
|
||||||
|
if (!chosenAppIdBuf.Assign(chosenAppId)) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer authenticatorData;
|
||||||
|
CryptoBuffer emptyAttestationData;
|
||||||
|
rv = AssembleAuthenticatorData(chosenAppIdBuf, 0x01, counterBuf,
|
||||||
|
emptyAttestationData, authenticatorData);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAuthnGetAssertionResult result(aInfo.ClientDataJSON(), keyHandle,
|
||||||
|
signatureBuf, authenticatorData,
|
||||||
|
extensions, signatureDataBuf);
|
||||||
return U2FSignPromise::CreateAndResolve(Move(result), __func__);
|
return U2FSignPromise::CreateAndResolve(Move(result), __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ public:
|
||||||
explicit U2FSoftTokenManager(uint32_t aCounter);
|
explicit U2FSoftTokenManager(uint32_t aCounter);
|
||||||
|
|
||||||
RefPtr<U2FRegisterPromise>
|
RefPtr<U2FRegisterPromise>
|
||||||
Register(const WebAuthnMakeCredentialInfo& aInfo) override;
|
Register(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation) override;
|
||||||
|
|
||||||
RefPtr<U2FSignPromise>
|
RefPtr<U2FSignPromise>
|
||||||
Sign(const WebAuthnGetAssertionInfo& aInfo) override;
|
Sign(const WebAuthnGetAssertionInfo& aInfo) override;
|
||||||
|
|
|
@ -14,11 +14,7 @@
|
||||||
#include "mozilla/ipc/BackgroundParent.h"
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "hasht.h"
|
|
||||||
#include "nsICryptoHash.h"
|
|
||||||
#include "nsTextFormatter.h"
|
#include "nsTextFormatter.h"
|
||||||
#include "pkix/Input.h"
|
|
||||||
#include "pkixutil.h"
|
|
||||||
|
|
||||||
// Not named "security.webauth.u2f_softtoken_counter" because setting that
|
// Not named "security.webauth.u2f_softtoken_counter" because setting that
|
||||||
// name causes the window.u2f object to disappear until preferences get
|
// name causes the window.u2f object to disappear until preferences get
|
||||||
|
@ -303,23 +299,26 @@ U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all the supplied parameters are syntactically well-formed and
|
mLastTransactionId = aTransactionId;
|
||||||
// of the correct length. If not, return an error code equivalent to
|
|
||||||
// UnknownError and terminate the operation.
|
|
||||||
|
|
||||||
if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
|
// Determine whether direct attestation was requested.
|
||||||
(aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
|
bool directAttestationRequested = false;
|
||||||
AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
|
if (aTransactionInfo.Extra().type() == WebAuthnMaybeMakeCredentialExtraInfo::TWebAuthnMakeCredentialExtraInfo) {
|
||||||
|
const auto& extra = aTransactionInfo.Extra().get_WebAuthnMakeCredentialExtraInfo();
|
||||||
|
directAttestationRequested = extra.RequestDirectAttestation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a register request immediately if direct attestation
|
||||||
|
// wasn't requested or the test pref is set.
|
||||||
|
if (!directAttestationRequested ||
|
||||||
|
U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) {
|
||||||
|
// Force "none" attestation when "direct" attestation wasn't requested.
|
||||||
|
DoRegister(aTransactionInfo, !directAttestationRequested);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLastTransactionId = aTransactionId;
|
|
||||||
|
|
||||||
// If the RP request direct attestation, ask the user for permission and
|
// If the RP request direct attestation, ask the user for permission and
|
||||||
// store the transaction info until the user proceeds or cancels.
|
// store the transaction info until the user proceeds or cancels.
|
||||||
// Might be overriden by a pref for testing purposes.
|
|
||||||
if (aTransactionInfo.RequestDirectAttestation() &&
|
|
||||||
!U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) {
|
|
||||||
NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
|
NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
|
||||||
SendPromptNotification(kRegisterDirectPromptNotifcation,
|
SendPromptNotification(kRegisterDirectPromptNotifcation,
|
||||||
aTransactionId,
|
aTransactionId,
|
||||||
|
@ -327,13 +326,11 @@ U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
|
||||||
|
|
||||||
MOZ_ASSERT(mPendingRegisterInfo.isNothing());
|
MOZ_ASSERT(mPendingRegisterInfo.isNothing());
|
||||||
mPendingRegisterInfo = Some(aTransactionInfo);
|
mPendingRegisterInfo = Some(aTransactionInfo);
|
||||||
} else {
|
|
||||||
DoRegister(aTransactionInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo)
|
U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation)
|
||||||
{
|
{
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(mLastTransactionId > 0);
|
MOZ_ASSERT(mLastTransactionId > 0);
|
||||||
|
@ -346,17 +343,12 @@ U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo)
|
||||||
|
|
||||||
uint64_t tid = mLastTransactionId;
|
uint64_t tid = mLastTransactionId;
|
||||||
mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
|
mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
|
||||||
bool requestDirectAttestation = aInfo.RequestDirectAttestation();
|
|
||||||
|
|
||||||
mTokenManagerImpl
|
mTokenManagerImpl
|
||||||
->Register(aInfo)
|
->Register(aInfo, aForceNoneAttestation)
|
||||||
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
[tid, startTime, requestDirectAttestation](WebAuthnMakeCredentialResult&& aResult) {
|
[tid, startTime](WebAuthnMakeCredentialResult&& aResult) {
|
||||||
U2FTokenManager* mgr = U2FTokenManager::Get();
|
U2FTokenManager* mgr = U2FTokenManager::Get();
|
||||||
// The token manager implementations set DirectAttestationPermitted
|
|
||||||
// to false by default. Override this here with information from
|
|
||||||
// the JS prompt.
|
|
||||||
aResult.DirectAttestationPermitted() = requestDirectAttestation;
|
|
||||||
mgr->MaybeConfirmRegister(tid, aResult);
|
mgr->MaybeConfirmRegister(tid, aResult);
|
||||||
Telemetry::ScalarAdd(
|
Telemetry::ScalarAdd(
|
||||||
Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
|
Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
|
||||||
|
@ -412,12 +404,6 @@ U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
|
|
||||||
(aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
|
|
||||||
AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show a prompt that lets the user cancel the ongoing transaction.
|
// Show a prompt that lets the user cancel the ongoing transaction.
|
||||||
NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
|
NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
|
||||||
SendPromptNotification(kSignPromptNotifcation,
|
SendPromptNotification(kSignPromptNotifcation,
|
||||||
|
@ -487,7 +473,7 @@ U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent,
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
U2FTokenManager::ResumeRegister(uint64_t aTransactionId,
|
U2FTokenManager::ResumeRegister(uint64_t aTransactionId,
|
||||||
bool aPermitDirectAttestation)
|
bool aForceNoneAttestation)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess());
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
@ -499,14 +485,14 @@ U2FTokenManager::ResumeRegister(uint64_t aTransactionId,
|
||||||
nsCOMPtr<nsIRunnable> r(NewRunnableMethod<uint64_t, bool>(
|
nsCOMPtr<nsIRunnable> r(NewRunnableMethod<uint64_t, bool>(
|
||||||
"U2FTokenManager::RunResumeRegister", this,
|
"U2FTokenManager::RunResumeRegister", this,
|
||||||
&U2FTokenManager::RunResumeRegister, aTransactionId,
|
&U2FTokenManager::RunResumeRegister, aTransactionId,
|
||||||
aPermitDirectAttestation));
|
aForceNoneAttestation));
|
||||||
|
|
||||||
return gBackgroundThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
return gBackgroundThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
U2FTokenManager::RunResumeRegister(uint64_t aTransactionId,
|
U2FTokenManager::RunResumeRegister(uint64_t aTransactionId,
|
||||||
bool aPermitDirectAttestation)
|
bool aForceNoneAttestation)
|
||||||
{
|
{
|
||||||
mozilla::ipc::AssertIsOnBackgroundThread();
|
mozilla::ipc::AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
|
@ -518,12 +504,8 @@ U2FTokenManager::RunResumeRegister(uint64_t aTransactionId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward whether the user opted into direct attestation.
|
|
||||||
mPendingRegisterInfo.ref().RequestDirectAttestation() =
|
|
||||||
aPermitDirectAttestation;
|
|
||||||
|
|
||||||
// Resume registration and cleanup.
|
// Resume registration and cleanup.
|
||||||
DoRegister(mPendingRegisterInfo.ref());
|
DoRegister(mPendingRegisterInfo.ref(), aForceNoneAttestation);
|
||||||
mPendingRegisterInfo.reset();
|
mPendingRegisterInfo.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,8 @@ private:
|
||||||
void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError);
|
void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError);
|
||||||
void ClearTransaction();
|
void ClearTransaction();
|
||||||
// Step two of "Register", kicking off the actual transaction.
|
// Step two of "Register", kicking off the actual transaction.
|
||||||
void DoRegister(const WebAuthnMakeCredentialInfo& aInfo);
|
void DoRegister(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation);
|
||||||
void MaybeConfirmRegister(const uint64_t& aTransactionId,
|
void MaybeConfirmRegister(const uint64_t& aTransactionId,
|
||||||
const WebAuthnMakeCredentialResult& aResult);
|
const WebAuthnMakeCredentialResult& aResult);
|
||||||
void MaybeAbortRegister(const uint64_t& aTransactionId, const nsresult& aError);
|
void MaybeAbortRegister(const uint64_t& aTransactionId, const nsresult& aError);
|
||||||
|
@ -59,7 +60,7 @@ private:
|
||||||
const WebAuthnGetAssertionResult& aResult);
|
const WebAuthnGetAssertionResult& aResult);
|
||||||
void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError);
|
void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError);
|
||||||
// The main thread runnable function for "nsIU2FTokenManager.ResumeRegister".
|
// The main thread runnable function for "nsIU2FTokenManager.ResumeRegister".
|
||||||
void RunResumeRegister(uint64_t aTransactionId, bool aPermitDirectAttestation);
|
void RunResumeRegister(uint64_t aTransactionId, bool aForceNoneAttestation);
|
||||||
// The main thread runnable function for "nsIU2FTokenManager.Cancel".
|
// The main thread runnable function for "nsIU2FTokenManager.Cancel".
|
||||||
void RunCancel(uint64_t aTransactionId);
|
void RunCancel(uint64_t aTransactionId);
|
||||||
// Sends a "webauthn-prompt" observer notification with the given data.
|
// Sends a "webauthn-prompt" observer notification with the given data.
|
||||||
|
|
|
@ -28,7 +28,8 @@ public:
|
||||||
U2FTokenTransport() {}
|
U2FTokenTransport() {}
|
||||||
|
|
||||||
virtual RefPtr<U2FRegisterPromise>
|
virtual RefPtr<U2FRegisterPromise>
|
||||||
Register(const WebAuthnMakeCredentialInfo& aInfo) = 0;
|
Register(const WebAuthnMakeCredentialInfo& aInfo,
|
||||||
|
bool aForceNoneAttestation) = 0;
|
||||||
|
|
||||||
virtual RefPtr<U2FSignPromise>
|
virtual RefPtr<U2FSignPromise>
|
||||||
Sign(const WebAuthnGetAssertionInfo& aInfo) = 0;
|
Sign(const WebAuthnGetAssertionInfo& aInfo) = 0;
|
||||||
|
|
|
@ -6,15 +6,11 @@
|
||||||
|
|
||||||
#include "hasht.h"
|
#include "hasht.h"
|
||||||
#include "nsHTMLDocument.h"
|
#include "nsHTMLDocument.h"
|
||||||
#include "nsICryptoHash.h"
|
|
||||||
#include "nsNetCID.h"
|
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "WebAuthnCoseIdentifiers.h"
|
#include "WebAuthnCoseIdentifiers.h"
|
||||||
#include "mozilla/dom/AuthenticatorAttestationResponse.h"
|
#include "mozilla/dom/AuthenticatorAttestationResponse.h"
|
||||||
#include "mozilla/dom/Promise.h"
|
#include "mozilla/dom/Promise.h"
|
||||||
#include "mozilla/dom/PWebAuthnTransaction.h"
|
#include "mozilla/dom/PWebAuthnTransaction.h"
|
||||||
#include "mozilla/dom/U2FUtil.h"
|
|
||||||
#include "mozilla/dom/WebAuthnCBORUtil.h"
|
|
||||||
#include "mozilla/dom/WebAuthnManager.h"
|
#include "mozilla/dom/WebAuthnManager.h"
|
||||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||||
#include "mozilla/dom/WebAuthnUtil.h"
|
#include "mozilla/dom/WebAuthnUtil.h"
|
||||||
|
@ -26,15 +22,6 @@ using namespace mozilla::ipc;
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* Protocol Constants
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
const uint8_t FLAG_TUP = 0x01; // Test of User Presence required
|
|
||||||
const uint8_t FLAG_AT = 0x40; // Authenticator Data is provided
|
|
||||||
const uint8_t FLAG_UV = 0x04; // User was Verified (biometrics, etc.); this
|
|
||||||
// flag is not possible with U2F devices
|
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* Statics
|
* Statics
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
@ -271,27 +258,6 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer rpIdHash;
|
|
||||||
if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
|
|
||||||
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult srv;
|
|
||||||
nsCOMPtr<nsICryptoHash> hashService =
|
|
||||||
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
srv = HashCString(hashService, rpId, rpIdHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Move this logic into U2FTokenManager in Bug 1409220.
|
// TODO: Move this logic into U2FTokenManager in Bug 1409220.
|
||||||
|
|
||||||
// Process each element of mPubKeyCredParams using the following steps, to
|
// Process each element of mPubKeyCredParams using the following steps, to
|
||||||
|
@ -351,7 +317,7 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoCString clientDataJSON;
|
nsAutoCString clientDataJSON;
|
||||||
srv = AssembleClientData(origin, challenge,
|
nsresult srv = AssembleClientData(origin, challenge,
|
||||||
NS_LITERAL_STRING("webauthn.create"),
|
NS_LITERAL_STRING("webauthn.create"),
|
||||||
aOptions.mExtensions, clientDataJSON);
|
aOptions.mExtensions, clientDataJSON);
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||||
|
@ -359,18 +325,6 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer clientDataHash;
|
|
||||||
if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
srv = HashCString(hashService, clientDataJSON, clientDataHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsTArray<WebAuthnScopedCredential> excludeList;
|
nsTArray<WebAuthnScopedCredential> excludeList;
|
||||||
for (const auto& s: aOptions.mExcludeCredentials) {
|
for (const auto& s: aOptions.mExcludeCredentials) {
|
||||||
WebAuthnScopedCredential c;
|
WebAuthnScopedCredential c;
|
||||||
|
@ -410,15 +364,18 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
|
||||||
requireUserVerification,
|
requireUserVerification,
|
||||||
requirePlatformAttachment);
|
requirePlatformAttachment);
|
||||||
|
|
||||||
WebAuthnMakeCredentialInfo info(origin,
|
WebAuthnMakeCredentialExtraInfo extra(extensions,
|
||||||
rpIdHash,
|
|
||||||
clientDataHash,
|
|
||||||
adjustedTimeout,
|
|
||||||
excludeList,
|
|
||||||
extensions,
|
|
||||||
authSelection,
|
authSelection,
|
||||||
requestDirectAttestation);
|
requestDirectAttestation);
|
||||||
|
|
||||||
|
WebAuthnMakeCredentialInfo info(origin,
|
||||||
|
NS_ConvertUTF8toUTF16(rpId),
|
||||||
|
challenge,
|
||||||
|
clientDataJSON,
|
||||||
|
adjustedTimeout,
|
||||||
|
excludeList,
|
||||||
|
extra);
|
||||||
|
|
||||||
ListenForVisibilityEvents();
|
ListenForVisibilityEvents();
|
||||||
|
|
||||||
AbortSignal* signal = nullptr;
|
AbortSignal* signal = nullptr;
|
||||||
|
@ -428,11 +385,7 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mTransaction.isNothing());
|
MOZ_ASSERT(mTransaction.isNothing());
|
||||||
mTransaction = Some(WebAuthnTransaction(promise,
|
mTransaction = Some(WebAuthnTransaction(promise, signal));
|
||||||
rpIdHash,
|
|
||||||
clientDataJSON,
|
|
||||||
signal));
|
|
||||||
|
|
||||||
mChild->SendRequestRegister(mTransaction.ref().mId, info);
|
mChild->SendRequestRegister(mTransaction.ref().mId, info);
|
||||||
|
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
|
@ -502,20 +455,6 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult srv;
|
|
||||||
nsCOMPtr<nsICryptoHash> hashService =
|
|
||||||
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
srv = HashCString(hashService, rpId, rpIdHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use assertionChallenge, callerOrigin and rpId, along with the token binding
|
// Use assertionChallenge, callerOrigin and rpId, along with the token binding
|
||||||
// key associated with callerOrigin (if any), to create a ClientData structure
|
// key associated with callerOrigin (if any), to create a ClientData structure
|
||||||
// representing this request. Choose a hash algorithm for hashAlg and compute
|
// representing this request. Choose a hash algorithm for hashAlg and compute
|
||||||
|
@ -527,25 +466,14 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoCString clientDataJSON;
|
nsAutoCString clientDataJSON;
|
||||||
srv = AssembleClientData(origin, challenge, NS_LITERAL_STRING("webauthn.get"),
|
nsresult srv = AssembleClientData(origin, challenge,
|
||||||
|
NS_LITERAL_STRING("webauthn.get"),
|
||||||
aOptions.mExtensions, clientDataJSON);
|
aOptions.mExtensions, clientDataJSON);
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer clientDataHash;
|
|
||||||
if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
srv = HashCString(hashService, clientDataJSON, clientDataHash);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsTArray<WebAuthnScopedCredential> allowList;
|
nsTArray<WebAuthnScopedCredential> allowList;
|
||||||
for (const auto& s: aOptions.mAllowCredentials) {
|
for (const auto& s: aOptions.mAllowCredentials) {
|
||||||
if (s.mType == PublicKeyCredentialType::Public_key) {
|
if (s.mType == PublicKeyCredentialType::Public_key) {
|
||||||
|
@ -608,7 +536,7 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need the SHA-256 hash of the appId.
|
// We need the SHA-256 hash of the appId.
|
||||||
nsresult srv = HashCString(hashService, NS_ConvertUTF16toUTF8(appId), appIdHash);
|
srv = HashCString(NS_ConvertUTF16toUTF8(appId), appIdHash);
|
||||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
|
@ -618,13 +546,15 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
|
||||||
extensions.AppendElement(WebAuthnExtensionAppId(appIdHash));
|
extensions.AppendElement(WebAuthnExtensionAppId(appIdHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebAuthnGetAssertionExtraInfo extra(extensions, requireUserVerification);
|
||||||
|
|
||||||
WebAuthnGetAssertionInfo info(origin,
|
WebAuthnGetAssertionInfo info(origin,
|
||||||
rpIdHash,
|
NS_ConvertUTF8toUTF16(rpId),
|
||||||
clientDataHash,
|
challenge,
|
||||||
|
clientDataJSON,
|
||||||
adjustedTimeout,
|
adjustedTimeout,
|
||||||
allowList,
|
allowList,
|
||||||
requireUserVerification,
|
extra);
|
||||||
extensions);
|
|
||||||
|
|
||||||
ListenForVisibilityEvents();
|
ListenForVisibilityEvents();
|
||||||
|
|
||||||
|
@ -635,12 +565,9 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mTransaction.isNothing());
|
MOZ_ASSERT(mTransaction.isNothing());
|
||||||
mTransaction = Some(WebAuthnTransaction(promise,
|
mTransaction = Some(WebAuthnTransaction(promise, signal));
|
||||||
rpIdHash,
|
|
||||||
clientDataJSON,
|
|
||||||
signal));
|
|
||||||
|
|
||||||
mChild->SendRequestSign(mTransaction.ref().mId, info);
|
mChild->SendRequestSign(mTransaction.ref().mId, info);
|
||||||
|
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,120 +603,38 @@ WebAuthnManager::FinishMakeCredential(const uint64_t& aTransactionId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer regData;
|
CryptoBuffer clientDataBuf;
|
||||||
if (NS_WARN_IF(!regData.Assign(aResult.RegBuffer().Elements(),
|
if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
|
||||||
aResult.RegBuffer().Length()))) {
|
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mozilla::dom::CryptoBuffer aaguidBuf;
|
CryptoBuffer attObjBuf;
|
||||||
if (NS_WARN_IF(!aaguidBuf.SetCapacity(16, mozilla::fallible))) {
|
if (NS_WARN_IF(!attObjBuf.Assign(aResult.AttestationObject()))) {
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// FIDO U2F devices have no AAGUIDs, so they'll be all zeros until we add
|
|
||||||
// support for CTAP2 devices.
|
|
||||||
for (int i=0; i<16; i++) {
|
|
||||||
aaguidBuf.AppendElement(0x00, mozilla::fallible);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decompose the U2F registration packet
|
|
||||||
CryptoBuffer pubKeyBuf;
|
|
||||||
CryptoBuffer keyHandleBuf;
|
CryptoBuffer keyHandleBuf;
|
||||||
CryptoBuffer attestationCertBuf;
|
if (NS_WARN_IF(!keyHandleBuf.Assign(aResult.KeyHandle()))) {
|
||||||
CryptoBuffer signatureBuf;
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
// Only handles attestation cert chains of length=1.
|
|
||||||
nsresult rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandleBuf,
|
|
||||||
attestationCertBuf, signatureBuf);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(keyHandleBuf.Length() <= 0xFFFF);
|
|
||||||
|
|
||||||
nsAutoString keyHandleBase64Url;
|
nsAutoString keyHandleBase64Url;
|
||||||
rv = keyHandleBuf.ToJwkBase64(keyHandleBase64Url);
|
nsresult rv = keyHandleBuf.ToJwkBase64(keyHandleBase64Url);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
RejectTransaction(rv);
|
RejectTransaction(rv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer clientDataBuf;
|
|
||||||
if (!clientDataBuf.Assign(mTransaction.ref().mClientData)) {
|
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoBuffer rpIdHashBuf;
|
|
||||||
if (!rpIdHashBuf.Assign(mTransaction.ref().mRpIdHash)) {
|
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the public key object
|
|
||||||
CryptoBuffer pubKeyObj;
|
|
||||||
rv = CBOREncodePublicKeyObj(pubKeyBuf, pubKeyObj);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// During create credential, counter is always 0 for U2F
|
|
||||||
// See https://github.com/w3c/webauthn/issues/507
|
|
||||||
mozilla::dom::CryptoBuffer counterBuf;
|
|
||||||
if (NS_WARN_IF(!counterBuf.SetCapacity(4, mozilla::fallible))) {
|
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
counterBuf.AppendElement(0x00, mozilla::fallible);
|
|
||||||
counterBuf.AppendElement(0x00, mozilla::fallible);
|
|
||||||
counterBuf.AppendElement(0x00, mozilla::fallible);
|
|
||||||
counterBuf.AppendElement(0x00, mozilla::fallible);
|
|
||||||
|
|
||||||
// Construct the Attestation Data, which slots into the end of the
|
|
||||||
// Authentication Data buffer.
|
|
||||||
CryptoBuffer attDataBuf;
|
|
||||||
rv = AssembleAttestationData(aaguidBuf, keyHandleBuf, pubKeyObj, attDataBuf);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mozilla::dom::CryptoBuffer authDataBuf;
|
|
||||||
// attDataBuf always contains data, so per [1] we have to set the AT flag.
|
|
||||||
// [1] https://w3c.github.io/webauthn/#sec-authenticator-data
|
|
||||||
const uint8_t flags = FLAG_TUP | FLAG_AT;
|
|
||||||
rv = AssembleAuthenticatorData(rpIdHashBuf, flags, counterBuf, attDataBuf,
|
|
||||||
authDataBuf);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct attestation might have been requested by the RP. This will
|
|
||||||
// be true only if the user consented via the permission UI.
|
|
||||||
CryptoBuffer attObj;
|
|
||||||
if (aResult.DirectAttestationPermitted()) {
|
|
||||||
rv = CBOREncodeFidoU2FAttestationObj(authDataBuf, attestationCertBuf,
|
|
||||||
signatureBuf, attObj);
|
|
||||||
} else {
|
|
||||||
rv = CBOREncodeNoneAttestationObj(authDataBuf, attObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new PublicKeyCredential object and populate its fields with the
|
// Create a new PublicKeyCredential object and populate its fields with the
|
||||||
// values returned from the authenticator as well as the clientDataJSON
|
// values returned from the authenticator as well as the clientDataJSON
|
||||||
// computed earlier.
|
// computed earlier.
|
||||||
RefPtr<AuthenticatorAttestationResponse> attestation =
|
RefPtr<AuthenticatorAttestationResponse> attestation =
|
||||||
new AuthenticatorAttestationResponse(mParent);
|
new AuthenticatorAttestationResponse(mParent);
|
||||||
attestation->SetClientDataJSON(clientDataBuf);
|
attestation->SetClientDataJSON(clientDataBuf);
|
||||||
attestation->SetAttestationObject(attObj);
|
attestation->SetAttestationObject(attObjBuf);
|
||||||
|
|
||||||
RefPtr<PublicKeyCredential> credential =
|
RefPtr<PublicKeyCredential> credential =
|
||||||
new PublicKeyCredential(mParent);
|
new PublicKeyCredential(mParent);
|
||||||
|
@ -813,57 +658,32 @@ WebAuthnManager::FinishGetAssertion(const uint64_t& aTransactionId,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer tokenSignatureData;
|
|
||||||
if (NS_WARN_IF(!tokenSignatureData.Assign(aResult.SigBuffer().Elements(),
|
|
||||||
aResult.SigBuffer().Length()))) {
|
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoBuffer clientDataBuf;
|
CryptoBuffer clientDataBuf;
|
||||||
if (!clientDataBuf.Assign(mTransaction.ref().mClientData)) {
|
if (!clientDataBuf.Assign(aResult.ClientDataJSON())) {
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer rpIdHashBuf;
|
CryptoBuffer credentialBuf;
|
||||||
if (!rpIdHashBuf.Assign(aResult.RpIdHash())) {
|
if (!credentialBuf.Assign(aResult.KeyHandle())) {
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoBuffer signatureBuf;
|
CryptoBuffer signatureBuf;
|
||||||
CryptoBuffer counterBuf;
|
if (!signatureBuf.Assign(aResult.Signature())) {
|
||||||
uint8_t flags = 0;
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
nsresult rv = U2FDecomposeSignResponse(tokenSignatureData, flags, counterBuf,
|
|
||||||
signatureBuf);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve the two LSBs of the flags byte, UP and RFU1.
|
|
||||||
// See <https://github.com/fido-alliance/fido-2-specs/pull/519>
|
|
||||||
flags &= 0b11;
|
|
||||||
|
|
||||||
CryptoBuffer attestationDataBuf;
|
|
||||||
CryptoBuffer authenticatorDataBuf;
|
CryptoBuffer authenticatorDataBuf;
|
||||||
rv = AssembleAuthenticatorData(rpIdHashBuf, flags, counterBuf,
|
if (!authenticatorDataBuf.Assign(aResult.AuthenticatorData())) {
|
||||||
/* deliberately empty */ attestationDataBuf,
|
|
||||||
authenticatorDataBuf);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
RejectTransaction(rv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoBuffer credentialBuf;
|
|
||||||
if (!credentialBuf.Assign(aResult.CredentialID())) {
|
|
||||||
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoString credentialBase64Url;
|
nsAutoString credentialBase64Url;
|
||||||
rv = credentialBuf.ToJwkBase64(credentialBase64Url);
|
nsresult rv = credentialBuf.ToJwkBase64(credentialBase64Url);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
RejectTransaction(rv);
|
RejectTransaction(rv);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -48,12 +48,8 @@ class WebAuthnTransaction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WebAuthnTransaction(const RefPtr<Promise>& aPromise,
|
WebAuthnTransaction(const RefPtr<Promise>& aPromise,
|
||||||
const nsTArray<uint8_t>& aRpIdHash,
|
|
||||||
const nsCString& aClientData,
|
|
||||||
AbortSignal* aSignal)
|
AbortSignal* aSignal)
|
||||||
: mPromise(aPromise)
|
: mPromise(aPromise)
|
||||||
, mRpIdHash(aRpIdHash)
|
|
||||||
, mClientData(aClientData)
|
|
||||||
, mSignal(aSignal)
|
, mSignal(aSignal)
|
||||||
, mId(NextId())
|
, mId(NextId())
|
||||||
{
|
{
|
||||||
|
@ -63,12 +59,6 @@ public:
|
||||||
// JS Promise representing the transaction status.
|
// JS Promise representing the transaction status.
|
||||||
RefPtr<Promise> mPromise;
|
RefPtr<Promise> mPromise;
|
||||||
|
|
||||||
// The RP ID hash.
|
|
||||||
nsTArray<uint8_t> mRpIdHash;
|
|
||||||
|
|
||||||
// Client data used to assemble reply objects.
|
|
||||||
nsCString mClientData;
|
|
||||||
|
|
||||||
// An optional AbortSignal instance.
|
// An optional AbortSignal instance.
|
||||||
RefPtr<AbortSignal> mSignal;
|
RefPtr<AbortSignal> mSignal;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId1,
|
||||||
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId2,
|
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId2,
|
||||||
"https://www.gstatic.com/securitykey/a/google.com/origins.json");
|
"https://www.gstatic.com/securitykey/a/google.com/origins.json");
|
||||||
|
|
||||||
|
const uint8_t FLAG_TUP = 0x01; // Test of User Presence required
|
||||||
|
const uint8_t FLAG_AT = 0x40; // Authenticator Data is provided
|
||||||
|
|
||||||
bool
|
bool
|
||||||
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
|
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
|
||||||
const U2FOperation& aOp, /* in/out */ nsString& aAppId)
|
const U2FOperation& aOp, /* in/out */ nsString& aAppId)
|
||||||
|
@ -193,6 +196,74 @@ AssembleAttestationData(const CryptoBuffer& aaguidBuf,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
AssembleAttestationObject(const CryptoBuffer& aRpIdHash,
|
||||||
|
const CryptoBuffer& aPubKeyBuf,
|
||||||
|
const CryptoBuffer& aKeyHandleBuf,
|
||||||
|
const CryptoBuffer& aAttestationCertBuf,
|
||||||
|
const CryptoBuffer& aSignatureBuf,
|
||||||
|
bool aForceNoneAttestation,
|
||||||
|
/* out */ CryptoBuffer& aAttestationObjBuf)
|
||||||
|
{
|
||||||
|
// Construct the public key object
|
||||||
|
CryptoBuffer pubKeyObj;
|
||||||
|
nsresult rv = CBOREncodePublicKeyObj(aPubKeyBuf, pubKeyObj);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::dom::CryptoBuffer aaguidBuf;
|
||||||
|
if (NS_WARN_IF(!aaguidBuf.SetCapacity(16, mozilla::fallible))) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIDO U2F devices have no AAGUIDs, so they'll be all zeros until we add
|
||||||
|
// support for CTAP2 devices.
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
aaguidBuf.AppendElement(0x00, mozilla::fallible);
|
||||||
|
}
|
||||||
|
|
||||||
|
// During create credential, counter is always 0 for U2F
|
||||||
|
// See https://github.com/w3c/webauthn/issues/507
|
||||||
|
mozilla::dom::CryptoBuffer counterBuf;
|
||||||
|
if (NS_WARN_IF(!counterBuf.SetCapacity(4, mozilla::fallible))) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
counterBuf.AppendElement(0x00, mozilla::fallible);
|
||||||
|
counterBuf.AppendElement(0x00, mozilla::fallible);
|
||||||
|
counterBuf.AppendElement(0x00, mozilla::fallible);
|
||||||
|
counterBuf.AppendElement(0x00, mozilla::fallible);
|
||||||
|
|
||||||
|
// Construct the Attestation Data, which slots into the end of the
|
||||||
|
// Authentication Data buffer.
|
||||||
|
CryptoBuffer attDataBuf;
|
||||||
|
rv = AssembleAttestationData(aaguidBuf, aKeyHandleBuf, pubKeyObj, attDataBuf);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoBuffer authDataBuf;
|
||||||
|
// attDataBuf always contains data, so per [1] we have to set the AT flag.
|
||||||
|
// [1] https://w3c.github.io/webauthn/#sec-authenticator-data
|
||||||
|
const uint8_t flags = FLAG_TUP | FLAG_AT;
|
||||||
|
rv = AssembleAuthenticatorData(aRpIdHash, flags, counterBuf, attDataBuf,
|
||||||
|
authDataBuf);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct attestation might have been requested by the RP.
|
||||||
|
// The user might override this and request anonymization anyway.
|
||||||
|
if (aForceNoneAttestation) {
|
||||||
|
rv = CBOREncodeNoneAttestationObj(authDataBuf, aAttestationObjBuf);
|
||||||
|
} else {
|
||||||
|
rv = CBOREncodeFidoU2FAttestationObj(authDataBuf, aAttestationCertBuf,
|
||||||
|
aSignatureBuf, aAttestationObjBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
U2FDecomposeSignResponse(const CryptoBuffer& aResponse,
|
U2FDecomposeSignResponse(const CryptoBuffer& aResponse,
|
||||||
/* out */ uint8_t& aFlags,
|
/* out */ uint8_t& aFlags,
|
||||||
|
@ -323,5 +394,89 @@ U2FDecomposeECKey(const CryptoBuffer& aPubKeyBuf,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static nsresult
|
||||||
|
HashCString(nsICryptoHash* aHashService,
|
||||||
|
const nsACString& aIn,
|
||||||
|
/* out */ CryptoBuffer& aOut)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aHashService);
|
||||||
|
|
||||||
|
nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = aHashService->Update(
|
||||||
|
reinterpret_cast<const uint8_t*>(aIn.BeginReading()), aIn.Length());
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString fullHash;
|
||||||
|
// Passing false below means we will get a binary result rather than a
|
||||||
|
// base64-encoded string.
|
||||||
|
rv = aHashService->Finish(false, fullHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(!aOut.Assign(fullHash))) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
HashCString(const nsACString& aIn, /* out */ CryptoBuffer& aOut)
|
||||||
|
{
|
||||||
|
nsresult srv;
|
||||||
|
nsCOMPtr<nsICryptoHash> hashService =
|
||||||
|
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
|
||||||
|
if (NS_FAILED(srv)) {
|
||||||
|
return srv;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv = HashCString(hashService, aIn, aOut);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
BuildTransactionHashes(const nsCString& aRpId,
|
||||||
|
const nsCString& aClientDataJSON,
|
||||||
|
/* out */ CryptoBuffer& aRpIdHash,
|
||||||
|
/* out */ CryptoBuffer& aClientDataHash)
|
||||||
|
{
|
||||||
|
nsresult srv;
|
||||||
|
nsCOMPtr<nsICryptoHash> hashService =
|
||||||
|
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
|
||||||
|
if (NS_FAILED(srv)) {
|
||||||
|
return srv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aRpIdHash.SetLength(SHA256_LENGTH, fallible)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
srv = HashCString(hashService, aRpId, aRpIdHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aClientDataHash.SetLength(SHA256_LENGTH, fallible)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
srv = HashCString(hashService, aClientDataJSON, aClientDataHash);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mozilla/dom/CryptoBuffer.h"
|
#include "mozilla/dom/CryptoBuffer.h"
|
||||||
#include "pkix/Input.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
@ -35,10 +34,13 @@ AssembleAuthenticatorData(const CryptoBuffer& rpIdHashBuf,
|
||||||
/* out */ CryptoBuffer& authDataBuf);
|
/* out */ CryptoBuffer& authDataBuf);
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
AssembleAttestationData(const CryptoBuffer& aaguidBuf,
|
AssembleAttestationObject(const CryptoBuffer& aRpIdHash,
|
||||||
const CryptoBuffer& keyHandleBuf,
|
const CryptoBuffer& aPubKeyBuf,
|
||||||
const CryptoBuffer& pubKeyObj,
|
const CryptoBuffer& aKeyHandleBuf,
|
||||||
/* out */ CryptoBuffer& attestationDataBuf);
|
const CryptoBuffer& aAttestationCertBuf,
|
||||||
|
const CryptoBuffer& aSignatureBuf,
|
||||||
|
bool aForceNoneAttestation,
|
||||||
|
/* out */ CryptoBuffer& aAttestationObjBuf);
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
U2FDecomposeSignResponse(const CryptoBuffer& aResponse,
|
U2FDecomposeSignResponse(const CryptoBuffer& aResponse,
|
||||||
|
@ -53,15 +55,20 @@ U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
|
||||||
/* out */ CryptoBuffer& aAttestationCertBuf,
|
/* out */ CryptoBuffer& aAttestationCertBuf,
|
||||||
/* out */ CryptoBuffer& aSignatureBuf);
|
/* out */ CryptoBuffer& aSignatureBuf);
|
||||||
|
|
||||||
nsresult
|
|
||||||
ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
|
|
||||||
uint32_t aLen);
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
U2FDecomposeECKey(const CryptoBuffer& aPubKeyBuf,
|
U2FDecomposeECKey(const CryptoBuffer& aPubKeyBuf,
|
||||||
/* out */ CryptoBuffer& aXcoord,
|
/* out */ CryptoBuffer& aXcoord,
|
||||||
/* out */ CryptoBuffer& aYcoord);
|
/* out */ CryptoBuffer& aYcoord);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
HashCString(const nsACString& aIn, /* out */ CryptoBuffer& aOut);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
BuildTransactionHashes(const nsCString& aRpId,
|
||||||
|
const nsCString& aClientDataJSON,
|
||||||
|
/* out */ CryptoBuffer& aRpIdHash,
|
||||||
|
/* out */ CryptoBuffer& aClientDataHash);
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,10 @@ interface nsIU2FTokenManager : nsISupports
|
||||||
* and we have to wait for user input to proceed.
|
* and we have to wait for user input to proceed.
|
||||||
*
|
*
|
||||||
* @param aTransactionID : The ID of the transaction to resume.
|
* @param aTransactionID : The ID of the transaction to resume.
|
||||||
* @param aPermitDirectAttestation : Whether direct attestation was
|
* @param aForceNoneAttestation : The user might enforce none attestation.
|
||||||
* permitted by the user.
|
|
||||||
*/
|
*/
|
||||||
void resumeRegister(in uint64_t aTransactionID,
|
void resumeRegister(in uint64_t aTransactionID,
|
||||||
in bool aPermitDirectAttestation);
|
in bool aForceNoneAttestation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the current WebAuthn/U2F transaction if that matches the given
|
* Cancels the current WebAuthn/U2F transaction if that matches the given
|
||||||
|
|
|
@ -98,8 +98,7 @@
|
||||||
.then(verifyAnonymizedCertificate)
|
.then(verifyAnonymizedCertificate)
|
||||||
.catch(arrivingHereIsBad);
|
.catch(arrivingHereIsBad);
|
||||||
|
|
||||||
// Request direct attestation, which should prompt for user intervention,
|
// Request direct attestation, which will prompt for user intervention.
|
||||||
// once 1430150 lands.
|
|
||||||
await requestMakeCredential("direct")
|
await requestMakeCredential("direct")
|
||||||
.then(verifyDirectCertificate)
|
.then(verifyDirectCertificate)
|
||||||
.catch(arrivingHereIsBad);
|
.catch(arrivingHereIsBad);
|
||||||
|
|
|
@ -262,11 +262,9 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn rust_u2f_mgr_cancel(mgr: *mut U2FManager) -> u64 {
|
pub unsafe extern "C" fn rust_u2f_mgr_cancel(mgr: *mut U2FManager) {
|
||||||
if !mgr.is_null() {
|
if !mgr.is_null() {
|
||||||
// Ignore return value.
|
// Ignore return value.
|
||||||
let _ = (*mgr).cancel();
|
let _ = (*mgr).cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
new_tid()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ uint64_t rust_u2f_mgr_sign(rust_u2f_manager* mgr,
|
||||||
const rust_u2f_app_ids* app_ids,
|
const rust_u2f_app_ids* app_ids,
|
||||||
const rust_u2f_key_handles* khs);
|
const rust_u2f_key_handles* khs);
|
||||||
|
|
||||||
uint64_t rust_u2f_mgr_cancel(rust_u2f_manager* mgr);
|
void rust_u2f_mgr_cancel(rust_u2f_manager* mgr);
|
||||||
|
|
||||||
|
|
||||||
/// U2FAppIds functions.
|
/// U2FAppIds functions.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче