mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
e6dd50ba10
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
316 lines
8.4 KiB
C++
316 lines
8.4 KiB
C++
/* -*- 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_U2F_h
|
|
#define mozilla_dom_U2F_h
|
|
|
|
#include "js/TypeDecls.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/dom/BindingDeclarations.h"
|
|
#include "mozilla/dom/Nullable.h"
|
|
#include "mozilla/dom/U2FBinding.h"
|
|
#include "mozilla/ErrorResult.h"
|
|
#include "mozilla/MozPromise.h"
|
|
#include "mozilla/ReentrantMonitor.h"
|
|
#include "mozilla/SharedThreadPool.h"
|
|
#include "nsCycleCollectionParticipant.h"
|
|
#include "nsIU2FToken.h"
|
|
#include "nsNSSShutDown.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "nsWrapperCache.h"
|
|
|
|
#include "U2FAuthenticator.h"
|
|
#include "USBToken.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
class U2FRegisterCallback;
|
|
class U2FSignCallback;
|
|
|
|
// Defined in U2FBinding.h by the U2F.webidl; their use requires a JSContext.
|
|
struct RegisterRequest;
|
|
struct RegisteredKey;
|
|
|
|
// These structs are analogs to the WebIDL versions, but can be used on worker
|
|
// threads which lack a JSContext.
|
|
struct LocalRegisterRequest
|
|
{
|
|
nsString mChallenge;
|
|
nsString mVersion;
|
|
CryptoBuffer mClientData;
|
|
};
|
|
|
|
struct LocalRegisteredKey
|
|
{
|
|
nsString mKeyHandle;
|
|
nsString mVersion;
|
|
Nullable<nsString> mAppId;
|
|
// TODO: Support transport preferences
|
|
// Nullable<nsTArray<Transport>> mTransports;
|
|
};
|
|
|
|
typedef MozPromise<nsString, ErrorCode, false> U2FPromise;
|
|
typedef MozPromise<Authenticator, ErrorCode, false> U2FPrepPromise;
|
|
|
|
// U2FPrepTasks return lists of Authenticators that are OK to
|
|
// proceed; they're useful for culling incompatible Authenticators.
|
|
// Currently, only IsRegistered is supported.
|
|
class U2FPrepTask : public Runnable
|
|
{
|
|
public:
|
|
explicit U2FPrepTask(const Authenticator& aAuthenticator);
|
|
|
|
RefPtr<U2FPrepPromise> Execute();
|
|
|
|
protected:
|
|
virtual ~U2FPrepTask();
|
|
|
|
Authenticator mAuthenticator;
|
|
MozPromiseHolder<U2FPrepPromise> mPromise;
|
|
};
|
|
|
|
// Determine whether the provided Authenticator already knows
|
|
// of the provided Registered Key.
|
|
class U2FIsRegisteredTask final : public U2FPrepTask
|
|
{
|
|
public:
|
|
U2FIsRegisteredTask(const Authenticator& aAuthenticator,
|
|
const LocalRegisteredKey& aRegisteredKey);
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
private:
|
|
~U2FIsRegisteredTask();
|
|
|
|
LocalRegisteredKey mRegisteredKey;
|
|
};
|
|
|
|
class U2FTask : public Runnable
|
|
{
|
|
public:
|
|
U2FTask(const nsAString& aOrigin,
|
|
const nsAString& aAppId,
|
|
const Authenticator& aAuthenticator);
|
|
|
|
RefPtr<U2FPromise> Execute();
|
|
|
|
nsString mOrigin;
|
|
nsString mAppId;
|
|
Authenticator mAuthenticator;
|
|
|
|
protected:
|
|
virtual ~U2FTask();
|
|
|
|
MozPromiseHolder<U2FPromise> mPromise;
|
|
};
|
|
|
|
// Use the provided Authenticator to Register a new scoped credential
|
|
// for the provided application.
|
|
class U2FRegisterTask final : public U2FTask
|
|
{
|
|
public:
|
|
U2FRegisterTask(const nsAString& aOrigin,
|
|
const nsAString& aAppId,
|
|
const Authenticator& aAuthenticator,
|
|
const CryptoBuffer& aAppParam,
|
|
const CryptoBuffer& aChallengeParam,
|
|
const LocalRegisterRequest& aRegisterEntry);
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
private:
|
|
~U2FRegisterTask();
|
|
|
|
CryptoBuffer mAppParam;
|
|
CryptoBuffer mChallengeParam;
|
|
LocalRegisterRequest mRegisterEntry;
|
|
};
|
|
|
|
// Generate an assertion using the provided Authenticator for the given origin
|
|
// and provided application to attest to ownership of a valid scoped credential.
|
|
class U2FSignTask final : public U2FTask
|
|
{
|
|
public:
|
|
U2FSignTask(const nsAString& aOrigin,
|
|
const nsAString& aAppId,
|
|
const nsAString& aVersion,
|
|
const Authenticator& aAuthenticator,
|
|
const CryptoBuffer& aAppParam,
|
|
const CryptoBuffer& aChallengeParam,
|
|
const CryptoBuffer& aClientData,
|
|
const CryptoBuffer& aKeyHandle);
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
private:
|
|
~U2FSignTask();
|
|
|
|
nsString mVersion;
|
|
CryptoBuffer mAppParam;
|
|
CryptoBuffer mChallengeParam;
|
|
CryptoBuffer mClientData;
|
|
CryptoBuffer mKeyHandle;
|
|
};
|
|
|
|
// Mediate inter-thread communication for multiple authenticators being queried
|
|
// in concert. Operates as a cyclic buffer with a stop-work method.
|
|
class U2FStatus
|
|
{
|
|
public:
|
|
U2FStatus();
|
|
|
|
void WaitGroupAdd();
|
|
void WaitGroupDone();
|
|
void WaitGroupWait();
|
|
|
|
void Stop(const ErrorCode aErrorCode);
|
|
void Stop(const ErrorCode aErrorCode, const nsAString& aResponse);
|
|
bool IsStopped();
|
|
ErrorCode GetErrorCode();
|
|
nsString GetResponse();
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(U2FStatus)
|
|
|
|
private:
|
|
~U2FStatus();
|
|
|
|
uint16_t mCount;
|
|
bool mIsStopped;
|
|
nsString mResponse;
|
|
MOZ_INIT_OUTSIDE_CTOR ErrorCode mErrorCode;
|
|
ReentrantMonitor mReentrantMonitor;
|
|
};
|
|
|
|
// U2FRunnables run to completion, performing a single U2F operation such as
|
|
// registering, or signing.
|
|
class U2FRunnable : public Runnable
|
|
, public nsNSSShutDownObject
|
|
{
|
|
public:
|
|
U2FRunnable(const nsAString& aOrigin, const nsAString& aAppId);
|
|
|
|
// No NSS resources to release.
|
|
virtual
|
|
void virtualDestroyNSSReference() override {};
|
|
|
|
protected:
|
|
virtual ~U2FRunnable();
|
|
ErrorCode EvaluateAppID();
|
|
|
|
nsString mOrigin;
|
|
nsString mAppId;
|
|
};
|
|
|
|
// This U2FRunnable completes a single application-requested U2F Register
|
|
// operation.
|
|
class U2FRegisterRunnable : public U2FRunnable
|
|
{
|
|
public:
|
|
U2FRegisterRunnable(const nsAString& aOrigin,
|
|
const nsAString& aAppId,
|
|
const Sequence<RegisterRequest>& aRegisterRequests,
|
|
const Sequence<RegisteredKey>& aRegisteredKeys,
|
|
const Sequence<Authenticator>& aAuthenticators,
|
|
U2FRegisterCallback* aCallback);
|
|
|
|
void SendResponse(const RegisterResponse& aResponse);
|
|
void SetTimeout(const int32_t aTimeoutMillis);
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
private:
|
|
~U2FRegisterRunnable();
|
|
|
|
nsTArray<LocalRegisterRequest> mRegisterRequests;
|
|
nsTArray<LocalRegisteredKey> mRegisteredKeys;
|
|
nsTArray<Authenticator> mAuthenticators;
|
|
nsMainThreadPtrHandle<U2FRegisterCallback> mCallback;
|
|
Nullable<int32_t> opt_mTimeoutSeconds;
|
|
};
|
|
|
|
// This U2FRunnable completes a single application-requested U2F Sign operation.
|
|
class U2FSignRunnable : public U2FRunnable
|
|
{
|
|
public:
|
|
U2FSignRunnable(const nsAString& aOrigin,
|
|
const nsAString& aAppId,
|
|
const nsAString& aChallenge,
|
|
const Sequence<RegisteredKey>& aRegisteredKeys,
|
|
const Sequence<Authenticator>& aAuthenticators,
|
|
U2FSignCallback* aCallback);
|
|
|
|
void SendResponse(const SignResponse& aResponse);
|
|
void SetTimeout(const int32_t aTimeoutMillis);
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
private:
|
|
~U2FSignRunnable();
|
|
|
|
nsString mChallenge;
|
|
CryptoBuffer mClientData;
|
|
nsTArray<LocalRegisteredKey> mRegisteredKeys;
|
|
nsTArray<Authenticator> mAuthenticators;
|
|
nsMainThreadPtrHandle<U2FSignCallback> mCallback;
|
|
Nullable<int32_t> opt_mTimeoutSeconds;
|
|
};
|
|
|
|
// The U2F Class is used by the JS engine to initiate U2F operations.
|
|
class U2F final : public nsISupports
|
|
, public nsWrapperCache
|
|
, public nsNSSShutDownObject
|
|
{
|
|
public:
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(U2F)
|
|
|
|
U2F();
|
|
|
|
nsPIDOMWindowInner*
|
|
GetParentObject() const
|
|
{
|
|
return mParent;
|
|
}
|
|
|
|
void
|
|
Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv);
|
|
|
|
virtual JSObject*
|
|
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
|
|
|
void
|
|
Register(const nsAString& aAppId,
|
|
const Sequence<RegisterRequest>& aRegisterRequests,
|
|
const Sequence<RegisteredKey>& aRegisteredKeys,
|
|
U2FRegisterCallback& aCallback,
|
|
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
|
|
ErrorResult& aRv);
|
|
|
|
void
|
|
Sign(const nsAString& aAppId,
|
|
const nsAString& aChallenge,
|
|
const Sequence<RegisteredKey>& aRegisteredKeys,
|
|
U2FSignCallback& aCallback,
|
|
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
|
|
ErrorResult& aRv);
|
|
|
|
// No NSS resources to release.
|
|
virtual
|
|
void virtualDestroyNSSReference() override {};
|
|
|
|
private:
|
|
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
|
nsString mOrigin;
|
|
Sequence<Authenticator> mAuthenticators;
|
|
bool mInitialized;
|
|
|
|
~U2F();
|
|
};
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_dom_U2F_h
|