Bug 1309284 - Implement W3C Web Authentication JS API [part 1] r=keeler,qdot

This patch implements the W3C Web Authentication API from
https://www.w3.org/TR/webauthn/, currently the 28 September 2016
working draft.

It utilizes a tentative binding of the U2F NSS Soft Token to provide
authentication services while waiting on Bug 1245527 to support USB HID-based
U2F tokens. This binding is not in the specification yet, so it should be
considered an experiment to help the specification move fowrard.

There are also a handful of deviations from the specification's WebIDL, which
are annotated with comments in WebAuthentication.webidl.

There are no tests in this commit; they are in Part 4 of this commit series.
There is a small script online at https://webauthn.bin.coffee/ to exercise this
code, but it doesn't do any automated checks.

There are also a handful of TODOS:
1) The algorithm to relax the same-origin restriction is in Part 3.
2) The use of AlgorithmIdentifier and having a way to coerce an object to a
   string is still missing.
3) Timeouts and deadlines aren't there, and are pending reworking how
   the nsIU2FToken interface works.

UPDATED:
- Address qdot, keeler review comments (thanks!)
- Address more qdot, keeler review comments (thanks!)

MozReview-Commit-ID: JITapI38iOh

--HG--
extra : rebase_source : 9a09e852dd0c8dc47f42dabbcf8b845a6828b225
This commit is contained in:
J.C. Jones 2017-01-09 13:22:49 -07:00
parent dd22b188d3
commit e6dd50ba10
25 changed files with 2092 additions and 18 deletions

View File

@ -44,6 +44,7 @@
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h"
#include "mozilla/dom/VRDisplay.h"
#include "mozilla/dom/WebAuthentication.h"
#include "mozilla/dom/workers/RuntimeService.h"
#include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h"
@ -204,6 +205,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAuthentication)
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
#endif
@ -2182,5 +2184,14 @@ Navigator::GetPresentation(ErrorResult& aRv)
return mPresentation;
}
WebAuthentication*
Navigator::Authentication()
{
if (!mAuthentication) {
mAuthentication = new WebAuthentication(GetWindow());
}
return mAuthentication;
}
} // namespace dom
} // namespace mozilla

View File

@ -41,6 +41,7 @@ class ServiceWorkerContainer;
class DOMRequest;
struct FlyWebPublishOptions;
struct FlyWebFilter;
class WebAuthentication;
} // namespace dom
} // namespace mozilla
@ -240,6 +241,8 @@ public:
already_AddRefed<ServiceWorkerContainer> ServiceWorker();
mozilla::dom::WebAuthentication* Authentication();
void GetLanguages(nsTArray<nsString>& aLanguages);
bool MozE10sEnabled();
@ -297,6 +300,7 @@ private:
RefPtr<Promise> mBatteryPromise;
RefPtr<PowerManager> mPowerManager;
RefPtr<network::Connection> mConnection;
RefPtr<WebAuthentication> mAuthentication;
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
RefPtr<system::AudioChannelManager> mAudioChannelManager;
#endif

View File

@ -1062,6 +1062,10 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::TextTrackRegion',
},
'WebAuthentication': {
'implicitJSContext': 'makeCredential',
},
'WindowClient': {
'nativeType': 'mozilla::dom::workers::ServiceWorkerWindowClient',
'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerWindowClient.h',

View File

@ -124,7 +124,7 @@ CryptoBuffer::FromJwkBase64(const nsString& aBase64)
}
nsresult
CryptoBuffer::ToJwkBase64(nsString& aBase64)
CryptoBuffer::ToJwkBase64(nsString& aBase64) const
{
// Shortcut for the empty octet string
if (Length() == 0) {

View File

@ -44,7 +44,7 @@ public:
}
nsresult FromJwkBase64(const nsString& aBase64);
nsresult ToJwkBase64(nsString& aBase64);
nsresult ToJwkBase64(nsString& aBase64) const;
bool ToSECItem(PLArenaPool* aArena, SECItem* aItem) const;
JSObject* ToUint8Array(JSContext* aCx) const;
bool ToNewUnsignedBuffer(uint8_t** aBuf, uint32_t* aBufLen) const;

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ScopedCredential.h"
#include "mozilla/dom/WebAuthenticationBinding.h"
namespace mozilla {
namespace dom {
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ScopedCredential, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ScopedCredential)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScopedCredential)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScopedCredential)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
ScopedCredential::ScopedCredential(WebAuthentication* aParent)
: mParent(aParent)
, mType(ScopedCredentialType::ScopedCred)
{}
ScopedCredential::~ScopedCredential()
{}
JSObject*
ScopedCredential::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ScopedCredentialBinding::Wrap(aCx, this, aGivenProto);
}
ScopedCredentialType
ScopedCredential::Type() const
{
return mType;
}
void
ScopedCredential::GetId(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
aRetVal.set(mIdBuffer.ToUint8Array(aCx));
}
nsresult
ScopedCredential::SetType(ScopedCredentialType aType)
{
mType = aType;
return NS_OK;
}
nsresult
ScopedCredential::SetId(CryptoBuffer& aBuffer)
{
if (!mIdBuffer.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_ScopedCredential_h
#define mozilla_dom_ScopedCredential_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/CryptoBuffer.h"
#include "mozilla/dom/WebAuthenticationBinding.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class ScopedCredential final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScopedCredential)
public:
ScopedCredential(WebAuthentication* aParent);
protected:
~ScopedCredential();
public:
WebAuthentication*
GetParentObject() const
{
return mParent;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
ScopedCredentialType
Type() const;
void
GetId(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
nsresult
SetType(ScopedCredentialType aType);
nsresult
SetId(CryptoBuffer& aBuffer);
private:
RefPtr<WebAuthentication> mParent;
CryptoBuffer mIdBuffer;
ScopedCredentialType mType;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ScopedCredential_h

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ScopedCredentialInfo.h"
#include "mozilla/dom/WebAuthenticationBinding.h"
namespace mozilla {
namespace dom {
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ScopedCredentialInfo, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ScopedCredentialInfo)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScopedCredentialInfo)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScopedCredentialInfo)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
ScopedCredentialInfo::ScopedCredentialInfo(WebAuthentication* aParent)
: mParent(aParent)
{}
ScopedCredentialInfo::~ScopedCredentialInfo()
{}
JSObject*
ScopedCredentialInfo::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return ScopedCredentialInfoBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<ScopedCredential>
ScopedCredentialInfo::Credential() const
{
RefPtr<ScopedCredential> temp(mCredential);
return temp.forget();
}
already_AddRefed<WebAuthnAttestation>
ScopedCredentialInfo::Attestation() const
{
RefPtr<WebAuthnAttestation> temp(mAttestation);
return temp.forget();
}
void
ScopedCredentialInfo::SetCredential(RefPtr<ScopedCredential> aCredential)
{
mCredential = aCredential;
}
void
ScopedCredentialInfo::SetAttestation(RefPtr<WebAuthnAttestation> aAttestation)
{
mAttestation = aAttestation;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,74 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_ScopedCredentialInfo_h
#define mozilla_dom_ScopedCredentialInfo_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class CryptoKey;
class ScopedCredential;
class WebAuthnAttestation;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
class ScopedCredentialInfo final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScopedCredentialInfo)
public:
ScopedCredentialInfo(WebAuthentication* aParent);
protected:
~ScopedCredentialInfo();
public:
WebAuthentication*
GetParentObject() const
{
return mParent;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
already_AddRefed<ScopedCredential>
Credential() const;
already_AddRefed<WebAuthnAttestation>
Attestation() const;
void
SetCredential(RefPtr<ScopedCredential>);
void
SetAttestation(RefPtr<WebAuthnAttestation>);
private:
RefPtr<WebAuthentication> mParent;
RefPtr<WebAuthnAttestation> mAttestation;
RefPtr<ScopedCredential> mCredential;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ScopedCredentialInfo_h

View File

@ -49,7 +49,7 @@ AssembleClientData(const nsAString& aOrigin, const nsAString& aTyp,
const nsAString& aChallenge, CryptoBuffer& aClientData)
{
MOZ_ASSERT(NS_IsMainThread());
ClientData clientDataObject;
U2FClientData clientDataObject;
clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
clientDataObject.mChallenge.Construct(aChallenge);
clientDataObject.mOrigin.Construct(aOrigin);

View File

@ -23,6 +23,7 @@
#include "nsProxyRelease.h"
#include "nsWrapperCache.h"
#include "U2FAuthenticator.h"
#include "USBToken.h"
namespace mozilla {
@ -53,19 +54,6 @@ struct LocalRegisteredKey
// Nullable<nsTArray<Transport>> mTransports;
};
// These enumerations are defined in the FIDO U2F Javascript API under the
// interface "ErrorCode" as constant integers, and thus in the U2F.webidl file.
// Any changes to these must occur in both locations.
enum class ErrorCode {
OK = 0,
OTHER_ERROR = 1,
BAD_REQUEST = 2,
CONFIGURATION_UNSUPPORTED = 3,
DEVICE_INELIGIBLE = 4,
TIMEOUT = 5
};
typedef nsCOMPtr<nsIU2FToken> Authenticator;
typedef MozPromise<nsString, ErrorCode, false> U2FPromise;
typedef MozPromise<Authenticator, ErrorCode, false> U2FPrepPromise;

View File

@ -0,0 +1,33 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_U2FAuthenticator_h
#define mozilla_dom_U2FAuthenticator_h
#include "nsIU2FToken.h"
#include "USBToken.h"
namespace mozilla {
namespace dom {
// These enumerations are defined in the FIDO U2F Javascript API under the
// interface "ErrorCode" as constant integers, and thus in the U2F.webidl file.
// Any changes to these must occur in both locations.
enum class ErrorCode {
OK = 0,
OTHER_ERROR = 1,
BAD_REQUEST = 2,
CONFIGURATION_UNSUPPORTED = 3,
DEVICE_INELIGIBLE = 4,
TIMEOUT = 5
};
typedef nsCOMPtr<nsIU2FToken> Authenticator;
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_U2FAuthenticator_h

File diff suppressed because it is too large Load Diff

110
dom/u2f/WebAuthentication.h Normal file
View File

@ -0,0 +1,110 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_WebAuthentication_h
#define mozilla_dom_WebAuthentication_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/WebAuthenticationBinding.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/MozPromise.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/SharedThreadPool.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "U2FAuthenticator.h"
#include "WebAuthnRequest.h"
namespace mozilla {
namespace dom {
struct Account;
class ArrayBufferViewOrArrayBuffer;
struct AssertionOptions;
class OwningArrayBufferViewOrArrayBuffer;
struct ScopedCredentialOptions;
struct ScopedCredentialParameters;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
typedef RefPtr<ScopedCredentialInfo> CredentialPtr;
typedef RefPtr<WebAuthnAssertion> AssertionPtr;
typedef WebAuthnRequest<CredentialPtr> CredentialRequest;
typedef WebAuthnRequest<AssertionPtr> AssertionRequest;
typedef MozPromise<CredentialPtr, nsresult, false> CredentialPromise;
typedef MozPromise<AssertionPtr, nsresult, false> AssertionPromise;
class WebAuthentication final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
public:
WebAuthentication(nsPIDOMWindowInner* aParent);
protected:
~WebAuthentication();
public:
nsPIDOMWindowInner*
GetParentObject() const
{
return mParent;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
already_AddRefed<Promise>
MakeCredential(JSContext* aCx, const Account& accountInformation,
const Sequence<ScopedCredentialParameters>& cryptoParameters,
const ArrayBufferViewOrArrayBuffer& attestationChallenge,
const ScopedCredentialOptions& options);
already_AddRefed<Promise>
GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
const AssertionOptions& options);
private:
nsresult
InitLazily();
void
U2FAuthMakeCredential(const RefPtr<CredentialRequest>& aRequest,
const Authenticator& aToken, CryptoBuffer& aRpIdHash,
const nsACString& aClientData, CryptoBuffer& aClientDataHash,
const Account& aAccount,
const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
const WebAuthnExtensions& aExtensions);
void
U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
const Authenticator& aToken, CryptoBuffer& aRpIdHash,
const nsACString& aClientData, CryptoBuffer& aClientDataHash,
nsTArray<CryptoBuffer>& aAllowList,
const WebAuthnExtensions& aExtensions);
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsString mOrigin;
Sequence<Authenticator> mAuthenticators;
bool mInitialized;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_WebAuthentication_h

View File

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/WebAuthenticationBinding.h"
#include "mozilla/dom/WebAuthnAssertion.h"
namespace mozilla {
namespace dom {
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthnAssertion, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthnAssertion)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthnAssertion)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthnAssertion)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
WebAuthnAssertion::WebAuthnAssertion(WebAuthentication* aParent)
: mParent(aParent)
{}
WebAuthnAssertion::~WebAuthnAssertion()
{}
JSObject*
WebAuthnAssertion::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return WebAuthnAssertionBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<ScopedCredential>
WebAuthnAssertion::Credential() const
{
RefPtr<ScopedCredential> temp(mCredential);
return temp.forget();
}
void
WebAuthnAssertion::GetClientData(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
aRetVal.set(mClientData.ToUint8Array(aCx));
}
void
WebAuthnAssertion::GetAuthenticatorData(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
}
void
WebAuthnAssertion::GetSignature(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
aRetVal.set(mSignature.ToUint8Array(aCx));
}
nsresult
WebAuthnAssertion::SetCredential(RefPtr<ScopedCredential> aCredential)
{
mCredential = aCredential;
return NS_OK;
}
nsresult
WebAuthnAssertion::SetClientData(CryptoBuffer& aBuffer)
{
if (!mClientData.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
WebAuthnAssertion::SetAuthenticatorData(CryptoBuffer& aBuffer)
{
if (!mAuthenticatorData.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
WebAuthnAssertion::SetSignature(CryptoBuffer& aBuffer)
{
if (!mSignature.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_WebAuthnAssertion_h
#define mozilla_dom_WebAuthnAssertion_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class ScopedCredential;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
class WebAuthnAssertion final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthnAssertion)
public:
WebAuthnAssertion(WebAuthentication* aParent);
protected:
~WebAuthnAssertion();
public:
WebAuthentication*
GetParentObject() const
{
return mParent;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
already_AddRefed<ScopedCredential>
Credential() const;
void
GetClientData(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
void
GetAuthenticatorData(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
void
GetSignature(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
nsresult
SetCredential(RefPtr<ScopedCredential> aCredential);
nsresult
SetClientData(CryptoBuffer& aBuffer);
nsresult
SetAuthenticatorData(CryptoBuffer& aBuffer);
nsresult
SetSignature(CryptoBuffer& aBuffer);
private:
RefPtr<WebAuthentication> mParent;
RefPtr<ScopedCredential> mCredential;
CryptoBuffer mAuthenticatorData;
CryptoBuffer mClientData;
CryptoBuffer mSignature;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_WebAuthnAssertion_h

View File

@ -0,0 +1,100 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/WebAuthenticationBinding.h"
#include "mozilla/dom/WebAuthnAttestation.h"
namespace mozilla {
namespace dom {
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthnAttestation, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthnAttestation)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthnAttestation)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthnAttestation)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
WebAuthnAttestation::WebAuthnAttestation(WebAuthentication* aParent)
: mParent(aParent)
{}
WebAuthnAttestation::~WebAuthnAttestation()
{}
JSObject*
WebAuthnAttestation::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return WebAuthnAttestationBinding::Wrap(aCx, this, aGivenProto);
}
void
WebAuthnAttestation::GetFormat(nsString& aRetVal) const
{
aRetVal = mFormat;
}
void
WebAuthnAttestation::GetClientData(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
aRetVal.set(mClientData.ToUint8Array(aCx));
}
void
WebAuthnAttestation::GetAuthenticatorData(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetVal) const
{
aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
}
void
WebAuthnAttestation::GetAttestation(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetVal) const
{
// JS::RootedObject obj(aCx);
// obj.set();
aRetVal.setObject(*mAttestation.ToUint8Array(aCx));
}
nsresult
WebAuthnAttestation::SetFormat(nsString aFormat)
{
mFormat = aFormat;
return NS_OK;
}
nsresult
WebAuthnAttestation::SetClientData(CryptoBuffer& aBuffer)
{
if (!mClientData.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
WebAuthnAttestation::SetAuthenticatorData(CryptoBuffer& aBuffer)
{
if (!mAuthenticatorData.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
WebAuthnAttestation::SetAttestation(CryptoBuffer& aBuffer)
{
if (!mAttestation.Assign(aBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_WebAuthnAttestation_h
#define mozilla_dom_WebAuthnAttestation_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/CryptoBuffer.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class WebAuthnAttestation final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthnAttestation)
public:
WebAuthnAttestation(WebAuthentication* aParent);
protected:
~WebAuthnAttestation();
public:
WebAuthentication*
GetParentObject() const
{
return mParent;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
GetFormat(nsString& aRetVal) const;
void
GetClientData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
void
GetAuthenticatorData(JSContext* caCxx, JS::MutableHandle<JSObject*> aRetVal) const;
void
GetAttestation(JSContext* aCx, JS::MutableHandle<JS::Value> aRetVal) const;
nsresult
SetFormat(nsString aFormat);
nsresult
SetClientData(CryptoBuffer& aBuffer);
nsresult
SetAuthenticatorData(CryptoBuffer& aBuffer);
nsresult
SetAttestation(CryptoBuffer& aBuffer);
private:
RefPtr<WebAuthentication> mParent;
nsString mFormat;
CryptoBuffer mClientData;
CryptoBuffer mAuthenticatorData;
CryptoBuffer mAttestation;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_WebAuthnAttestation_h

137
dom/u2f/WebAuthnRequest.h Normal file
View File

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_WebAuthnAsync_h
#define mozilla_dom_WebAuthnAsync_h
#include "mozilla/MozPromise.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/TimeStamp.h"
namespace mozilla {
namespace dom {
extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
// WebAuthnRequest tracks the completion of a single WebAuthn request that
// may run on multiple kinds of authenticators, and be subject to a deadline.
template<class Success>
class WebAuthnRequest {
public:
WebAuthnRequest()
: mCancelled(false)
, mSuccess(false)
, mCountTokens(0)
, mTokensFailed(0)
, mReentrantMonitor("WebAuthnRequest")
{}
void AddActiveToken(const char* aCallSite)
{
MOZ_LOG(gWebauthLog, LogLevel::Debug,
("WebAuthnRequest is tracking a new token, called from [%s]",
aCallSite));
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
MOZ_ASSERT(!IsComplete());
mCountTokens += 1;
}
bool IsComplete()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mCancelled || mSuccess ||
(mCountTokens > 0 && mTokensFailed == mCountTokens);
}
void CancelNow()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// It's possible for a race to cause CancelNow to get called after
// a success or a cancel. We only complete once.
if (IsComplete()) {
return;
}
mCancelled = true;
mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
}
void SetFailure(nsresult aError)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// It's possible for a race to cause SetFailure to get called after
// a success or a cancel. We only complete once.
if (IsComplete()) {
return;
}
mTokensFailed += 1;
MOZ_ASSERT(mTokensFailed <= mCountTokens);
if (mTokensFailed == mCountTokens) {
// Provide the final error as being indicitive of the whole set.
mPromise.Reject(aError, __func__);
}
}
void SetSuccess(const Success& aResult)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// It's possible for a race to cause multiple calls to SetSuccess
// in succession. We will only select the earliest.
if (IsComplete()) {
return;
}
mSuccess = true;
mPromise.Resolve(aResult, __func__);
}
void SetDeadline(TimeDuration aDeadline)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
MOZ_ASSERT(!IsComplete());
// TODO: Monitor the deadline and stop with a timeout error if it expires.
}
RefPtr<MozPromise<Success, nsresult, false>> Ensure()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
MOZ_ASSERT(!IsComplete());
return mPromise.Ensure(__func__);
}
void CompleteTask()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (mCountTokens == 0) {
// Special case for there being no tasks to complete
mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
}
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebAuthnRequest)
private:
~WebAuthnRequest() {};
bool mCancelled;
bool mSuccess;
int mCountTokens;
int mTokensFailed;
ReentrantMonitor mReentrantMonitor;
MozPromiseHolder<MozPromise<Success, nsresult, false>> mPromise;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_WebAuthnAsync_h

View File

@ -6,14 +6,26 @@
EXPORTS.mozilla.dom += [
'NSSU2FTokenRemote.h',
'ScopedCredential.h',
'ScopedCredentialInfo.h',
'U2F.h',
'U2FAuthenticator.h',
'USBToken.h',
'WebAuthentication.h',
'WebAuthnAssertion.h',
'WebAuthnAttestation.h',
'WebAuthnRequest.h',
]
UNIFIED_SOURCES += [
'NSSU2FTokenRemote.cpp',
'ScopedCredential.cpp',
'ScopedCredentialInfo.cpp',
'U2F.cpp',
'USBToken.cpp',
'WebAuthentication.cpp',
'WebAuthnAssertion.cpp',
'WebAuthnAttestation.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
@ -25,6 +37,7 @@ LOCAL_INCLUDES += [
'/dom/crypto',
'/security/manager/ssl',
'/security/pkix/include',
'/security/pkix/lib',
]
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']

View File

@ -380,3 +380,8 @@ partial interface Navigator {
interface NavigatorConcurrentHardware {
readonly attribute unsigned long long hardwareConcurrency;
};
partial interface Navigator {
[Pref="security.webauth.w3c", SameObject]
readonly attribute WebAuthentication authentication;
};

View File

@ -26,7 +26,7 @@ enum Transport {
"usb"
};
dictionary ClientData {
dictionary U2FClientData {
DOMString typ; // Spelling is from the specification
DOMString challenge;
DOMString origin;

View File

@ -0,0 +1,113 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://www.w3.org/TR/webauthn/
*/
/***** Interfaces to Data *****/
[SecureContext]
interface ScopedCredentialInfo {
readonly attribute ScopedCredential credential;
readonly attribute WebAuthnAttestation attestation;
};
dictionary Account {
required DOMString rpDisplayName;
required DOMString displayName;
required DOMString id;
DOMString name;
DOMString imageURL;
};
typedef (boolean or DOMString) WebAuthnAlgorithmID; // Fix when upstream there's a definition of how to serialize AlgorithmIdentifier
dictionary ScopedCredentialParameters {
required ScopedCredentialType type;
required WebAuthnAlgorithmID algorithm; // NOTE: changed from AllgorithmIdentifier because typedef (object or DOMString) not serializable
};
dictionary ScopedCredentialOptions {
unsigned long timeoutSeconds;
USVString rpId;
sequence<ScopedCredentialDescriptor> excludeList;
WebAuthnExtensions extensions;
};
[SecureContext]
interface WebAuthnAssertion {
readonly attribute ScopedCredential credential;
readonly attribute ArrayBuffer clientData;
readonly attribute ArrayBuffer authenticatorData;
readonly attribute ArrayBuffer signature;
};
dictionary AssertionOptions {
unsigned long timeoutSeconds;
USVString rpId;
sequence<ScopedCredentialDescriptor> allowList;
WebAuthnExtensions extensions;
};
dictionary WebAuthnExtensions {
};
[SecureContext]
interface WebAuthnAttestation {
readonly attribute USVString format;
readonly attribute ArrayBuffer clientData;
readonly attribute ArrayBuffer authenticatorData;
readonly attribute any attestation;
};
// Renamed from "ClientData" to avoid a collision with U2F
dictionary WebAuthnClientData {
required DOMString challenge;
required DOMString origin;
required WebAuthnAlgorithmID hashAlg; // NOTE: changed from AllgorithmIdentifier because typedef (object or DOMString) not serializable
DOMString tokenBinding;
WebAuthnExtensions extensions;
};
enum ScopedCredentialType {
"ScopedCred"
};
[SecureContext]
interface ScopedCredential {
readonly attribute ScopedCredentialType type;
readonly attribute ArrayBuffer id;
};
dictionary ScopedCredentialDescriptor {
required ScopedCredentialType type;
required BufferSource id;
sequence <WebAuthnTransport> transports;
};
// Renamed from "Transport" to avoid a collision with U2F
enum WebAuthnTransport {
"usb",
"nfc",
"ble"
};
/***** The Main API *****/
[SecureContext]
interface WebAuthentication {
Promise<ScopedCredentialInfo> makeCredential (
Account accountInformation,
sequence<ScopedCredentialParameters> cryptoParameters,
BufferSource attestationChallenge,
optional ScopedCredentialOptions options
);
Promise<WebAuthnAssertion> getAssertion (
BufferSource assertionChallenge,
optional AssertionOptions options
);
};

View File

@ -562,6 +562,7 @@ WEBIDL_FILES = [
'VTTCue.webidl',
'VTTRegion.webidl',
'WaveShaperNode.webidl',
'WebAuthentication.webidl',
'WebComponents.webidl',
'WebGL2RenderingContext.webidl',
'WebGLRenderingContext.webidl',

View File

@ -93,6 +93,7 @@ pref("security.pki.certificate_transparency.mode", 1);
pref("security.webauth.u2f", false);
pref("security.webauth.u2f_enable_softtoken", false);
pref("security.webauth.u2f_enable_usbtoken", false);
pref("security.webauth.w3c", false);
pref("security.ssl.errorReporting.enabled", true);
pref("security.ssl.errorReporting.url", "https://incoming.telemetry.mozilla.org/submit/sslreports/");