Skip to content

Commit e0c24a5

Browse files
committed
Bug 1323339 - Add WebAuthnManager and support IPC Child classes; r=jcj r=baku
Takes functionality once in the WebAuthentication DOM class that needs to be handled by the content process, and moves it to a singleton (per-content-process) manager class. This allows the WebAuthn API to centralize management of transactions and IPC channels. Patch also creates the child (content-process) classes for WebAuthn IPC channels. MozReview-Commit-ID: 6ju2LK8lvNR
1 parent b0260e8 commit e0c24a5

File tree

9 files changed

+1229
-0
lines changed

9 files changed

+1229
-0
lines changed

dom/webauthn/WebAuthnManager.cpp

Lines changed: 801 additions & 0 deletions
Large diffs are not rendered by default.

dom/webauthn/WebAuthnManager.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2+
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3+
/* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6+
7+
#ifndef mozilla_dom_WebAuthnManager_h
8+
#define mozilla_dom_WebAuthnManager_h
9+
10+
#include "nsIIPCBackgroundChildCreateCallback.h"
11+
#include "mozilla/MozPromise.h"
12+
#include "mozilla/dom/PWebAuthnTransaction.h"
13+
14+
/*
15+
* Content process manager for the WebAuthn protocol. Created on calls to the
16+
* WebAuthentication DOM object, this manager handles establishing IPC channels
17+
* for WebAuthn transactions, as well as keeping track of JS Promise objects
18+
* representing transactions in flight.
19+
*
20+
* The WebAuthn spec (https://www.w3.org/TR/webauthn/) allows for two different
21+
* types of transactions: registration and signing. When either of these is
22+
* requested via the DOM API, the following steps are executed in the
23+
* WebAuthnManager:
24+
*
25+
* - Validation of the request. Return a failed promise to js if request does
26+
* not have correct parameters.
27+
*
28+
* - If request is valid, open a new IPC channel for running the transaction. If
29+
* another transaction is already running in this content process, cancel it.
30+
* Return a pending promise to js.
31+
*
32+
* - Send transaction information to parent process (by running the Start*
33+
* functions of WebAuthnManager). Assuming another transaction is currently in
34+
* flight in another content process, parent will handle canceling it.
35+
*
36+
* - On return of successful transaction information from parent process, turn
37+
* information into DOM object format required by spec, and resolve promise
38+
* (by running the Finish* functions of WebAuthnManager). On cancellation
39+
* request from parent, reject promise with corresponding error code. Either
40+
* outcome will also close the IPC channel.
41+
*
42+
*/
43+
44+
namespace mozilla {
45+
namespace dom {
46+
47+
struct Account;
48+
class ArrayBufferViewOrArrayBuffer;
49+
struct AssertionOptions;
50+
class OwningArrayBufferViewOrArrayBuffer;
51+
struct ScopedCredentialOptions;
52+
struct ScopedCredentialParameters;
53+
class Promise;
54+
class WebAuthnTransactionChild;
55+
class WebAuthnTransactionInfo;
56+
57+
class WebAuthnManager final : public nsIIPCBackgroundChildCreateCallback
58+
{
59+
public:
60+
NS_DECL_ISUPPORTS
61+
static WebAuthnManager* GetOrCreate();
62+
static WebAuthnManager* Get();
63+
64+
void
65+
FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer,
66+
nsTArray<uint8_t>& aSigBuffer);
67+
68+
void
69+
FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
70+
nsTArray<uint8_t>& aSigBuffer);
71+
72+
void
73+
Cancel(const nsresult& aError);
74+
75+
already_AddRefed<Promise>
76+
MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
77+
const Account& aAccountInformation,
78+
const Sequence<ScopedCredentialParameters>& aCryptoParameters,
79+
const ArrayBufferViewOrArrayBuffer& aAttestationChallenge,
80+
const ScopedCredentialOptions& aOptions);
81+
82+
already_AddRefed<Promise>
83+
GetAssertion(nsPIDOMWindowInner* aParent,
84+
const ArrayBufferViewOrArrayBuffer& aAssertionChallenge,
85+
const AssertionOptions& aOptions);
86+
87+
void StartRegister();
88+
void StartSign();
89+
90+
// nsIIPCbackgroundChildCreateCallback methods
91+
void ActorCreated(PBackgroundChild* aActor) override;
92+
void ActorFailed() override;
93+
void ActorDestroyed();
94+
private:
95+
WebAuthnManager();
96+
virtual ~WebAuthnManager();
97+
98+
void MaybeClearTransaction();
99+
100+
already_AddRefed<MozPromise<nsresult, nsresult, false>>
101+
GetOrCreateBackgroundActor();
102+
103+
// JS Promise representing transaction status.
104+
RefPtr<Promise> mTransactionPromise;
105+
106+
// IPC Channel for the current transaction.
107+
RefPtr<WebAuthnTransactionChild> mChild;
108+
109+
// Parent of the context we're currently running the transaction in.
110+
nsCOMPtr<nsPIDOMWindowInner> mCurrentParent;
111+
112+
// Client data, stored on successful transaction creation, so that it can be
113+
// used to assemble reply objects.
114+
Maybe<nsCString> mClientData;
115+
116+
// Holds the parameters of the current transaction, as we need them both
117+
// before the transaction request is sent, and on successful return.
118+
Maybe<WebAuthnTransactionInfo> mInfo;
119+
120+
// Promise for dealing with PBackground Actor creation.
121+
MozPromiseHolder<MozPromise<nsresult, nsresult, false>> mPBackgroundCreationPromise;
122+
};
123+
124+
} // namespace dom
125+
} // namespace mozilla
126+
127+
#endif // mozilla_dom_WebAuthnManager_h
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2+
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3+
/* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6+
7+
#include "mozilla/dom/WebAuthnTransactionChild.h"
8+
#include "mozilla/dom/WebAuthnManager.h"
9+
10+
namespace mozilla {
11+
namespace dom {
12+
13+
WebAuthnTransactionChild::WebAuthnTransactionChild()
14+
{
15+
// Retain a reference so the task object isn't deleted without IPDL's
16+
// knowledge. The reference will be released by
17+
// mozilla::ipc::BackgroundChildImpl::DeallocPWebAuthnTransactionChild.
18+
NS_ADDREF_THIS();
19+
}
20+
21+
mozilla::ipc::IPCResult
22+
WebAuthnTransactionChild::RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer,
23+
nsTArray<uint8_t>&& aSigBuffer)
24+
{
25+
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
26+
MOZ_ASSERT(mgr);
27+
mgr->FinishMakeCredential(aRegBuffer, aSigBuffer);
28+
return IPC_OK();
29+
}
30+
31+
mozilla::ipc::IPCResult
32+
WebAuthnTransactionChild::RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
33+
nsTArray<uint8_t>&& aBuffer)
34+
{
35+
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
36+
MOZ_ASSERT(mgr);
37+
mgr->FinishGetAssertion(aCredentialId, aBuffer);
38+
return IPC_OK();
39+
}
40+
41+
mozilla::ipc::IPCResult
42+
WebAuthnTransactionChild::RecvCancel(const nsresult& aError)
43+
{
44+
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
45+
MOZ_ASSERT(mgr);
46+
mgr->Cancel(aError);
47+
return IPC_OK();
48+
}
49+
50+
void
51+
WebAuthnTransactionChild::ActorDestroy(ActorDestroyReason why)
52+
{
53+
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
54+
// This could happen after the WebAuthnManager has been shut down.
55+
if (mgr) {
56+
mgr->ActorDestroyed();
57+
}
58+
}
59+
60+
}
61+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2+
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3+
/* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6+
7+
#ifndef mozilla_dom_WebAuthnTransactionChild_h
8+
#define mozilla_dom_WebAuthnTransactionChild_h
9+
10+
#include "mozilla/dom/PWebAuthnTransactionChild.h"
11+
12+
/*
13+
* Child process IPC implementation for WebAuthn API. Receives results of
14+
* WebAuthn transactions from the parent process, and sends them to the
15+
* WebAuthnManager either cancel the transaction, or be formatted and relayed to
16+
* content.
17+
*/
18+
19+
namespace mozilla {
20+
namespace dom {
21+
22+
class WebAuthnTransactionChild final : public PWebAuthnTransactionChild
23+
{
24+
public:
25+
NS_INLINE_DECL_REFCOUNTING(WebAuthnTransactionChild);
26+
WebAuthnTransactionChild();
27+
mozilla::ipc::IPCResult RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer,
28+
nsTArray<uint8_t>&& aSigBuffer) override;
29+
mozilla::ipc::IPCResult RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
30+
nsTArray<uint8_t>&& aBuffer) override;
31+
mozilla::ipc::IPCResult RecvCancel(const nsresult& aError) override;
32+
void ActorDestroy(ActorDestroyReason why) override;
33+
private:
34+
~WebAuthnTransactionChild() = default;
35+
};
36+
37+
}
38+
}
39+
40+
#endif //mozilla_dom_WebAuthnTransactionChild_h

dom/webauthn/WebAuthnUtil.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2+
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3+
/* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
5+
* You can obtain one at http://mozilla.org/MPL/2.0/. */
6+
7+
#include "mozilla/dom/WebAuthnUtil.h"
8+
#include "pkixutil.h"
9+
10+
namespace mozilla {
11+
namespace dom {
12+
13+
nsresult
14+
ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
15+
uint32_t aLen)
16+
{
17+
if (aSrc.EnsureLength(aLen) != pkix::Success) {
18+
return NS_ERROR_DOM_UNKNOWN_ERR;
19+
}
20+
21+
aDest.ClearAndRetainStorage();
22+
23+
for (uint32_t offset = 0; offset < aLen; ++offset) {
24+
uint8_t b;
25+
if (aSrc.Read(b) != pkix::Success) {
26+
return NS_ERROR_DOM_UNKNOWN_ERR;
27+
}
28+
if (!aDest.AppendElement(b, mozilla::fallible)) {
29+
return NS_ERROR_OUT_OF_MEMORY;
30+
}
31+
}
32+
33+
return NS_OK;
34+
}
35+
36+
nsresult
37+
U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
38+
const CryptoBuffer& aRpIdHash,
39+
const CryptoBuffer& aSignatureData)
40+
{
41+
// The AuthenticatorData for U2F devices is the concatenation of the
42+
// RP ID with the output of the U2F Sign operation.
43+
if (aRpIdHash.Length() != 32) {
44+
return NS_ERROR_INVALID_ARG;
45+
}
46+
47+
if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
48+
return NS_ERROR_OUT_OF_MEMORY;
49+
}
50+
51+
if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
52+
return NS_ERROR_OUT_OF_MEMORY;
53+
}
54+
55+
return NS_OK;
56+
}
57+
58+
nsresult
59+
U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
60+
/* out */ CryptoBuffer& aPubKeyBuf,
61+
/* out */ CryptoBuffer& aKeyHandleBuf,
62+
/* out */ CryptoBuffer& aAttestationCertBuf,
63+
/* out */ CryptoBuffer& aSignatureBuf)
64+
{
65+
// U2F v1.1 Format via
66+
// http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
67+
//
68+
// Bytes Value
69+
// 1 0x05
70+
// 65 public key
71+
// 1 key handle length
72+
// * key handle
73+
// ASN.1 attestation certificate
74+
// * attestation signature
75+
76+
pkix::Input u2fResponse;
77+
u2fResponse.Init(aResponse.Elements(), aResponse.Length());
78+
79+
pkix::Reader input(u2fResponse);
80+
81+
uint8_t b;
82+
if (input.Read(b) != pkix::Success) {
83+
return NS_ERROR_DOM_UNKNOWN_ERR;
84+
}
85+
if (b != 0x05) {
86+
return NS_ERROR_DOM_UNKNOWN_ERR;
87+
}
88+
89+
nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
90+
if (NS_FAILED(rv)) {
91+
return rv;
92+
}
93+
94+
uint8_t handleLen;
95+
if (input.Read(handleLen) != pkix::Success) {
96+
return NS_ERROR_DOM_UNKNOWN_ERR;
97+
}
98+
99+
rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
100+
if (NS_FAILED(rv)) {
101+
return rv;
102+
}
103+
104+
// We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
105+
// length.
106+
pkix::Input cert;
107+
if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
108+
!= pkix::Success) {
109+
return NS_ERROR_DOM_UNKNOWN_ERR;
110+
}
111+
112+
pkix::Reader certInput(cert);
113+
rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
114+
if (NS_FAILED(rv)) {
115+
return rv;
116+
}
117+
118+
// The remainder of u2fResponse is the signature
119+
pkix::Input u2fSig;
120+
input.SkipToEnd(u2fSig);
121+
pkix::Reader sigInput(u2fSig);
122+
rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
123+
if (NS_FAILED(rv)) {
124+
return rv;
125+
}
126+
127+
return NS_OK;
128+
}
129+
130+
}
131+
}

0 commit comments

Comments
 (0)