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:
Tim Taubert 2018-05-30 16:06:09 +02:00
parent e09ad9e15c
commit 2a252e45a4
21 changed files with 631 additions and 566 deletions

View File

@ -7109,7 +7109,7 @@ var WebAuthnPromptHelper = {
label: gNavigatorBundle.getString("webauthn.proceed"),
accessKey: gNavigatorBundle.getString("webauthn.proceed.accesskey"),
callback(state) {
mgr.resumeRegister(tid, !state.checkboxChecked);
mgr.resumeRegister(tid, state.checkboxChecked);
}
};
},

View File

@ -11,13 +11,9 @@
#include "mozilla/dom/WebAuthnTransactionChild.h"
#include "mozilla/dom/WebAuthnUtil.h"
#include "nsContentUtils.h"
#include "nsICryptoHash.h"
#include "nsIEffectiveTLDService.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsURLParsers.h"
#include "U2FUtil.h"
#include "hasht.h"
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
**********************************************************************/
@ -280,17 +223,18 @@ U2F::Register(const nsAString& aAppId,
return;
}
// Produce the AppParam from the current AppID
nsCString cAppId = NS_ConvertUTF16toUTF8(adjustedAppId);
nsAutoString clientDataJSON;
// Pick the first valid RegisterRequest; we can only work with one.
CryptoBuffer challenge;
for (const RegisterRequest& req : aRegisterRequests) {
if (!req.mChallenge.WasPassed() || !req.mVersion.WasPassed() ||
req.mVersion.Value() != kRequiredU2FVersion) {
continue;
}
if (!challenge.Assign(NS_ConvertUTF16toUTF8(req.mChallenge.Value()))) {
continue;
}
nsresult rv = AssembleClientData(mOrigin, kFinishEnrollment,
req.mChallenge.Value(), clientDataJSON);
@ -312,17 +256,6 @@ U2F::Register(const nsAString& aAppId,
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
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()) {
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
@ -332,27 +265,19 @@ U2F::Register(const nsAString& aAppId,
ListenForVisibilityEvents();
// Always blank for U2F
nsTArray<WebAuthnExtension> extensions;
// Default values for U2F.
WebAuthnAuthenticatorSelection authSelection(false /* requireResidentKey */,
false /* requireUserVerification */,
false /* requirePlatformAttachment */);
NS_ConvertUTF16toUTF8 clientData(clientDataJSON);
uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
WebAuthnMakeCredentialInfo info(mOrigin,
rpIdHash,
clientDataHash,
adjustedAppId,
challenge,
clientData,
adjustedTimeoutMillis,
excludeList,
extensions,
authSelection,
false /* RequestDirectAttestation */);
null_t() /* no extra info for U2F */);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(clientData, Move(AsVariant(callback))));
mTransaction = Some(U2FTransaction(Move(AsVariant(callback))));
mChild->SendRequestRegister(mTransaction.ref().mId, info);
}
@ -372,14 +297,20 @@ U2F::FinishMakeCredential(const uint64_t& aTransactionId,
return;
}
// A CTAP2 response.
if (aResult.RegistrationData().Length() == 0) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer regBuf;
if (NS_WARN_IF(!regBuf.Assign(aResult.RegBuffer()))) {
if (NS_WARN_IF(!regBuf.Assign(aResult.RegistrationData()))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
@ -455,22 +386,19 @@ U2F::Sign(const nsAString& aAppId,
return;
}
// Build the key list, if any
nsTArray<WebAuthnScopedCredential> permittedList;
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
permittedList);
auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
CryptoBuffer rpIdHash, clientDataHash;
if (NS_FAILED(BuildTransactionHashes(cAppId, clientData,
rpIdHash, clientDataHash))) {
CryptoBuffer challenge;
if (!challenge.Assign(NS_ConvertUTF16toUTF8(aChallenge))) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
ExecuteCallback(response, callback);
return;
}
// Build the key list, if any
nsTArray<WebAuthnScopedCredential> permittedList;
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
permittedList);
if (!MaybeCreateBackgroundActor()) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
@ -483,18 +411,19 @@ U2F::Sign(const nsAString& aAppId,
// Always blank for U2F
nsTArray<WebAuthnExtension> extensions;
NS_ConvertUTF16toUTF8 clientData(clientDataJSON);
uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
WebAuthnGetAssertionInfo info(mOrigin,
rpIdHash,
clientDataHash,
adjustedAppId,
challenge,
clientData,
adjustedTimeoutMillis,
permittedList,
false, /* requireUserVerification */
extensions);
null_t() /* no extra info for U2F */);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(clientData, Move(AsVariant(callback))));
mTransaction = Some(U2FTransaction(Move(AsVariant(callback))));
mChild->SendRequestSign(mTransaction.ref().mId, info);
}
@ -514,20 +443,26 @@ U2F::FinishGetAssertion(const uint64_t& aTransactionId,
return;
}
// A CTAP2 response.
if (aResult.SignatureData().Length() == 0) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer credBuf;
if (NS_WARN_IF(!credBuf.Assign(aResult.CredentialID()))) {
if (NS_WARN_IF(!credBuf.Assign(aResult.KeyHandle()))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer sigBuf;
if (NS_WARN_IF(!sigBuf.Assign(aResult.SigBuffer()))) {
if (NS_WARN_IF(!sigBuf.Assign(aResult.SignatureData()))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}

View File

@ -38,10 +38,8 @@ class U2FTransaction
nsMainThreadPtrHandle<U2FSignCallback>> U2FCallback;
public:
explicit U2FTransaction(const nsCString& aClientData,
const U2FCallback&& aCallback)
: mClientData(aClientData)
, mCallback(Move(aCallback))
explicit U2FTransaction(const U2FCallback&& aCallback)
: mCallback(Move(aCallback))
, mId(NextId())
{
MOZ_ASSERT(mId > 0);
@ -63,9 +61,6 @@ public:
return mCallback.as<nsMainThreadPtrHandle<U2FSignCallback>>();
}
// Client data used to assemble reply objects.
nsCString mClientData;
// The callback passed to the API.
U2FCallback mCallback;

View File

@ -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

View File

@ -10,7 +10,6 @@ with Files("**"):
EXPORTS.mozilla.dom += [
'U2F.h',
'U2FAuthenticator.h',
'U2FUtil.h',
]
UNIFIED_SOURCES += [

View File

@ -16,6 +16,8 @@
include protocol PBackground;
using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace dom {
@ -46,37 +48,63 @@ union WebAuthnExtensionResult {
WebAuthnExtensionResultAppId;
};
struct WebAuthnMakeCredentialInfo {
nsString Origin;
uint8_t[] RpIdHash;
uint8_t[] ClientDataHash;
uint32_t TimeoutMS;
WebAuthnScopedCredential[] ExcludeList;
struct WebAuthnMakeCredentialExtraInfo {
WebAuthnExtension[] Extensions;
WebAuthnAuthenticatorSelection AuthenticatorSelection;
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 {
uint8_t[] RegBuffer;
bool DirectAttestationPermitted;
nsCString ClientDataJSON;
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 {
nsString Origin;
uint8_t[] RpIdHash;
uint8_t[] ClientDataHash;
nsString RpId;
uint8_t[] Challenge;
nsCString ClientDataJSON;
uint32_t TimeoutMS;
WebAuthnScopedCredential[] AllowList;
bool RequireUserVerification;
WebAuthnExtension[] Extensions;
WebAuthnMaybeGetAssertionExtraInfo Extra;
};
struct WebAuthnGetAssertionResult {
uint8_t[] RpIdHash;
uint8_t[] CredentialID;
uint8_t[] SigBuffer;
nsCString ClientDataJSON;
uint8_t[] KeyHandle;
uint8_t[] Signature;
uint8_t[] AuthenticatorData;
WebAuthnExtensionResult[] Extensions;
/* Might be empty if the token implementation doesn't support CTAP1. */
uint8_t[] SignatureData;
};
async protocol PWebAuthnTransaction {

View File

@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/U2FHIDTokenManager.h"
#include "mozilla/dom/WebAuthnUtil.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/StaticMutex.h"
@ -51,7 +52,7 @@ u2f_sign_callback(uint64_t aTransactionId, rust_u2f_result* aResult)
NS_DISPATCH_NORMAL));
}
U2FHIDTokenManager::U2FHIDTokenManager() : mTransactionId(0)
U2FHIDTokenManager::U2FHIDTokenManager()
{
StaticMutexAutoLock lock(gInstanceMutex);
mozilla::ipc::AssertIsOnBackgroundThread();
@ -84,7 +85,7 @@ U2FHIDTokenManager::Drop()
mU2FManager = nullptr;
// 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.
@ -107,41 +108,56 @@ U2FHIDTokenManager::Drop()
// * attestation signature
//
RefPtr<U2FRegisterPromise>
U2FHIDTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
U2FHIDTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation)
{
mozilla::ipc::AssertIsOnBackgroundThread();
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.
if (sel.requireResidentKey()) {
registerFlags |= U2F_FLAG_REQUIRE_RESIDENT_KEY;
// Set flags for credential creation.
if (sel.requireResidentKey()) {
registerFlags |= U2F_FLAG_REQUIRE_RESIDENT_KEY;
}
if (sel.requireUserVerification()) {
registerFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
}
if (sel.requirePlatformAttachment()) {
registerFlags |= U2F_FLAG_REQUIRE_PLATFORM_ATTACHMENT;
}
}
if (sel.requireUserVerification()) {
registerFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
}
if (sel.requirePlatformAttachment()) {
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();
mCurrentAppId = aInfo.RpIdHash();
mTransactionId = rust_u2f_mgr_register(mU2FManager,
registerFlags,
(uint64_t)aInfo.TimeoutMS(),
u2f_register_callback,
aInfo.ClientDataHash().Elements(),
aInfo.ClientDataHash().Length(),
aInfo.RpIdHash().Elements(),
aInfo.RpIdHash().Length(),
U2FKeyHandles(aInfo.ExcludeList()).Get());
mTransaction.reset();
uint64_t tid = rust_u2f_mgr_register(mU2FManager,
registerFlags,
(uint64_t)aInfo.TimeoutMS(),
u2f_register_callback,
clientDataHash.Elements(),
clientDataHash.Length(),
rpIdHash.Elements(),
rpIdHash.Length(),
U2FKeyHandles(aInfo.ExcludeList()).Get());
if (mTransactionId == 0) {
if (tid == 0) {
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
}
mTransaction = Some(Transaction(tid, rpIdHash, aInfo.ClientDataJSON(),
aForceNoneAttestation));
return mRegisterPromise.Ensure(__func__);
}
@ -166,37 +182,50 @@ U2FHIDTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
{
mozilla::ipc::AssertIsOnBackgroundThread();
uint64_t signFlags = 0;
// Set flags for credential requests.
if (aInfo.RequireUserVerification()) {
signFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
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__);
}
mCurrentAppId = aInfo.RpIdHash();
uint64_t signFlags = 0;
nsTArray<nsTArray<uint8_t>> appIds;
appIds.AppendElement(mCurrentAppId);
appIds.AppendElement(rpIdHash);
// Process extensions.
for (const WebAuthnExtension& ext: aInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
if (aInfo.Extra().type() != WebAuthnMaybeGetAssertionExtraInfo::Tnull_t) {
const auto& extra = aInfo.Extra().get_WebAuthnGetAssertionExtraInfo();
// Set flags for credential requests.
if (extra.RequireUserVerification()) {
signFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
}
// Process extensions.
for (const WebAuthnExtension& ext: extra.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
}
}
}
ClearPromises();
mTransactionId = rust_u2f_mgr_sign(mU2FManager,
signFlags,
(uint64_t)aInfo.TimeoutMS(),
u2f_sign_callback,
aInfo.ClientDataHash().Elements(),
aInfo.ClientDataHash().Length(),
U2FAppIds(appIds).Get(),
U2FKeyHandles(aInfo.AllowList()).Get());
if (mTransactionId == 0) {
mTransaction.reset();
uint64_t tid = rust_u2f_mgr_sign(mU2FManager,
signFlags,
(uint64_t)aInfo.TimeoutMS(),
u2f_sign_callback,
clientDataHash.Elements(),
clientDataHash.Length(),
U2FAppIds(appIds).Get(),
U2FKeyHandles(aInfo.AllowList()).Get());
if (tid == 0) {
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
}
mTransaction = Some(Transaction(tid, rpIdHash, aInfo.ClientDataJSON()));
return mSignPromise.Ensure(__func__);
}
@ -206,7 +235,8 @@ U2FHIDTokenManager::Cancel()
mozilla::ipc::AssertIsOnBackgroundThread();
ClearPromises();
mTransactionId = rust_u2f_mgr_cancel(mU2FManager);
rust_u2f_mgr_cancel(mU2FManager);
mTransaction.reset();
}
void
@ -214,7 +244,8 @@ U2FHIDTokenManager::HandleRegisterResult(UniquePtr<U2FResult>&& aResult)
{
mozilla::ipc::AssertIsOnBackgroundThread();
if (aResult->GetTransactionId() != mTransactionId) {
if (mTransaction.isNothing() ||
aResult->GetTransactionId() != mTransaction.ref().mId) {
return;
}
@ -231,9 +262,41 @@ U2FHIDTokenManager::HandleRegisterResult(UniquePtr<U2FResult>&& aResult)
return;
}
// Will be set by the U2FTokenManager.
bool directAttestationPermitted = false;
WebAuthnMakeCredentialResult result(registration, directAttestationPermitted);
// Decompose the U2F registration packet
CryptoBuffer pubKeyBuf;
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__);
}
@ -242,7 +305,8 @@ U2FHIDTokenManager::HandleSignResult(UniquePtr<U2FResult>&& aResult)
{
mozilla::ipc::AssertIsOnBackgroundThread();
if (aResult->GetTransactionId() != mTransactionId) {
if (mTransaction.isNothing() ||
aResult->GetTransactionId() != mTransaction.ref().mId) {
return;
}
@ -271,14 +335,51 @@ U2FHIDTokenManager::HandleSignResult(UniquePtr<U2FResult>&& aResult)
return;
}
CryptoBuffer rawSignatureBuf;
if (!rawSignatureBuf.Assign(signature)) {
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
return;
}
nsTArray<WebAuthnExtensionResult> extensions;
if (appId != mCurrentAppId) {
if (appId != mTransaction.ref().mRpIdHash) {
// Indicate to the RP that we used the FIDO appId.
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__);
}

View File

@ -134,7 +134,8 @@ public:
explicit U2FHIDTokenManager();
RefPtr<U2FRegisterPromise>
Register(const WebAuthnMakeCredentialInfo& aInfo) override;
Register(const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation) override;
RefPtr<U2FSignPromise>
Sign(const WebAuthnGetAssertionInfo& aInfo) override;
@ -153,9 +154,34 @@ private:
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;
uint64_t mTransactionId;
nsTArray<uint8_t> mCurrentAppId;
Maybe<Transaction> mTransaction;
MozPromiseHolder<U2FRegisterPromise> mRegisterPromise;
MozPromiseHolder<U2FSignPromise> mSignPromise;
};

View File

@ -583,7 +583,8 @@ U2FSoftTokenManager::IsRegistered(const nsTArray<uint8_t>& aKeyHandle,
// * attestation signature
//
RefPtr<U2FRegisterPromise>
U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation)
{
if (!mInitialized) {
nsresult rv = Init();
@ -592,20 +593,31 @@ 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
// user verification, nor is it a platform authenticator.
if (sel.requireResidentKey() ||
sel.requireUserVerification() ||
sel.requirePlatformAttachment()) {
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
// The U2F softtoken neither supports resident keys or
// user verification, nor is it a platform authenticator.
if (sel.requireResidentKey() ||
sel.requireUserVerification() ||
sel.requirePlatformAttachment()) {
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.
for (const WebAuthnScopedCredential& cred: aInfo.ExcludeList()) {
bool isRegistered = false;
nsresult rv = IsRegistered(cred.id(), aInfo.RpIdHash(), isRegistered);
nsresult rv = IsRegistered(cred.id(), rpIdHash, isRegistered);
if (NS_FAILED(rv)) {
return U2FRegisterPromise::CreateAndReject(rv, __func__);
}
@ -623,7 +635,7 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
// Construct a one-time-use Attestation Certificate
UniqueSECKEYPrivateKey attestPrivKey;
UniqueCERTCertificate attestCert;
nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert);
rv = GetAttestationCertificate(slot, attestPrivKey, attestCert);
if (NS_WARN_IF(NS_FAILED(rv))) {
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)
UniqueSECItem keyHandleItem =
KeyHandleFromPrivateKey(slot, mWrappingKey,
const_cast<uint8_t*>(aInfo.RpIdHash().Elements()),
aInfo.RpIdHash().Length(), privKey);
const_cast<uint8_t*>(rpIdHash.Elements()),
rpIdHash.Length(), privKey);
if (NS_WARN_IF(!keyHandleItem.get())) {
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Sign the challenge using the Attestation privkey (from attestCert)
mozilla::dom::CryptoBuffer signedDataBuf;
if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aInfo.RpIdHash().Length() +
aInfo.ClientDataHash().Length() +
if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + rpIdHash.Length() +
clientDataHash.Length() +
keyHandleItem->len + kPublicKeyLen,
mozilla::fallible))) {
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
// // pre-allocated space
signedDataBuf.AppendElement(0x00, mozilla::fallible);
signedDataBuf.AppendElements(aInfo.RpIdHash(), mozilla::fallible);
signedDataBuf.AppendElements(aInfo.ClientDataHash(), mozilla::fallible);
signedDataBuf.AppendElements(rpIdHash, mozilla::fallible);
signedDataBuf.AppendElements(clientDataHash, mozilla::fallible);
signedDataBuf.AppendSECItem(keyHandleItem.get());
signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue);
@ -688,10 +700,36 @@ U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo)
registrationBuf.AppendSECItem(attestCert.get()->derCert);
registrationBuf.AppendSECItem(signatureItem);
// Will be set by the U2FTokenManager.
bool directAttestationPermitted = false;
WebAuthnMakeCredentialResult result((nsTArray<uint8_t>(registrationBuf)),
directAttestationPermitted);
CryptoBuffer keyHandleBuf;
if (!keyHandleBuf.AppendSECItem(keyHandleItem.get())) {
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
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__);
}
@ -742,18 +780,30 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
}
}
// The U2F softtoken doesn't support user verification.
if (aInfo.RequireUserVerification()) {
return U2FSignPromise::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 U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
}
nsTArray<nsTArray<uint8_t>> appIds;
appIds.AppendElement(aInfo.RpIdHash());
appIds.AppendElement(rpIdHash);
// Process extensions.
for (const WebAuthnExtension& ext: aInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
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.
for (const WebAuthnExtension& ext: extra.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
}
}
}
@ -770,11 +820,11 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
MOZ_ASSERT(slot.get());
if (NS_WARN_IF((aInfo.ClientDataHash().Length() != kParamLen) ||
if (NS_WARN_IF((clientDataHash.Length() != kParamLen) ||
(chosenAppId.Length() != kParamLen))) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
(uint32_t)aInfo.ClientDataHash().Length(),
(uint32_t)clientDataHash.Length(),
(uint32_t)chosenAppId.Length(), kParamLen));
return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
@ -820,8 +870,8 @@ U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
mozilla::fallible);
signedDataBuf.AppendElement(0x01, mozilla::fallible);
signedDataBuf.AppendSECItem(counterItem);
signedDataBuf.AppendElements(aInfo.ClientDataHash().Elements(),
aInfo.ClientDataHash().Length(),
signedDataBuf.AppendElements(clientDataHash.Elements(),
clientDataHash.Length(),
mozilla::fallible);
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
mozilla::dom::CryptoBuffer signatureBuf;
if (NS_WARN_IF(!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
mozilla::dom::CryptoBuffer signatureDataBuf;
if (NS_WARN_IF(!signatureDataBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
mozilla::fallible))) {
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
}
// It's OK to ignore the return values here because we're writing into
// pre-allocated space
signatureBuf.AppendElement(0x01, mozilla::fallible);
signatureBuf.AppendSECItem(counterItem);
signatureBuf.AppendSECItem(signatureItem);
signatureDataBuf.AppendElement(0x01, mozilla::fallible);
signatureDataBuf.AppendSECItem(counterItem);
signatureDataBuf.AppendSECItem(signatureItem);
nsTArray<uint8_t> signature(signatureBuf);
nsTArray<WebAuthnExtensionResult> extensions;
if (chosenAppId != aInfo.RpIdHash()) {
if (chosenAppId != rpIdHash) {
// Indicate to the RP that we used the FIDO appId.
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__);
}

View File

@ -24,7 +24,8 @@ public:
explicit U2FSoftTokenManager(uint32_t aCounter);
RefPtr<U2FRegisterPromise>
Register(const WebAuthnMakeCredentialInfo& aInfo) override;
Register(const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation) override;
RefPtr<U2FSignPromise>
Sign(const WebAuthnGetAssertionInfo& aInfo) override;

View File

@ -14,11 +14,7 @@
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Unused.h"
#include "hasht.h"
#include "nsICryptoHash.h"
#include "nsTextFormatter.h"
#include "pkix/Input.h"
#include "pkixutil.h"
// Not named "security.webauth.u2f_softtoken_counter" because setting that
// name causes the window.u2f object to disappear until preferences get
@ -303,37 +299,38 @@ U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
return;
}
// Check if all the supplied parameters are syntactically well-formed and
// of the correct length. If not, return an error code equivalent to
// UnknownError and terminate the operation.
mLastTransactionId = aTransactionId;
if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
(aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
// Determine whether direct attestation was requested.
bool directAttestationRequested = false;
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;
}
mLastTransactionId = aTransactionId;
// If the RP request direct attestation, ask the user for permission and
// 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());
SendPromptNotification(kRegisterDirectPromptNotifcation,
aTransactionId,
origin.get());
NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
SendPromptNotification(kRegisterDirectPromptNotifcation,
aTransactionId,
origin.get());
MOZ_ASSERT(mPendingRegisterInfo.isNothing());
mPendingRegisterInfo = Some(aTransactionInfo);
} else {
DoRegister(aTransactionInfo);
}
MOZ_ASSERT(mPendingRegisterInfo.isNothing());
mPendingRegisterInfo = Some(aTransactionInfo);
}
void
U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo)
U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation)
{
mozilla::ipc::AssertIsOnBackgroundThread();
MOZ_ASSERT(mLastTransactionId > 0);
@ -346,17 +343,12 @@ U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo)
uint64_t tid = mLastTransactionId;
mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
bool requestDirectAttestation = aInfo.RequestDirectAttestation();
mTokenManagerImpl
->Register(aInfo)
->Register(aInfo, aForceNoneAttestation)
->Then(GetCurrentThreadSerialEventTarget(), __func__,
[tid, startTime, requestDirectAttestation](WebAuthnMakeCredentialResult&& aResult) {
[tid, startTime](WebAuthnMakeCredentialResult&& aResult) {
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);
Telemetry::ScalarAdd(
Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
@ -412,12 +404,6 @@ U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
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.
NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
SendPromptNotification(kSignPromptNotifcation,
@ -487,7 +473,7 @@ U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent,
NS_IMETHODIMP
U2FTokenManager::ResumeRegister(uint64_t aTransactionId,
bool aPermitDirectAttestation)
bool aForceNoneAttestation)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
@ -499,14 +485,14 @@ U2FTokenManager::ResumeRegister(uint64_t aTransactionId,
nsCOMPtr<nsIRunnable> r(NewRunnableMethod<uint64_t, bool>(
"U2FTokenManager::RunResumeRegister", this,
&U2FTokenManager::RunResumeRegister, aTransactionId,
aPermitDirectAttestation));
aForceNoneAttestation));
return gBackgroundThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}
void
U2FTokenManager::RunResumeRegister(uint64_t aTransactionId,
bool aPermitDirectAttestation)
bool aForceNoneAttestation)
{
mozilla::ipc::AssertIsOnBackgroundThread();
@ -518,12 +504,8 @@ U2FTokenManager::RunResumeRegister(uint64_t aTransactionId,
return;
}
// Forward whether the user opted into direct attestation.
mPendingRegisterInfo.ref().RequestDirectAttestation() =
aPermitDirectAttestation;
// Resume registration and cleanup.
DoRegister(mPendingRegisterInfo.ref());
DoRegister(mPendingRegisterInfo.ref(), aForceNoneAttestation);
mPendingRegisterInfo.reset();
}

View File

@ -51,7 +51,8 @@ private:
void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError);
void ClearTransaction();
// 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,
const WebAuthnMakeCredentialResult& aResult);
void MaybeAbortRegister(const uint64_t& aTransactionId, const nsresult& aError);
@ -59,7 +60,7 @@ private:
const WebAuthnGetAssertionResult& aResult);
void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError);
// 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".
void RunCancel(uint64_t aTransactionId);
// Sends a "webauthn-prompt" observer notification with the given data.

View File

@ -28,7 +28,8 @@ public:
U2FTokenTransport() {}
virtual RefPtr<U2FRegisterPromise>
Register(const WebAuthnMakeCredentialInfo& aInfo) = 0;
Register(const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation) = 0;
virtual RefPtr<U2FSignPromise>
Sign(const WebAuthnGetAssertionInfo& aInfo) = 0;

View File

@ -6,15 +6,11 @@
#include "hasht.h"
#include "nsHTMLDocument.h"
#include "nsICryptoHash.h"
#include "nsNetCID.h"
#include "nsThreadUtils.h"
#include "WebAuthnCoseIdentifiers.h"
#include "mozilla/dom/AuthenticatorAttestationResponse.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PWebAuthnTransaction.h"
#include "mozilla/dom/U2FUtil.h"
#include "mozilla/dom/WebAuthnCBORUtil.h"
#include "mozilla/dom/WebAuthnManager.h"
#include "mozilla/dom/WebAuthnTransactionChild.h"
#include "mozilla/dom/WebAuthnUtil.h"
@ -26,15 +22,6 @@ using namespace mozilla::ipc;
namespace mozilla {
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
**********************************************************************/
@ -271,27 +258,6 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
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.
// Process each element of mPubKeyCredParams using the following steps, to
@ -351,21 +317,9 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
}
nsAutoCString clientDataJSON;
srv = AssembleClientData(origin, challenge,
NS_LITERAL_STRING("webauthn.create"),
aOptions.mExtensions, 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);
nsresult srv = AssembleClientData(origin, challenge,
NS_LITERAL_STRING("webauthn.create"),
aOptions.mExtensions, clientDataJSON);
if (NS_WARN_IF(NS_FAILED(srv))) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
@ -410,14 +364,17 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
requireUserVerification,
requirePlatformAttachment);
WebAuthnMakeCredentialExtraInfo extra(extensions,
authSelection,
requestDirectAttestation);
WebAuthnMakeCredentialInfo info(origin,
rpIdHash,
clientDataHash,
NS_ConvertUTF8toUTF16(rpId),
challenge,
clientDataJSON,
adjustedTimeout,
excludeList,
extensions,
authSelection,
requestDirectAttestation);
extra);
ListenForVisibilityEvents();
@ -428,11 +385,7 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
}
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(WebAuthnTransaction(promise,
rpIdHash,
clientDataJSON,
signal));
mTransaction = Some(WebAuthnTransaction(promise, signal));
mChild->SendRequestRegister(mTransaction.ref().mId, info);
return promise.forget();
@ -502,20 +455,6 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
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
@ -527,20 +466,9 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
}
nsAutoCString clientDataJSON;
srv = AssembleClientData(origin, challenge, NS_LITERAL_STRING("webauthn.get"),
aOptions.mExtensions, 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);
nsresult srv = AssembleClientData(origin, challenge,
NS_LITERAL_STRING("webauthn.get"),
aOptions.mExtensions, clientDataJSON);
if (NS_WARN_IF(NS_FAILED(srv))) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
@ -608,7 +536,7 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
}
// 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))) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
@ -618,13 +546,15 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
extensions.AppendElement(WebAuthnExtensionAppId(appIdHash));
}
WebAuthnGetAssertionExtraInfo extra(extensions, requireUserVerification);
WebAuthnGetAssertionInfo info(origin,
rpIdHash,
clientDataHash,
NS_ConvertUTF8toUTF16(rpId),
challenge,
clientDataJSON,
adjustedTimeout,
allowList,
requireUserVerification,
extensions);
extra);
ListenForVisibilityEvents();
@ -635,12 +565,9 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
}
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(WebAuthnTransaction(promise,
rpIdHash,
clientDataJSON,
signal));
mTransaction = Some(WebAuthnTransaction(promise, signal));
mChild->SendRequestSign(mTransaction.ref().mId, info);
return promise.forget();
}
@ -676,120 +603,38 @@ WebAuthnManager::FinishMakeCredential(const uint64_t& aTransactionId,
return;
}
CryptoBuffer regData;
if (NS_WARN_IF(!regData.Assign(aResult.RegBuffer().Elements(),
aResult.RegBuffer().Length()))) {
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
mozilla::dom::CryptoBuffer aaguidBuf;
if (NS_WARN_IF(!aaguidBuf.SetCapacity(16, mozilla::fallible))) {
CryptoBuffer attObjBuf;
if (NS_WARN_IF(!attObjBuf.Assign(aResult.AttestationObject()))) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
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 attestationCertBuf;
CryptoBuffer signatureBuf;
// 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);
if (NS_WARN_IF(!keyHandleBuf.Assign(aResult.KeyHandle()))) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
MOZ_ASSERT(keyHandleBuf.Length() <= 0xFFFF);
nsAutoString keyHandleBase64Url;
rv = keyHandleBuf.ToJwkBase64(keyHandleBase64Url);
nsresult rv = keyHandleBuf.ToJwkBase64(keyHandleBase64Url);
if (NS_WARN_IF(NS_FAILED(rv))) {
RejectTransaction(rv);
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
// values returned from the authenticator as well as the clientDataJSON
// computed earlier.
RefPtr<AuthenticatorAttestationResponse> attestation =
new AuthenticatorAttestationResponse(mParent);
attestation->SetClientDataJSON(clientDataBuf);
attestation->SetAttestationObject(attObj);
attestation->SetAttestationObject(attObjBuf);
RefPtr<PublicKeyCredential> credential =
new PublicKeyCredential(mParent);
@ -813,57 +658,32 @@ WebAuthnManager::FinishGetAssertion(const uint64_t& aTransactionId,
return;
}
CryptoBuffer tokenSignatureData;
if (NS_WARN_IF(!tokenSignatureData.Assign(aResult.SigBuffer().Elements(),
aResult.SigBuffer().Length()))) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
CryptoBuffer clientDataBuf;
if (!clientDataBuf.Assign(mTransaction.ref().mClientData)) {
if (!clientDataBuf.Assign(aResult.ClientDataJSON())) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
CryptoBuffer rpIdHashBuf;
if (!rpIdHashBuf.Assign(aResult.RpIdHash())) {
CryptoBuffer credentialBuf;
if (!credentialBuf.Assign(aResult.KeyHandle())) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
CryptoBuffer signatureBuf;
CryptoBuffer counterBuf;
uint8_t flags = 0;
nsresult rv = U2FDecomposeSignResponse(tokenSignatureData, flags, counterBuf,
signatureBuf);
if (NS_WARN_IF(NS_FAILED(rv))) {
RejectTransaction(rv);
if (!signatureBuf.Assign(aResult.Signature())) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
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;
rv = AssembleAuthenticatorData(rpIdHashBuf, flags, counterBuf,
/* deliberately empty */ attestationDataBuf,
authenticatorDataBuf);
if (NS_WARN_IF(NS_FAILED(rv))) {
RejectTransaction(rv);
return;
}
CryptoBuffer credentialBuf;
if (!credentialBuf.Assign(aResult.CredentialID())) {
if (!authenticatorDataBuf.Assign(aResult.AuthenticatorData())) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
nsAutoString credentialBase64Url;
rv = credentialBuf.ToJwkBase64(credentialBase64Url);
nsresult rv = credentialBuf.ToJwkBase64(credentialBase64Url);
if (NS_WARN_IF(NS_FAILED(rv))) {
RejectTransaction(rv);
return;

View File

@ -48,12 +48,8 @@ class WebAuthnTransaction
{
public:
WebAuthnTransaction(const RefPtr<Promise>& aPromise,
const nsTArray<uint8_t>& aRpIdHash,
const nsCString& aClientData,
AbortSignal* aSignal)
: mPromise(aPromise)
, mRpIdHash(aRpIdHash)
, mClientData(aClientData)
, mSignal(aSignal)
, mId(NextId())
{
@ -63,12 +59,6 @@ public:
// JS Promise representing the transaction status.
RefPtr<Promise> mPromise;
// The RP ID hash.
nsTArray<uint8_t> mRpIdHash;
// Client data used to assemble reply objects.
nsCString mClientData;
// An optional AbortSignal instance.
RefPtr<AbortSignal> mSignal;

View File

@ -18,6 +18,9 @@ NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId1,
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId2,
"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
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
const U2FOperation& aOp, /* in/out */ nsString& aAppId)
@ -193,6 +196,74 @@ AssembleAttestationData(const CryptoBuffer& aaguidBuf,
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
U2FDecomposeSignResponse(const CryptoBuffer& aResponse,
/* out */ uint8_t& aFlags,
@ -323,5 +394,89 @@ U2FDecomposeECKey(const CryptoBuffer& aPubKeyBuf,
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;
}
}
}

View File

@ -12,7 +12,6 @@
*/
#include "mozilla/dom/CryptoBuffer.h"
#include "pkix/Input.h"
namespace mozilla {
namespace dom {
@ -35,10 +34,13 @@ AssembleAuthenticatorData(const CryptoBuffer& rpIdHashBuf,
/* out */ CryptoBuffer& authDataBuf);
nsresult
AssembleAttestationData(const CryptoBuffer& aaguidBuf,
const CryptoBuffer& keyHandleBuf,
const CryptoBuffer& pubKeyObj,
/* out */ CryptoBuffer& attestationDataBuf);
AssembleAttestationObject(const CryptoBuffer& aRpIdHash,
const CryptoBuffer& aPubKeyBuf,
const CryptoBuffer& aKeyHandleBuf,
const CryptoBuffer& aAttestationCertBuf,
const CryptoBuffer& aSignatureBuf,
bool aForceNoneAttestation,
/* out */ CryptoBuffer& aAttestationObjBuf);
nsresult
U2FDecomposeSignResponse(const CryptoBuffer& aResponse,
@ -53,15 +55,20 @@ U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
/* out */ CryptoBuffer& aAttestationCertBuf,
/* out */ CryptoBuffer& aSignatureBuf);
nsresult
ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
uint32_t aLen);
nsresult
U2FDecomposeECKey(const CryptoBuffer& aPubKeyBuf,
/* out */ CryptoBuffer& aXcoord,
/* 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 mozilla

View File

@ -22,11 +22,10 @@ interface nsIU2FTokenManager : nsISupports
* and we have to wait for user input to proceed.
*
* @param aTransactionID : The ID of the transaction to resume.
* @param aPermitDirectAttestation : Whether direct attestation was
* permitted by the user.
* @param aForceNoneAttestation : The user might enforce none attestation.
*/
void resumeRegister(in uint64_t aTransactionID,
in bool aPermitDirectAttestation);
in bool aForceNoneAttestation);
/**
* Cancels the current WebAuthn/U2F transaction if that matches the given

View File

@ -98,8 +98,7 @@
.then(verifyAnonymizedCertificate)
.catch(arrivingHereIsBad);
// Request direct attestation, which should prompt for user intervention,
// once 1430150 lands.
// Request direct attestation, which will prompt for user intervention.
await requestMakeCredential("direct")
.then(verifyDirectCertificate)
.catch(arrivingHereIsBad);

View File

@ -262,11 +262,9 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
}
#[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() {
// Ignore return value.
let _ = (*mgr).cancel();
}
new_tid()
}

View File

@ -78,7 +78,7 @@ uint64_t rust_u2f_mgr_sign(rust_u2f_manager* mgr,
const rust_u2f_app_ids* app_ids,
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.