зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 09ca3b67a359 (bug 1323339)
This commit is contained in:
Родитель
a03d774c23
Коммит
44140b7e15
|
@ -1,794 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "hasht.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
#include "mozilla/dom/WebAuthnUtil.h"
|
||||
#include "mozilla/dom/PWebAuthnTransaction.h"
|
||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||
#include "mozilla/dom/WebCryptoCommon.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/***********************************************************************
|
||||
* Statics
|
||||
**********************************************************************/
|
||||
|
||||
namespace {
|
||||
StaticRefPtr<WebAuthnManager> gWebAuthnManager;
|
||||
static mozilla::LazyLogModule gWebAuthnManagerLog("webauthnmanager");
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebAuthnManager, nsIIPCBackgroundChildCreateCallback);
|
||||
|
||||
/***********************************************************************
|
||||
* Utility Functions
|
||||
**********************************************************************/
|
||||
|
||||
template<class OOS>
|
||||
static nsresult
|
||||
GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
|
||||
/* out */ nsString& aName)
|
||||
{
|
||||
MOZ_ASSERT(aAlgorithm.IsString()); // TODO: remove assertion when we coerce.
|
||||
|
||||
if (aAlgorithm.IsString()) {
|
||||
// If string, then treat as algorithm name
|
||||
aName.Assign(aAlgorithm.GetAsString());
|
||||
} else {
|
||||
// TODO: Coerce to string and extract name. See WebCryptoTask.cpp
|
||||
}
|
||||
|
||||
if (!NormalizeToken(aName, aName)) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
aOut.Assign(fullHash);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
|
||||
/* out */ nsACString& aJsonOut)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString challengeBase64;
|
||||
nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
WebAuthnClientData clientDataObject;
|
||||
clientDataObject.mOrigin.Assign(aOrigin);
|
||||
clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256"));
|
||||
clientDataObject.mChallenge.Assign(challengeBase64);
|
||||
|
||||
nsAutoString temp;
|
||||
if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetOrigin(nsPIDOMWindowInner* aParent,
|
||||
/*out*/ nsAString& aOrigin)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = aParent->GetDoc();
|
||||
MOZ_ASSERT(doc);
|
||||
|
||||
nsIPrincipal* principal = doc->NodePrincipal();
|
||||
nsresult rv = nsContentUtils::GetUTFOrigin(principal, aOrigin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) ||
|
||||
NS_WARN_IF(aOrigin.IsEmpty())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aOrigin.EqualsLiteral("null")) {
|
||||
// 4.1.1.3 If callerOrigin is an opaque origin, reject promise with a
|
||||
// DOMException whose name is "NotAllowedError", and terminate this
|
||||
// algorithm
|
||||
MOZ_LOG(gWebAuthnManagerLog, LogLevel::Debug, ("Rejecting due to opaque origin"));
|
||||
return NS_ERROR_DOM_NOT_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RelaxSameOrigin(nsPIDOMWindowInner* aParent,
|
||||
const nsAString& aInputRpId,
|
||||
/* out */ nsACString& aRelaxedRpId)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
nsCOMPtr<nsIDocument> document = aParent->GetDoc();
|
||||
if (!document || !document->IsHTMLDocument()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// TODO: Bug 1329764: Invoke the Relax Algorithm, once properly defined
|
||||
aRelaxedRpId.Assign(NS_ConvertUTF16toUTF8(aInputRpId));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WebAuthnManager Implementation
|
||||
**********************************************************************/
|
||||
|
||||
WebAuthnManager::WebAuthnManager()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::MaybeClearTransaction()
|
||||
{
|
||||
mClientData.reset();
|
||||
mInfo.reset();
|
||||
mTransactionPromise = nullptr;
|
||||
if (mChild) {
|
||||
RefPtr<WebAuthnTransactionChild> c;
|
||||
mChild.swap(c);
|
||||
c->Send__delete__(c);
|
||||
}
|
||||
}
|
||||
|
||||
WebAuthnManager::~WebAuthnManager()
|
||||
{
|
||||
MaybeClearTransaction();
|
||||
}
|
||||
|
||||
already_AddRefed<MozPromise<nsresult, nsresult, false>>
|
||||
WebAuthnManager::GetOrCreateBackgroundActor()
|
||||
{
|
||||
bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
|
||||
if (NS_WARN_IF(!ok)) {
|
||||
ActorFailed();
|
||||
}
|
||||
return mPBackgroundCreationPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
//static
|
||||
WebAuthnManager*
|
||||
WebAuthnManager::GetOrCreate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (gWebAuthnManager) {
|
||||
return gWebAuthnManager;
|
||||
}
|
||||
|
||||
gWebAuthnManager = new WebAuthnManager();
|
||||
ClearOnShutdown(&gWebAuthnManager);
|
||||
return gWebAuthnManager;
|
||||
}
|
||||
|
||||
//static
|
||||
WebAuthnManager*
|
||||
WebAuthnManager::Get()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return gWebAuthnManager;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
const Account& aAccountInformation,
|
||||
const Sequence<ScopedCredentialParameters>& aCryptoParameters,
|
||||
const ArrayBufferViewOrArrayBuffer& aChallenge,
|
||||
const ScopedCredentialOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
|
||||
MaybeClearTransaction();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> promise = Promise::Create(global, rv);
|
||||
if(rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString origin;
|
||||
rv = GetOrigin(aParent, origin);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// If timeoutSeconds was specified, check if its value lies within a
|
||||
// reasonable range as defined by the platform and if not, correct it to the
|
||||
// closest value lying within that range.
|
||||
|
||||
double adjustedTimeout = 30.0;
|
||||
if (aOptions.mTimeoutSeconds.WasPassed()) {
|
||||
adjustedTimeout = aOptions.mTimeoutSeconds.Value();
|
||||
adjustedTimeout = std::max(15.0, adjustedTimeout);
|
||||
adjustedTimeout = std::min(120.0, adjustedTimeout);
|
||||
}
|
||||
|
||||
nsCString rpId;
|
||||
if (!aOptions.mRpId.WasPassed()) {
|
||||
// If rpId is not specified, then set rpId to callerOrigin, and rpIdHash to
|
||||
// the SHA-256 hash of rpId.
|
||||
rpId.Assign(NS_ConvertUTF16toUTF8(origin));
|
||||
} else {
|
||||
// If rpId is specified, then invoke the procedure used for relaxing the
|
||||
// same-origin restriction by setting the document.domain attribute, using
|
||||
// rpId as the given value but without changing the current document’s
|
||||
// domain. If no errors are thrown, set rpId to the value of host as
|
||||
// computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
|
||||
// Otherwise, reject promise with a DOMException whose name is
|
||||
// "SecurityError", and terminate this algorithm.
|
||||
|
||||
if (NS_FAILED(RelaxSameOrigin(aParent, aOptions.mRpId.Value(), rpId))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
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();
|
||||
}
|
||||
|
||||
// Process each element of cryptoParameters using the following steps, to
|
||||
// produce a new sequence normalizedParameters.
|
||||
nsTArray<ScopedCredentialParameters> normalizedParams;
|
||||
for (size_t a = 0; a < aCryptoParameters.Length(); ++a) {
|
||||
// Let current be the currently selected element of
|
||||
// cryptoParameters.
|
||||
|
||||
// If current.type does not contain a ScopedCredentialType
|
||||
// supported by this implementation, then stop processing current and move
|
||||
// on to the next element in cryptoParameters.
|
||||
if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Let normalizedAlgorithm be the result of normalizing an algorithm using
|
||||
// the procedure defined in [WebCryptoAPI], with alg set to
|
||||
// current.algorithm and op set to 'generateKey'. If an error occurs during
|
||||
// this procedure, then stop processing current and move on to the next
|
||||
// element in cryptoParameters.
|
||||
|
||||
nsString algName;
|
||||
if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm,
|
||||
algName))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add a new object of type ScopedCredentialParameters to
|
||||
// normalizedParameters, with type set to current.type and algorithm set to
|
||||
// normalizedAlgorithm.
|
||||
ScopedCredentialParameters normalizedObj;
|
||||
normalizedObj.mType = aCryptoParameters[a].mType;
|
||||
normalizedObj.mAlgorithm.SetAsString().Assign(algName);
|
||||
|
||||
if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
|
||||
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
||||
return promise.forget();
|
||||
}
|
||||
}
|
||||
|
||||
// If normalizedAlgorithm is empty and cryptoParameters was not empty, cancel
|
||||
// the timer started in step 2, reject promise with a DOMException whose name
|
||||
// is "NotSupportedError", and terminate this algorithm.
|
||||
if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// TODO: The following check should not be here. This is checking for
|
||||
// parameters specific to the soft key, and should be put in the soft key
|
||||
// manager in the parent process. Still need to serialize
|
||||
// ScopedCredentialParameters first.
|
||||
|
||||
// Check if at least one of the specified combinations of ScopedCredentialType
|
||||
// and cryptographic parameters is supported. If not, return an error code
|
||||
// equivalent to NotSupportedError and terminate the operation.
|
||||
|
||||
bool isValidCombination = false;
|
||||
|
||||
for (size_t a = 0; a < normalizedParams.Length(); ++a) {
|
||||
if (normalizedParams[a].mType == ScopedCredentialType::ScopedCred &&
|
||||
normalizedParams[a].mAlgorithm.IsString() &&
|
||||
normalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
|
||||
WEBCRYPTO_NAMED_CURVE_P256)) {
|
||||
isValidCombination = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValidCombination) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// If excludeList is undefined, set it to the empty list.
|
||||
//
|
||||
// If extensions was specified, process any extensions supported by this
|
||||
// client platform, to produce the extension data that needs to be sent to the
|
||||
// authenticator. If an error is encountered while processing an extension,
|
||||
// skip that extension and do not produce any extension data for it. Call the
|
||||
// result of this processing clientExtensions.
|
||||
//
|
||||
// Currently no extensions are supported
|
||||
//
|
||||
// Use attestationChallenge, callerOrigin and rpId, along with the token
|
||||
// binding key associated with callerOrigin (if any), to create a ClientData
|
||||
// structure representing this request. Choose a hash algorithm for hashAlg
|
||||
// and compute the clientDataJSON and clientDataHash.
|
||||
|
||||
CryptoBuffer challenge;
|
||||
if (!challenge.Assign(aChallenge)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsAutoCString clientDataJSON;
|
||||
srv = AssembleClientData(origin, challenge, clientDataJSON);
|
||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
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<WebAuthnScopedCredentialDescriptor> excludeList;
|
||||
if (aOptions.mExcludeList.WasPassed()) {
|
||||
for (const auto& s: aOptions.mExcludeList.Value()) {
|
||||
WebAuthnScopedCredentialDescriptor c;
|
||||
c.type() = static_cast<uint32_t>(s.mType);
|
||||
CryptoBuffer cb;
|
||||
cb.Assign(s.mId);
|
||||
c.id() = cb;
|
||||
if (s.mTransports.WasPassed()) {
|
||||
for (const auto& t: s.mTransports.Value()) {
|
||||
c.transports().AppendElement(static_cast<uint32_t>(t));
|
||||
}
|
||||
}
|
||||
excludeList.AppendElement(c);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add extension list building
|
||||
nsTArray<WebAuthnExtension> extensions;
|
||||
|
||||
WebAuthnTransactionInfo info(rpIdHash,
|
||||
clientDataHash,
|
||||
adjustedTimeout,
|
||||
excludeList,
|
||||
extensions);
|
||||
RefPtr<MozPromise<nsresult, nsresult, false>> p = GetOrCreateBackgroundActor();
|
||||
p->Then(AbstractThread::MainThread(), __func__,
|
||||
[this]() {
|
||||
this->StartRegister();
|
||||
},
|
||||
[this]() {
|
||||
this->MaybeClearTransaction();
|
||||
});
|
||||
mTransactionPromise = promise;
|
||||
mClientData = Some(clientDataJSON);
|
||||
mCurrentParent = aParent;
|
||||
mInfo = Some(info);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::StartRegister() {
|
||||
if (mChild) {
|
||||
mChild->SendRequestRegister(mInfo.ref());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::StartSign() {
|
||||
if (mChild) {
|
||||
mChild->SendRequestSign(mInfo.ref());
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
const ArrayBufferViewOrArrayBuffer& aChallenge,
|
||||
const AssertionOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
|
||||
MaybeClearTransaction();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> promise = Promise::Create(global, rv);
|
||||
if(rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString origin;
|
||||
rv = GetOrigin(aParent, origin);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// If timeoutSeconds was specified, check if its value lies within a
|
||||
// reasonable range as defined by the platform and if not, correct it to the
|
||||
// closest value lying within that range.
|
||||
|
||||
double adjustedTimeout = 30.0;
|
||||
if (aOptions.mTimeoutSeconds.WasPassed()) {
|
||||
adjustedTimeout = aOptions.mTimeoutSeconds.Value();
|
||||
adjustedTimeout = std::max(15.0, adjustedTimeout);
|
||||
adjustedTimeout = std::min(120.0, adjustedTimeout);
|
||||
}
|
||||
|
||||
nsCString rpId;
|
||||
if (!aOptions.mRpId.WasPassed()) {
|
||||
// If rpId is not specified, then set rpId to callerOrigin, and rpIdHash to
|
||||
// the SHA-256 hash of rpId.
|
||||
rpId.Assign(NS_ConvertUTF16toUTF8(origin));
|
||||
} else {
|
||||
// If rpId is specified, then invoke the procedure used for relaxing the
|
||||
// same-origin restriction by setting the document.domain attribute, using
|
||||
// rpId as the given value but without changing the current document’s
|
||||
// domain. If no errors are thrown, set rpId to the value of host as
|
||||
// computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
|
||||
// Otherwise, reject promise with a DOMException whose name is
|
||||
// "SecurityError", and terminate this algorithm.
|
||||
|
||||
if (NS_FAILED(RelaxSameOrigin(aParent, aOptions.mRpId.Value(), rpId))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
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();
|
||||
}
|
||||
|
||||
// Use assertionChallenge, callerOrigin and rpId, along with the token binding
|
||||
// key associated with callerOrigin (if any), to create a ClientData structure
|
||||
// representing this request. Choose a hash algorithm for hashAlg and compute
|
||||
// the clientDataJSON and clientDataHash.
|
||||
CryptoBuffer challenge;
|
||||
if (!challenge.Assign(aChallenge)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsAutoCString clientDataJSON;
|
||||
srv = AssembleClientData(origin, challenge, clientDataJSON);
|
||||
if (NS_WARN_IF(NS_FAILED(srv))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
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();
|
||||
}
|
||||
|
||||
// Note: we only support U2F-style authentication for now, so we effectively
|
||||
// require an AllowList.
|
||||
if (!aOptions.mAllowList.WasPassed()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<WebAuthnScopedCredentialDescriptor> allowList;
|
||||
for (const auto& s: aOptions.mAllowList.Value()) {
|
||||
WebAuthnScopedCredentialDescriptor c;
|
||||
c.type() = static_cast<uint32_t>(s.mType);
|
||||
CryptoBuffer cb;
|
||||
cb.Assign(s.mId);
|
||||
c.id() = cb;
|
||||
if (s.mTransports.WasPassed()) {
|
||||
for (const auto& t: s.mTransports.Value()) {
|
||||
c.transports().AppendElement(static_cast<uint32_t>(t));
|
||||
}
|
||||
}
|
||||
allowList.AppendElement(c);
|
||||
}
|
||||
|
||||
// TODO: Add extension list building
|
||||
// If extensions was specified, process any extensions supported by this
|
||||
// client platform, to produce the extension data that needs to be sent to the
|
||||
// authenticator. If an error is encountered while processing an extension,
|
||||
// skip that extension and do not produce any extension data for it. Call the
|
||||
// result of this processing clientExtensions.
|
||||
nsTArray<WebAuthnExtension> extensions;
|
||||
|
||||
WebAuthnTransactionInfo info(rpIdHash,
|
||||
clientDataHash,
|
||||
10000,
|
||||
allowList,
|
||||
extensions);
|
||||
RefPtr<MozPromise<nsresult, nsresult, false>> p = GetOrCreateBackgroundActor();
|
||||
p->Then(AbstractThread::MainThread(), __func__,
|
||||
[this]() {
|
||||
this->StartSign();
|
||||
},
|
||||
[this]() {
|
||||
this->MaybeClearTransaction();
|
||||
});
|
||||
|
||||
// Only store off the promise if we've succeeded in sending the IPC event.
|
||||
mTransactionPromise = promise;
|
||||
mClientData = Some(clientDataJSON);
|
||||
mCurrentParent = aParent;
|
||||
mInfo = Some(info);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer,
|
||||
nsTArray<uint8_t>& aSigBuffer)
|
||||
{
|
||||
MOZ_ASSERT(mTransactionPromise);
|
||||
MOZ_ASSERT(mInfo.isSome());
|
||||
|
||||
CryptoBuffer regData;
|
||||
if (NS_WARN_IF(!regData.Assign(aRegBuffer.Elements(), aRegBuffer.Length()))) {
|
||||
mTransactionPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
// Decompose the U2F registration packet
|
||||
CryptoBuffer pubKeyBuf;
|
||||
CryptoBuffer keyHandleBuf;
|
||||
CryptoBuffer attestationCertBuf;
|
||||
CryptoBuffer signatureBuf;
|
||||
|
||||
nsresult rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandleBuf,
|
||||
attestationCertBuf, signatureBuf);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Cancel(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer signatureData;
|
||||
if (NS_WARN_IF(!signatureData.Assign(aSigBuffer.Elements(), aSigBuffer.Length()))) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer clientDataBuf;
|
||||
if (!clientDataBuf.Assign(mClientData.ref())) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer rpIdHashBuf;
|
||||
if (!rpIdHashBuf.Assign(mInfo.ref().RpIdHash())) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer authenticatorDataBuf;
|
||||
rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, rpIdHashBuf,
|
||||
signatureData);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new ScopedCredentialInfo object named value and populate its
|
||||
// fields with the values returned from the authenticator as well as the
|
||||
// clientDataJSON computed earlier.
|
||||
|
||||
RefPtr<ScopedCredential> credential = new ScopedCredential(mCurrentParent);
|
||||
credential->SetType(ScopedCredentialType::ScopedCred);
|
||||
credential->SetId(keyHandleBuf);
|
||||
|
||||
RefPtr<WebAuthnAttestation> attestation = new WebAuthnAttestation(mCurrentParent);
|
||||
attestation->SetFormat(NS_LITERAL_STRING("u2f"));
|
||||
attestation->SetClientData(clientDataBuf);
|
||||
attestation->SetAuthenticatorData(authenticatorDataBuf);
|
||||
attestation->SetAttestation(regData);
|
||||
|
||||
RefPtr<ScopedCredentialInfo> info = new ScopedCredentialInfo(mCurrentParent);
|
||||
info->SetCredential(credential);
|
||||
info->SetAttestation(attestation);
|
||||
|
||||
mTransactionPromise->MaybeResolve(info);
|
||||
MaybeClearTransaction();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
|
||||
nsTArray<uint8_t>& aSigBuffer)
|
||||
{
|
||||
MOZ_ASSERT(mTransactionPromise);
|
||||
MOZ_ASSERT(mInfo.isSome());
|
||||
|
||||
CryptoBuffer signatureData;
|
||||
if (NS_WARN_IF(!signatureData.Assign(aSigBuffer.Elements(), aSigBuffer.Length()))) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer clientDataBuf;
|
||||
if (!clientDataBuf.Assign(mClientData.ref())) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer rpIdHashBuf;
|
||||
if (!rpIdHashBuf.Assign(mInfo.ref().RpIdHash())) {
|
||||
Cancel(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer authenticatorDataBuf;
|
||||
nsresult rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, rpIdHashBuf,
|
||||
signatureData);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Cancel(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer credentialBuf;
|
||||
if (!credentialBuf.Assign(aCredentialId)) {
|
||||
Cancel(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// If any authenticator returns success:
|
||||
|
||||
// Create a new WebAuthnAssertion object named value and populate its fields
|
||||
// with the values returned from the authenticator as well as the
|
||||
// clientDataJSON computed earlier.
|
||||
|
||||
RefPtr<ScopedCredential> credential = new ScopedCredential(mCurrentParent);
|
||||
credential->SetType(ScopedCredentialType::ScopedCred);
|
||||
credential->SetId(credentialBuf);
|
||||
|
||||
RefPtr<WebAuthnAssertion> assertion = new WebAuthnAssertion(mCurrentParent);
|
||||
assertion->SetCredential(credential);
|
||||
assertion->SetClientData(clientDataBuf);
|
||||
assertion->SetAuthenticatorData(authenticatorDataBuf);
|
||||
assertion->SetSignature(signatureData);
|
||||
|
||||
mTransactionPromise->MaybeResolve(assertion);
|
||||
MaybeClearTransaction();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::Cancel(const nsresult& aError)
|
||||
{
|
||||
if (mChild) {
|
||||
mChild->SendRequestCancel();
|
||||
}
|
||||
if (mTransactionPromise) {
|
||||
mTransactionPromise->MaybeReject(aError);
|
||||
}
|
||||
MaybeClearTransaction();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::ActorCreated(PBackgroundChild* aActor)
|
||||
{
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
RefPtr<WebAuthnTransactionChild> mgr(new WebAuthnTransactionChild());
|
||||
PWebAuthnTransactionChild* constructedMgr =
|
||||
aActor->SendPWebAuthnTransactionConstructor(mgr);
|
||||
|
||||
if (NS_WARN_IF(!constructedMgr)) {
|
||||
ActorFailed();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(constructedMgr == mgr);
|
||||
mChild = mgr.forget();
|
||||
// Add a ref to mChild here, that will be deref'd by
|
||||
// BackgroundChildImpl::DeallocPMIDIManagerChild on IPC cleanup.
|
||||
mChild->AddRef();
|
||||
mPBackgroundCreationPromise.Resolve(NS_OK, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::ActorDestroyed()
|
||||
{
|
||||
mChild = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::ActorFailed()
|
||||
{
|
||||
MOZ_CRASH("We shouldn't be here!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_WebAuthnManager_h
|
||||
#define mozilla_dom_WebAuthnManager_h
|
||||
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/dom/PWebAuthnTransaction.h"
|
||||
|
||||
/*
|
||||
* Content process manager for the WebAuthn protocol. Created on calls to the
|
||||
* WebAuthentication DOM object, this manager handles establishing IPC channels
|
||||
* for WebAuthn transactions, as well as keeping track of JS Promise objects
|
||||
* representing transactions in flight.
|
||||
*
|
||||
* The WebAuthn spec (https://www.w3.org/TR/webauthn/) allows for two different
|
||||
* types of transactions: registration and signing. When either of these is
|
||||
* requested via the DOM API, the following steps are executed in the
|
||||
* WebAuthnManager:
|
||||
*
|
||||
* - Validation of the request. Return a failed promise to js if request does
|
||||
* not have correct parameters.
|
||||
*
|
||||
* - If request is valid, open a new IPC channel for running the transaction. If
|
||||
* another transaction is already running in this content process, cancel it.
|
||||
* Return a pending promise to js.
|
||||
*
|
||||
* - Send transaction information to parent process (by running the Start*
|
||||
* functions of WebAuthnManager). Assuming another transaction is currently in
|
||||
* flight in another content process, parent will handle canceling it.
|
||||
*
|
||||
* - On return of successful transaction information from parent process, turn
|
||||
* information into DOM object format required by spec, and resolve promise
|
||||
* (by running the Finish* functions of WebAuthnManager). On cancellation
|
||||
* request from parent, reject promise with corresponding error code. Either
|
||||
* outcome will also close the IPC channel.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct Account;
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
struct AssertionOptions;
|
||||
class OwningArrayBufferViewOrArrayBuffer;
|
||||
struct ScopedCredentialOptions;
|
||||
struct ScopedCredentialParameters;
|
||||
class Promise;
|
||||
class WebAuthnTransactionChild;
|
||||
class WebAuthnTransactionInfo;
|
||||
|
||||
class WebAuthnManager final : public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
static WebAuthnManager* GetOrCreate();
|
||||
static WebAuthnManager* Get();
|
||||
|
||||
void
|
||||
FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer,
|
||||
nsTArray<uint8_t>& aSigBuffer);
|
||||
|
||||
void
|
||||
FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
|
||||
nsTArray<uint8_t>& aSigBuffer);
|
||||
|
||||
void
|
||||
Cancel(const nsresult& aError);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
const Account& aAccountInformation,
|
||||
const Sequence<ScopedCredentialParameters>& aCryptoParameters,
|
||||
const ArrayBufferViewOrArrayBuffer& aAttestationChallenge,
|
||||
const ScopedCredentialOptions& aOptions);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
const ArrayBufferViewOrArrayBuffer& aAssertionChallenge,
|
||||
const AssertionOptions& aOptions);
|
||||
|
||||
void StartRegister();
|
||||
void StartSign();
|
||||
|
||||
// nsIIPCbackgroundChildCreateCallback methods
|
||||
void ActorCreated(PBackgroundChild* aActor) override;
|
||||
void ActorFailed() override;
|
||||
void ActorDestroyed();
|
||||
private:
|
||||
WebAuthnManager();
|
||||
virtual ~WebAuthnManager();
|
||||
|
||||
void MaybeClearTransaction();
|
||||
|
||||
already_AddRefed<MozPromise<nsresult, nsresult, false>>
|
||||
GetOrCreateBackgroundActor();
|
||||
|
||||
// JS Promise representing transaction status.
|
||||
RefPtr<Promise> mTransactionPromise;
|
||||
|
||||
// IPC Channel for the current transaction.
|
||||
RefPtr<WebAuthnTransactionChild> mChild;
|
||||
|
||||
// Parent of the context we're currently running the transaction in.
|
||||
nsCOMPtr<nsPIDOMWindowInner> mCurrentParent;
|
||||
|
||||
// Client data, stored on successful transaction creation, so that it can be
|
||||
// used to assemble reply objects.
|
||||
Maybe<nsCString> mClientData;
|
||||
|
||||
// Holds the parameters of the current transaction, as we need them both
|
||||
// before the transaction request is sent, and on successful return.
|
||||
Maybe<WebAuthnTransactionInfo> mInfo;
|
||||
|
||||
// Promise for dealing with PBackground Actor creation.
|
||||
MozPromiseHolder<MozPromise<nsresult, nsresult, false>> mPBackgroundCreationPromise;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_WebAuthnManager_h
|
|
@ -1,53 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebAuthnTransactionChild::RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer,
|
||||
nsTArray<uint8_t>&& aSigBuffer)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
|
||||
MOZ_ASSERT(mgr);
|
||||
mgr->FinishMakeCredential(aRegBuffer, aSigBuffer);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebAuthnTransactionChild::RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
|
||||
nsTArray<uint8_t>&& aBuffer)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
|
||||
MOZ_ASSERT(mgr);
|
||||
mgr->FinishGetAssertion(aCredentialId, aBuffer);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebAuthnTransactionChild::RecvCancel(const nsresult& aError)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
|
||||
MOZ_ASSERT(mgr);
|
||||
mgr->Cancel(aError);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnTransactionChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
|
||||
// This could happen after the WebAuthnManager has been shut down.
|
||||
if (mgr) {
|
||||
mgr->ActorDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_WebAuthnTransactionChild_h
|
||||
#define mozilla_dom_WebAuthnTransactionChild_h
|
||||
|
||||
#include "mozilla/dom/PWebAuthnTransactionChild.h"
|
||||
|
||||
/*
|
||||
* Child process IPC implementation for WebAuthn API. Receives results of
|
||||
* WebAuthn transactions from the parent process, and sends them to the
|
||||
* WebAuthnManager either cancel the transaction, or be formatted and relayed to
|
||||
* content.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WebAuthnTransactionChild final : public PWebAuthnTransactionChild
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(WebAuthnTransactionChild);
|
||||
WebAuthnTransactionChild() = default;
|
||||
mozilla::ipc::IPCResult RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer,
|
||||
nsTArray<uint8_t>&& aSigBuffer) override;
|
||||
mozilla::ipc::IPCResult RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
|
||||
nsTArray<uint8_t>&& aBuffer) override;
|
||||
mozilla::ipc::IPCResult RecvCancel(const nsresult& aError) override;
|
||||
void ActorDestroy(ActorDestroyReason why) override;
|
||||
private:
|
||||
~WebAuthnTransactionChild() = default;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //mozilla_dom_WebAuthnTransactionChild_h
|
|
@ -1,131 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/WebAuthnUtil.h"
|
||||
#include "pkixutil.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
nsresult
|
||||
ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
|
||||
uint32_t aLen)
|
||||
{
|
||||
if (aSrc.EnsureLength(aLen) != pkix::Success) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
aDest.ClearAndRetainStorage();
|
||||
|
||||
for (uint32_t offset = 0; offset < aLen; ++offset) {
|
||||
uint8_t b;
|
||||
if (aSrc.Read(b) != pkix::Success) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
if (!aDest.AppendElement(b, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
|
||||
const CryptoBuffer& aRpIdHash,
|
||||
const CryptoBuffer& aSignatureData)
|
||||
{
|
||||
// The AuthenticatorData for U2F devices is the concatenation of the
|
||||
// RP ID with the output of the U2F Sign operation.
|
||||
if (aRpIdHash.Length() != 32) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
|
||||
/* out */ CryptoBuffer& aPubKeyBuf,
|
||||
/* out */ CryptoBuffer& aKeyHandleBuf,
|
||||
/* out */ CryptoBuffer& aAttestationCertBuf,
|
||||
/* out */ CryptoBuffer& aSignatureBuf)
|
||||
{
|
||||
// U2F v1.1 Format via
|
||||
// http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
|
||||
//
|
||||
// Bytes Value
|
||||
// 1 0x05
|
||||
// 65 public key
|
||||
// 1 key handle length
|
||||
// * key handle
|
||||
// ASN.1 attestation certificate
|
||||
// * attestation signature
|
||||
|
||||
pkix::Input u2fResponse;
|
||||
u2fResponse.Init(aResponse.Elements(), aResponse.Length());
|
||||
|
||||
pkix::Reader input(u2fResponse);
|
||||
|
||||
uint8_t b;
|
||||
if (input.Read(b) != pkix::Success) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
if (b != 0x05) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint8_t handleLen;
|
||||
if (input.Read(handleLen) != pkix::Success) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
|
||||
// length.
|
||||
pkix::Input cert;
|
||||
if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
|
||||
!= pkix::Success) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
pkix::Reader certInput(cert);
|
||||
rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// The remainder of u2fResponse is the signature
|
||||
pkix::Input u2fSig;
|
||||
input.SkipToEnd(u2fSig);
|
||||
pkix::Reader sigInput(u2fSig);
|
||||
rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_WebAuthnUtil_h
|
||||
#define mozilla_dom_WebAuthnUtil_h
|
||||
|
||||
/*
|
||||
* Utility functions used by both WebAuthnManager and U2FTokenManager.
|
||||
*/
|
||||
|
||||
#include "mozilla/dom/CryptoBuffer.h"
|
||||
#include "pkix/Input.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
nsresult
|
||||
U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
|
||||
const CryptoBuffer& aRpIdHash,
|
||||
const CryptoBuffer& aSignatureData);
|
||||
|
||||
nsresult
|
||||
U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
|
||||
/* out */ CryptoBuffer& aPubKeyBuf,
|
||||
/* out */ CryptoBuffer& aKeyHandleBuf,
|
||||
/* out */ CryptoBuffer& aAttestationCertBuf,
|
||||
/* out */ CryptoBuffer& aSignatureBuf);
|
||||
|
||||
nsresult
|
||||
ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
|
||||
uint32_t aLen);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_WebAuthnUtil_h
|
|
@ -18,10 +18,7 @@ EXPORTS.mozilla.dom += [
|
|||
'WebAuthentication.h',
|
||||
'WebAuthnAssertion.h',
|
||||
'WebAuthnAttestation.h',
|
||||
'WebAuthnManager.h',
|
||||
'WebAuthnRequest.h',
|
||||
'WebAuthnTransactionChild.h',
|
||||
'WebAuthnUtil.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -31,9 +28,6 @@ UNIFIED_SOURCES += [
|
|||
'WebAuthentication.cpp',
|
||||
'WebAuthnAssertion.cpp',
|
||||
'WebAuthnAttestation.cpp',
|
||||
'WebAuthnManager.cpp',
|
||||
'WebAuthnTransactionChild.cpp',
|
||||
'WebAuthnUtil.cpp'
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include "mozilla/layout/VsyncChild.h"
|
||||
#include "mozilla/net/PUDPSocketChild.h"
|
||||
#include "mozilla/dom/network/UDPSocketChild.h"
|
||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||
#include "nsID.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
|
||||
|
@ -79,8 +78,6 @@ using mozilla::dom::cache::PCacheChild;
|
|||
using mozilla::dom::cache::PCacheStorageChild;
|
||||
using mozilla::dom::cache::PCacheStreamControlChild;
|
||||
|
||||
using mozilla::dom::WebAuthnTransactionChild;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// BackgroundChildImpl::ThreadLocal
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -563,22 +560,6 @@ BackgroundChildImpl::OnChannelReceivedMessage(const Message& aMsg)
|
|||
}
|
||||
#endif
|
||||
|
||||
dom::PWebAuthnTransactionChild*
|
||||
BackgroundChildImpl::AllocPWebAuthnTransactionChild()
|
||||
{
|
||||
MOZ_CRASH("PWebAuthnTransaction actor should be manually constructed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundChildImpl::DeallocPWebAuthnTransactionChild(PWebAuthnTransactionChild* aActor)
|
||||
{
|
||||
MOZ_ASSERT(aActor);
|
||||
RefPtr<dom::WebAuthnTransactionChild> child =
|
||||
dont_AddRef(static_cast<dom::WebAuthnTransactionChild*>(aActor));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -202,12 +202,6 @@ protected:
|
|||
virtual void
|
||||
OnChannelReceivedMessage(const Message& aMsg) override;
|
||||
#endif
|
||||
|
||||
virtual PWebAuthnTransactionChild*
|
||||
AllocPWebAuthnTransactionChild() override;
|
||||
|
||||
virtual bool
|
||||
DeallocPWebAuthnTransactionChild(PWebAuthnTransactionChild* aActor) override;
|
||||
};
|
||||
|
||||
class BackgroundChildImpl::ThreadLocal final
|
||||
|
|
Загрузка…
Ссылка в новой задаче