Bug 1275479 - Refactor U2F Token Interface (Part 1). r=keeler

Rework U2F.cpp to use a collection of nsINSSU2FToken for U2F/WebAuth operations.

MozReview-Commit-ID: 9qwllawzOWh

--HG--
extra : transplant_source : %E1%7B%15%AEp%8C%1A%3C%E5%9F%13%D1%B3%1D%BB%C2%88%07%0AX
This commit is contained in:
J.C. Jones 2016-05-31 20:51:24 -07:00
Родитель 0408aec495
Коммит a253e31ba2
5 изменённых файлов: 261 добавлений и 154 удалений

Просмотреть файл

@ -0,0 +1,156 @@
/* -*- 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/. */
#include "mozilla/dom/ContentChild.h"
#include "NSSU2FTokenRemote.h"
using mozilla::dom::ContentChild;
NS_IMPL_ISUPPORTS(NSSU2FTokenRemote, nsINSSU2FToken)
static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
NSSU2FTokenRemote::NSSU2FTokenRemote()
{}
NSSU2FTokenRemote::~NSSU2FTokenRemote()
{}
NS_IMETHODIMP
NSSU2FTokenRemote::Init()
{
return NS_OK;
}
NS_IMETHODIMP
NSSU2FTokenRemote::IsCompatibleVersion(const nsAString& aVersionString,
bool* aIsCompatible)
{
NS_ENSURE_ARG_POINTER(aIsCompatible);
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenIsCompatibleVersion(
nsString(aVersionString), aIsCompatible)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
NSSU2FTokenRemote::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
bool* aIsRegistered)
{
NS_ENSURE_ARG_POINTER(aKeyHandle);
NS_ENSURE_ARG_POINTER(aIsRegistered);
nsTArray<uint8_t> keyHandle;
if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
aKeyHandleLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenIsRegistered(keyHandle, aIsRegistered)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
NSSU2FTokenRemote::Register(uint8_t* aApplication,
uint32_t aApplicationLen,
uint8_t* aChallenge,
uint32_t aChallengeLen,
uint8_t** aRegistration,
uint32_t* aRegistrationLen)
{
NS_ENSURE_ARG_POINTER(aApplication);
NS_ENSURE_ARG_POINTER(aChallenge);
NS_ENSURE_ARG_POINTER(aRegistration);
NS_ENSURE_ARG_POINTER(aRegistrationLen);
nsTArray<uint8_t> application;
if (!application.ReplaceElementsAt(0, application.Length(), aApplication,
aApplicationLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsTArray<uint8_t> challenge;
if (!challenge.ReplaceElementsAt(0, challenge.Length(), aChallenge,
aChallengeLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsTArray<uint8_t> registrationBuffer;
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenRegister(application, challenge,
&registrationBuffer)) {
return NS_ERROR_FAILURE;
}
size_t dataLen = registrationBuffer.Length();
uint8_t* tmp = reinterpret_cast<uint8_t*>(moz_xmalloc(dataLen));
if (NS_WARN_IF(!tmp)) {
return NS_ERROR_OUT_OF_MEMORY;
}
memcpy(tmp, registrationBuffer.Elements(), dataLen);
*aRegistration = tmp;
*aRegistrationLen = dataLen;
return NS_OK;
}
NS_IMETHODIMP
NSSU2FTokenRemote::Sign(uint8_t* aApplication, uint32_t aApplicationLen,
uint8_t* aChallenge, uint32_t aChallengeLen,
uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
uint8_t** aSignature, uint32_t* aSignatureLen)
{
NS_ENSURE_ARG_POINTER(aApplication);
NS_ENSURE_ARG_POINTER(aChallenge);
NS_ENSURE_ARG_POINTER(aKeyHandle);
NS_ENSURE_ARG_POINTER(aSignature);
NS_ENSURE_ARG_POINTER(aSignatureLen);
nsTArray<uint8_t> application;
if (!application.ReplaceElementsAt(0, application.Length(), aApplication,
aApplicationLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsTArray<uint8_t> challenge;
if (!challenge.ReplaceElementsAt(0, challenge.Length(), aChallenge,
aChallengeLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsTArray<uint8_t> keyHandle;
if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
aKeyHandleLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsTArray<uint8_t> signatureBuffer;
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenSign(application, challenge, keyHandle,
&signatureBuffer)) {
return NS_ERROR_FAILURE;
}
size_t dataLen = signatureBuffer.Length();
uint8_t* tmp = reinterpret_cast<uint8_t*>(moz_xmalloc(dataLen));
if (NS_WARN_IF(!tmp)) {
return NS_ERROR_OUT_OF_MEMORY;
}
memcpy(tmp, signatureBuffer.Elements(), dataLen);
*aSignature = tmp;
*aSignatureLen = dataLen;
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,24 @@
/* -*- 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 NSSU2FTokenRemote_h
#define NSSU2FTokenRemote_h
#include "nsINSSU2FToken.h"
class NSSU2FTokenRemote : public nsINSSU2FToken
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINSSU2FTOKEN
NSSU2FTokenRemote();
private:
virtual ~NSSU2FTokenRemote();
};
#endif // NSSU2FTokenRemote_h

Просмотреть файл

@ -7,6 +7,7 @@
#include "hasht.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/CryptoBuffer.h"
#include "mozilla/dom/NSSU2FTokenRemote.h"
#include "mozilla/dom/U2F.h"
#include "mozilla/dom/U2FBinding.h"
#include "mozilla/Preferences.h"
@ -37,7 +38,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
static mozilla::LazyLogModule gU2FLog("webauth_u2f");
static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
template <class CB, class Rsp>
void
@ -74,114 +75,6 @@ AssembleClientData(const nsAString& aOrigin, const nsAString& aTyp,
return NS_OK;
}
static nsresult
NSSTokenIsCompatible(nsINSSU2FToken* aNSSToken, const nsString& aVersionString,
bool* aIsCompatible)
{
MOZ_ASSERT(aIsCompatible);
if (XRE_IsParentProcess()) {
MOZ_ASSERT(aNSSToken);
return aNSSToken->IsCompatibleVersion(aVersionString, aIsCompatible);
}
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenIsCompatibleVersion(aVersionString, aIsCompatible)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
static nsresult
NSSTokenIsRegistered(nsINSSU2FToken* aNSSToken, CryptoBuffer& aKeyHandle,
bool* aIsRegistered)
{
MOZ_ASSERT(aIsRegistered);
if (XRE_IsParentProcess()) {
MOZ_ASSERT(aNSSToken);
return aNSSToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(),
aIsRegistered);
}
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenIsRegistered(aKeyHandle, aIsRegistered)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
static nsresult
NSSTokenSign(nsINSSU2FToken* aNSSToken, CryptoBuffer& aKeyHandle,
CryptoBuffer& aApplication, CryptoBuffer& aChallenge,
CryptoBuffer& aSignatureData)
{
if (XRE_IsParentProcess()) {
MOZ_ASSERT(aNSSToken);
uint8_t* buffer;
uint32_t bufferlen;
nsresult rv = aNSSToken->Sign(aApplication.Elements(), aApplication.Length(),
aChallenge.Elements(), aChallenge.Length(),
aKeyHandle.Elements(), aKeyHandle.Length(),
&buffer, &bufferlen);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(buffer);
aSignatureData.Assign(buffer, bufferlen);
free(buffer);
return NS_OK;
}
nsTArray<uint8_t> signatureBuffer;
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenSign(aApplication, aChallenge, aKeyHandle,
&signatureBuffer)) {
return NS_ERROR_FAILURE;
}
aSignatureData.Assign(signatureBuffer);
return NS_OK;
}
static nsresult
NSSTokenRegister(nsINSSU2FToken* aNSSToken, CryptoBuffer& aApplication,
CryptoBuffer& aChallenge, CryptoBuffer& aRegistrationData)
{
if (XRE_IsParentProcess()) {
MOZ_ASSERT(aNSSToken);
uint8_t* buffer;
uint32_t bufferlen;
nsresult rv;
rv = aNSSToken->Register(aApplication.Elements(), aApplication.Length(),
aChallenge.Elements(), aChallenge.Length(),
&buffer, &bufferlen);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(buffer);
aRegistrationData.Assign(buffer, bufferlen);
free(buffer);
return NS_OK;
}
nsTArray<uint8_t> registrationBuffer;
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
if (!cc->SendNSSU2FTokenRegister(aApplication, aChallenge,
&registrationBuffer)) {
return NS_ERROR_FAILURE;
}
aRegistrationData.Assign(registrationBuffer);
return NS_OK;
}
U2FTask::U2FTask(const nsAString& aOrigin, const nsAString& aAppId)
: mOrigin(aOrigin)
, mAppId(aAppId)
@ -195,12 +88,12 @@ U2FRegisterTask::U2FRegisterTask(const nsAString& aOrigin,
const Sequence<RegisterRequest>& aRegisterRequests,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FRegisterCallback* aCallback,
const nsCOMPtr<nsINSSU2FToken>& aNSSToken)
const Sequence<Authenticator>& aAuthenticators)
: U2FTask(aOrigin, aAppId)
, mRegisterRequests(aRegisterRequests)
, mRegisteredKeys(aRegisteredKeys)
, mCallback(aCallback)
, mNSSToken(aNSSToken)
, mAuthenticators(aAuthenticators)
{}
U2FRegisterTask::~U2FRegisterTask()
@ -228,10 +121,6 @@ U2FRegisterTask::Run()
return NS_ERROR_FAILURE;
}
// TODO: Implement USB Tokens in Bug 1245527
const bool softTokenEnabled =
Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
for (size_t i = 0; i < mRegisteredKeys.Length(); ++i) {
RegisteredKey request(mRegisteredKeys[i]);
@ -262,15 +151,18 @@ U2FRegisterTask::Run()
// Determine if the provided keyHandle is registered at any device. If so,
// then we'll return DEVICE_INELIGIBLE to signify we're already registered.
if (softTokenEnabled) {
rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
&isCompatible);
for (auto token : mAuthenticators) {
rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
}
if (!isCompatible) {
continue;
}
rv = NSSTokenIsRegistered(mNSSToken, keyHandle, &isRegistered);
rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
&isRegistered);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
@ -333,21 +225,30 @@ U2FRegisterTask::Run()
bool registerSuccess = false;
bool isCompatible = false;
if (!registerSuccess && softTokenEnabled) {
rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
&isCompatible);
for (auto token : mAuthenticators) {
rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
}
if (isCompatible) {
rv = NSSTokenRegister(mNSSToken, appParam, challengeParam, regData);
uint8_t* buffer;
uint32_t bufferlen;
nsresult rv;
rv = token->Register(appParam.Elements(), appParam.Length(),
challengeParam.Elements(), challengeParam.Length(),
&buffer, &bufferlen);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(buffer);
regData.Assign(buffer, bufferlen);
free(buffer);
registerSuccess = true;
break;
}
}
@ -391,12 +292,12 @@ U2FSignTask::U2FSignTask(const nsAString& aOrigin,
const nsAString& aChallenge,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FSignCallback* aCallback,
const nsCOMPtr<nsINSSU2FToken>& aNSSToken)
const Sequence<Authenticator>& aAuthenticators)
: U2FTask(aOrigin, aAppId)
, mChallenge(aChallenge)
, mRegisteredKeys(aRegisteredKeys)
, mCallback(aCallback)
, mNSSToken(aNSSToken)
, mAuthenticators(aAuthenticators)
{}
U2FSignTask::~U2FSignTask()
@ -423,10 +324,6 @@ U2FSignTask::Run()
return NS_ERROR_FAILURE;
}
// TODO: Implement USB Tokens in Bug 1245527
const bool softTokenEnabled =
Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
// Search the requests for one a token can fulfill
for (size_t i = 0; i < mRegisteredKeys.Length(); i += 1) {
RegisteredKey request(mRegisteredKeys[i]);
@ -492,30 +389,42 @@ U2FSignTask::Run()
// We ignore mTransports, as it is intended to be used for sorting the
// available devices by preference, but is not an exclusion factor.
if (!signSuccess && softTokenEnabled) {
for (size_t a = 0; a < mAuthenticators.Length() && !signSuccess; ++a) {
Authenticator token(mAuthenticators[a]);
bool isCompatible = false;
bool isRegistered = false;
rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
&isCompatible);
rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
}
if (!isCompatible) {
continue;
}
rv = NSSTokenIsRegistered(mNSSToken, keyHandle, &isRegistered);
rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
&isRegistered);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
}
if (isCompatible && isRegistered) {
rv = NSSTokenSign(mNSSToken, keyHandle, appParam, challengeParam,
signatureData);
uint8_t* buffer;
uint32_t bufferlen;
nsresult rv = token->Sign(appParam.Elements(), appParam.Length(),
challengeParam.Elements(), challengeParam.Length(),
keyHandle.Elements(), keyHandle.Length(),
&buffer, &bufferlen);
if (NS_FAILED(rv)) {
ReturnError(ErrorCode::OTHER_ERROR);
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(buffer);
signatureData.Assign(buffer, bufferlen);
free(buffer);
signSuccess = true;
}
}
@ -676,22 +585,37 @@ U2F::Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv)
}
if (!EnsureNSSInitializedChromeOrContent()) {
MOZ_LOG(gU2FLog, LogLevel::Debug, ("Failed to get NSS context for U2F"));
MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Failed to get NSS context for U2F"));
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (XRE_IsParentProcess()) {
mNSSToken = do_GetService(NS_NSSU2FTOKEN_CONTRACTID);
if (NS_WARN_IF(!mNSSToken)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
if (!XRE_IsParentProcess()) {
MOZ_LOG(gWebauthLog, LogLevel::Debug,
("Is e10s Process, getting remote U2F soft token"));
aRv = mUSBToken.Init();
if (NS_WARN_IF(aRv.Failed())) {
return;
if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
mozilla::fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
} else {
MOZ_LOG(gWebauthLog, LogLevel::Debug,
("Is non-e10s Process, getting direct U2F soft token"));
nsCOMPtr<nsINSSU2FToken> softToken =
do_GetService(NS_NSSU2FTOKEN_CONTRACTID);
if (NS_WARN_IF(!softToken)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (!mAuthenticators.AppendElement(softToken, mozilla::fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
}
}
}
@ -707,7 +631,7 @@ U2F::Register(const nsAString& aAppId,
aRegisterRequests,
aRegisteredKeys,
&aCallback,
mNSSToken);
mAuthenticators);
EvaluateAppIDAndRunTask(registerTask);
}
@ -722,7 +646,7 @@ U2F::Sign(const nsAString& aAppId,
{
RefPtr<U2FSignTask> signTask = new U2FSignTask(mOrigin, aAppId, aChallenge,
aRegisteredKeys, &aCallback,
mNSSToken);
mAuthenticators);
EvaluateAppIDAndRunTask(signTask);
}

Просмотреть файл

@ -40,6 +40,8 @@ enum class ErrorCode {
TIMEOUT = 5
};
typedef nsCOMPtr<nsINSSU2FToken> Authenticator;
class U2FTask : public Runnable
{
public:
@ -65,7 +67,7 @@ public:
const Sequence<RegisterRequest>& aRegisterRequests,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FRegisterCallback* aCallback,
const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
const Sequence<Authenticator>& aAuthenticators);
// No NSS resources to release.
virtual
@ -80,7 +82,7 @@ private:
Sequence<RegisterRequest> mRegisterRequests;
Sequence<RegisteredKey> mRegisteredKeys;
RefPtr<U2FRegisterCallback> mCallback;
nsCOMPtr<nsINSSU2FToken> mNSSToken;
Sequence<Authenticator> mAuthenticators;
};
class U2FSignTask final : public nsNSSShutDownObject,
@ -92,7 +94,7 @@ public:
const nsAString& aChallenge,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FSignCallback* aCallback,
const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
const Sequence<Authenticator>& aAuthenticators);
// No NSS resources to release.
virtual
@ -107,7 +109,7 @@ private:
nsString mChallenge;
Sequence<RegisteredKey> mRegisteredKeys;
RefPtr<U2FSignCallback> mCallback;
nsCOMPtr<nsINSSU2FToken> mNSSToken;
Sequence<Authenticator> mAuthenticators;
};
class U2F final : public nsISupports,
@ -155,8 +157,7 @@ public:
private:
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsString mOrigin;
USBToken mUSBToken;
nsCOMPtr<nsINSSU2FToken> mNSSToken;
Sequence<Authenticator> mAuthenticators;
~U2F();
};

Просмотреть файл

@ -5,11 +5,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
'NSSU2FTokenRemote.h',
'U2F.h',
'USBToken.h',
]
UNIFIED_SOURCES += [
'NSSU2FTokenRemote.cpp',
'U2F.cpp',
'USBToken.cpp',
]