mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
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:
parent
e09ad9e15c
commit
2a252e45a4
@ -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);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
145
dom/u2f/U2F.cpp
145
dom/u2f/U2F.cpp
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_U2FUtil_h
|
||||
#define mozilla_dom_U2FUtil_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static nsresult
|
||||
HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
|
||||
/* out */ CryptoBuffer& aOut)
|
||||
{
|
||||
MOZ_ASSERT(aHashService);
|
||||
|
||||
nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = aHashService->Update(
|
||||
reinterpret_cast<const uint8_t*>(aIn.BeginReading()), aIn.Length());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString fullHash;
|
||||
// Passing false below means we will get a binary result rather than a
|
||||
// base64-encoded string.
|
||||
rv = aHashService->Finish(false, fullHash);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aOut.Assign(fullHash);
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_U2FUtil_h
|
||||
|
@ -10,7 +10,6 @@ with Files("**"):
|
||||
EXPORTS.mozilla.dom += [
|
||||
'U2F.h',
|
||||
'U2FAuthenticator.h',
|
||||
'U2FUtil.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -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 {
|
||||
|
@ -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__);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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__);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user