import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,150 @@
/* -*- 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, nsIU2FToken)
static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
NSSU2FTokenRemote::NSSU2FTokenRemote()
{}
NSSU2FTokenRemote::~NSSU2FTokenRemote()
{}
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;
}

View file

@ -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 "nsIU2FToken.h"
class NSSU2FTokenRemote : public nsIU2FToken
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIU2FTOKEN
NSSU2FTokenRemote();
private:
virtual ~NSSU2FTokenRemote();
};
#endif // NSSU2FTokenRemote_h

1057
dom/u2f/U2F.cpp Normal file

File diff suppressed because it is too large Load diff

327
dom/u2f/U2F.h Normal file
View file

@ -0,0 +1,327 @@
/* -*- 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 "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;
};
// 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;
// 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

72
dom/u2f/USBToken.cpp Normal file
View file

@ -0,0 +1,72 @@
/* -*- 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 "USBToken.h"
namespace mozilla {
namespace dom {
USBToken::USBToken()
: mInitialized(false)
{}
USBToken::~USBToken()
{}
nsresult
USBToken::Init()
{
// This routine does nothing at present, but Bug 1245527 will
// integrate the upcoming USB HID service here, which will likely
// require an initialization upon load.
MOZ_ASSERT(!mInitialized);
if (mInitialized) {
return NS_OK;
}
mInitialized = true;
return NS_OK;
}
const nsString USBToken::mVersion = NS_LITERAL_STRING("U2F_V2");
bool
USBToken::IsCompatibleVersion(const nsString& aVersionParam) const
{
MOZ_ASSERT(mInitialized);
return mVersion == aVersionParam;
}
bool
USBToken::IsRegistered(const CryptoBuffer& aKeyHandle) const
{
MOZ_ASSERT(mInitialized);
return false;
}
nsresult
USBToken::Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& /* aChallengeParam */,
const CryptoBuffer& /* aApplicationParam */,
CryptoBuffer& aRegistrationData) const
{
MOZ_ASSERT(mInitialized);
return NS_ERROR_NOT_AVAILABLE;
}
nsresult
USBToken::Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
const CryptoBuffer& aKeyHandle,
CryptoBuffer& aSignatureData) const
{
MOZ_ASSERT(mInitialized);
return NS_ERROR_NOT_AVAILABLE;
}
} // namespace dom
} // namespace mozilla

49
dom/u2f/USBToken.h Normal file
View file

@ -0,0 +1,49 @@
/* -*- 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_USBToken_h
#define mozilla_dom_USBToken_h
#include "mozilla/dom/CryptoBuffer.h"
namespace mozilla {
namespace dom {
// USBToken implements FIDO operations using a USB device.
class USBToken final
{
public:
USBToken();
~USBToken();
nsresult Init();
bool IsCompatibleVersion(const nsString& aVersionParam) const;
bool IsRegistered(const CryptoBuffer& aKeyHandle) const;
nsresult Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
CryptoBuffer& aRegistrationData) const;
nsresult Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
const CryptoBuffer& aKeyHandle,
CryptoBuffer& aSignatureData) const;
private:
bool mInitialized;
static const nsString mVersion;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_USBToken_h

30
dom/u2f/moz.build Normal file
View file

@ -0,0 +1,30 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
EXPORTS.mozilla.dom += [
'NSSU2FTokenRemote.h',
'U2F.h',
'USBToken.h',
]
UNIFIED_SOURCES += [
'NSSU2FTokenRemote.cpp',
'U2F.cpp',
'USBToken.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/base',
'/dom/crypto',
'/security/manager/ssl',
'/security/pkix/include',
]
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']

View file

@ -0,0 +1,63 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
local_is(window.location.origin, "https://example.com", "Is loaded correctly");
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_expectThisManyTests(5);
u2f.register(null, [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Null AppID should work.");
local_completeTest();
});
u2f.register("", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Empty AppID should work.");
local_completeTest();
});
// Test: Correct TLD, but incorrect scheme
u2f.register("http://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "HTTP scheme is disallowed");
local_completeTest();
});
// Test: Correct TLD, and also HTTPS
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "HTTPS origin for example.com should work");
local_completeTest();
});
// Test: Dynamic origin
u2f.register(window.location.origin + "/otherAppId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Direct window origin should work");
local_completeTest();
});
</script>
</body>
</html>

View file

@ -0,0 +1,60 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
local_is(window.location.origin, "http://mochi.test:8888", "Is loaded correctly");
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_expectThisManyTests(5);
u2f.register(null, [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for null AppID");
local_completeTest();
});
u2f.register("", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for empty AppID");
local_completeTest();
});
u2f.register("http://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP AppID");
local_completeTest();
});
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTPS AppID from HTTP origin");
local_completeTest();
});
u2f.register(window.location.origin + "/otherAppId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP origin");
local_completeTest();
});
</script>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_is(window.location.origin, "https://test1.example.com", "Is loaded correctly");
local_expectThisManyTests(4);
// same domain check
u2f.register("https://test1.example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "AppID should work from a different path of this domain");
local_completeTest();
});
// same domain check, but wrong scheme
u2f.register("http://test1.example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "AppID should not work when using a different scheme");
local_completeTest();
});
// eTLD+1 subdomain check
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "AppID should not work from another subdomain in this registered domain");
local_completeTest();
});
// other domain check
u2f.register("https://mochi.test:8888/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "AppID should not work from other domains");
local_completeTest();
});
</script>
</body>
</html>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for FIDO Universal Second Factor No Token</title>
<script src="u2futil.js"></script>
</head>
<body>
<script class="testbody" type="text/javascript">
var challenge = new Uint8Array(16);
window.crypto.getRandomValues(challenge);
var regRequest = {
version: "U2F_V2",
challenge: bytesToBase64UrlSafe(challenge),
};
local_expectThisManyTests(1);
u2f.register(window.location.origin, [regRequest], [], function (res) {
local_isnot(res.errorCode, 0, "The registration should be rejected.");
local_completeTest();
});
</script>
</body>
</html>

View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for Register behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_is(window.location.origin, "https://example.com", "Is loaded correctly");
local_expectThisManyTests(7);
// basic check
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "AppID should work from the domain");
local_completeTest();
});
u2f.register("https://example.net/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 2, "AppID should not work from other domains");
local_completeTest();
});
u2f.register("", [], [], function(res){
local_is(res.errorCode, 2, "Empty register requests");
local_completeTest();
});
local_doesThrow(function(){
u2f.register("", null, [], null);
}, "Non-array register requests");
local_doesThrow(function(){
u2f.register("", [], null, null);
}, "Non-array sign requests");
local_doesThrow(function(){
u2f.register("", null, null, null);
}, "Non-array for both arguments");
u2f.register("", [{}], [], function(res){
local_is(res.errorCode, 2, "Empty request");
local_completeTest();
});
u2f.register("https://example.net/appId", [{
version: version,
}], [], function(res){
local_is(res.errorCode, 2, "Missing challenge");
local_completeTest();
});
u2f.register("https://example.net/appId", [{
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 2, "Missing version");
local_completeTest();
});
u2f.register("https://example.net/appId", [{
version: "a_version_00",
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 2, "Invalid version");
local_completeTest();
});
</script>
</body>
</html>

View file

@ -0,0 +1,201 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script type="text/javascript" src="u2futil.js"></script>
<script type="text/javascript" src="pkijs/common.js"></script>
<script type="text/javascript" src="pkijs/asn1.js"></script>
<script type="text/javascript" src="pkijs/x509_schema.js"></script>
<script type="text/javascript" src="pkijs/x509_simpl.js"></script>
</head>
<body>
<p>Register and Sign Test for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
var state = {
// Raw messages
regRequest: null,
regResponse: null,
regKey: null,
signChallenge: null,
signResponse: null,
// Parsed values
publicKey: null,
keyHandle: null,
// Constants
version: "U2F_V2",
appId: window.location.origin,
};
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true]]},
function() {
local_isnot(window.u2f, undefined, "U2F API endpoint must exist");
local_isnot(window.u2f.register, undefined, "U2F Register API endpoint must exist");
local_isnot(window.u2f.sign, undefined, "U2F Sign API endpoint must exist");
testRegistering();
function testRegistering() {
var challenge = new Uint8Array(16);
window.crypto.getRandomValues(challenge);
state.regRequest = {
version: state.version,
challenge: bytesToBase64UrlSafe(challenge),
};
u2f.register(state.appId, [state.regRequest], [], function(regResponse) {
state.regResponse = regResponse;
local_is(regResponse.errorCode, 0, "The registration did not error");
local_isnot(regResponse.registrationData, undefined, "The registration did not provide registration data");
if (regResponse.errorCode > 0) {
local_finished();
return;
}
// Parse the response data from the U2F token
var registrationData = base64ToBytesUrlSafe(regResponse.registrationData);
local_is(registrationData[0], 0x05, "Reserved byte is correct")
state.publicKeyBytes = registrationData.slice(1, 66);
var keyHandleLength = registrationData[66];
state.keyHandleBytes = registrationData.slice(67, 67 + keyHandleLength);
state.keyHandle = bytesToBase64UrlSafe(state.keyHandleBytes);
state.attestation = registrationData.slice(67 + keyHandleLength);
local_is(state.attestation[0], 0x30, "Attestation Certificate has correct starting byte");
var asn1 = org.pkijs.fromBER(state.attestation.buffer);
console.log(asn1);
state.attestationCert = new org.pkijs.simpl.CERT({ schema: asn1.result });
console.log(state.attestationCert);
state.attestationSig = state.attestation.slice(asn1.offset);
local_is(state.attestationCert.subject.types_and_values[0].value.value_block.value, "Firefox U2F Soft Token", "Expected Subject");
local_is(state.attestationCert.issuer.types_and_values[0].value.value_block.value, "Firefox U2F Soft Token", "Expected Issuer");
local_is(state.attestationCert.notAfter.value - state.attestationCert.notBefore.value, 1000*60*60*48, "Valid 48 hours (in millis)");
// Verify that the clientData from the U2F token makes sense
var clientDataJSON = "";
base64ToBytesUrlSafe(regResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
var clientData = JSON.parse(clientDataJSON);
local_is(clientData.typ, "navigator.id.finishEnrollment", "Register - Data type matches");
local_is(clientData.challenge, state.regRequest.challenge, "Register - Challenge matches");
local_is(clientData.origin, window.location.origin, "Register - Origins are the same");
// Verify the signature from the attestation certificate
deriveAppAndChallengeParam(state.appId, string2buffer(clientDataJSON))
.then(function(params){
state.appParam = params.appParam;
state.challengeParam = params.challengeParam;
return state.attestationCert.getPublicKey();
}).then(function(attestationPublicKey) {
var signedData = assembleRegistrationSignedData(state.appParam, state.challengeParam, state.keyHandleBytes, state.publicKeyBytes);
return verifySignature(attestationPublicKey, signedData, state.attestationSig);
}).then(function(verified) {
console.log("No error verifying signature");
local_ok(verified, "Attestation Certificate signature verified")
// Import the public key of the U2F token into WebCrypto
return importPublicKey(state.publicKeyBytes)
}).then(function(key) {
state.publicKey = key;
local_ok(true, "Imported public key")
// Ensure the attestation certificate is properly self-signed
return state.attestationCert.verify()
}).then(function(){
local_ok(true, "Attestation Certificate verification successful");
// Continue test
testReRegister()
}).catch(function(err){
console.log(err);
local_ok(false, "Attestation Certificate verification failed");
local_finished();
});
});
}
function testReRegister() {
state.regKey = {
version: state.version,
keyHandle: state.keyHandle,
};
// Test that we don't re-register if we provide regKey as an
// "already known" key handle. The U2F module should recognize regKey
// as being usable and, thus, give back errorCode 4.
u2f.register(state.appId, [state.regRequest], [state.regKey], function(regResponse) {
// Since we attempted to register with state.regKey as a known key, expect
// ineligible (=4).
local_is(regResponse.errorCode, 4, "The re-registration should show device ineligible");
local_is(regResponse.registrationData, undefined, "The re-registration did not provide registration data");
// Continue test
testSigning();
});
}
function testSigning() {
var challenge = new Uint8Array(16);
window.crypto.getRandomValues(challenge);
state.signChallenge = bytesToBase64UrlSafe(challenge);
// Now try to sign the signature challenge
u2f.sign(state.appId, state.signChallenge, [state.regKey], function(signResponse) {
state.signResponse = signResponse;
// Make sure this signature op worked, bailing early if it failed.
local_is(signResponse.errorCode, 0, "The signing did not error");
local_isnot(signResponse.clientData, undefined, "The signing did not provide client data");
if (signResponse.errorCode > 0) {
local_finished();
return;
}
// Decode the clientData that was returned from the module
var clientDataJSON = "";
base64ToBytesUrlSafe(signResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
var clientData = JSON.parse(clientDataJSON);
local_is(clientData.typ, "navigator.id.getAssertion", "Sign - Data type matches");
local_is(clientData.challenge, state.signChallenge, "Sign - Challenge matches");
local_is(clientData.origin, window.location.origin, "Sign - Origins are the same");
// Parse the signature data
var signatureData = base64ToBytesUrlSafe(signResponse.signatureData);
if (signatureData[0] != 0x01) {
throw "User presence byte not set";
}
var presenceAndCounter = signatureData.slice(0,5);
var signatureValue = signatureData.slice(5);
// Assemble the signed data and verify the signature
deriveAppAndChallengeParam(state.appId, string2buffer(clientDataJSON))
.then(function(params){
return assembleSignedData(params.appParam, presenceAndCounter, params.challengeParam);
})
.then(function(signedData) {
return verifySignature(state.publicKey, signedData, signatureValue);
})
.then(function(verified) {
console.log("No error verifying signing signature");
local_ok(verified, "Signing signature verified")
local_finished();
})
.catch(function(err) {
console.log(err);
local_ok(false, "Signing signature invalid");
local_finished();
});
});
}
});
</script>
</body>
</html>

View file

@ -0,0 +1,29 @@
[DEFAULT]
support-files =
frame_appid_facet.html
frame_appid_facet_insecure.html
frame_appid_facet_subdomain.html
frame_no_token.html
frame_register.html
frame_register_sign.html
pkijs/asn1.js
pkijs/common.js
pkijs/x509_schema.js
pkijs/x509_simpl.js
u2futil.js
# Feature does not function on e10s (Disabled in Bug 1297552)
[test_util_methods.html]
skip-if = !e10s
[test_no_token.html]
skip-if = !e10s
[test_register.html]
skip-if = !e10s
[test_register_sign.html]
skip-if = !e10s
[test_appid_facet.html]
skip-if = !e10s
[test_appid_facet_insecure.html]
skip-if = !e10s
[test_appid_facet_subdomain.html]
skip-if = !e10s

View file

@ -0,0 +1,30 @@
Copyright (c) 2014, GMO GlobalSign
Copyright (c) 2015, Peculiar Ventures
All rights reserved.
Author 2014-2015, Yury Strozhevsky
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1 @@
PKIjs and ASN1js are from https://pkijs.org/ and https://asn1js.org/.

5466
dom/u2f/tests/pkijs/asn1.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage, false);
document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_appid_facet.html";
});
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage, false);
document.getElementById('testing_frame').src = "http://mochi.test:8888/tests/dom/u2f/tests/frame_appid_facet_insecure.html";
});
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage, false);
document.getElementById('testing_frame').src = "https://test1.example.com/tests/dom/u2f/tests/frame_appid_facet_subdomain.html";
});
</script>
</body>
</html>

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for FIDO Universal Second Factor No Token</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", false],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage, false);
document.getElementById('testing_frame').src = 'frame_no_token.html';
});
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for Register behavior for FIDO Universal Second Factor</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage, false);
document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_register.html";
});
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Register and Sign Test for FIDO Universal Second Factor</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage, false);
document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_appid_facet.html";
});
</script>
</body>
</html>

View file

@ -0,0 +1,65 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for Utility Methods for other FIDO Universal Second Factor tests</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/dom/u2f/tests/u2futil.js"></script>
<script type="text/javascript" src="pkijs/common.js"></script>
<script type="text/javascript" src="pkijs/asn1.js"></script>
<script type="text/javascript" src="pkijs/x509_schema.js"></script>
<script type="text/javascript" src="pkijs/x509_simpl.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
["security.webauth.u2f_enable_softtoken", true],
["security.webauth.u2f_enable_usbtoken", false]]},
function() {
// Example from:
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html
//
// Run this example from the console to check that the u2futil methods work
var pubKey = hexDecode("04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d");
var appId = "https://gstatic.com/securitykey/a/example.com";
var clientData = string2buffer('{"typ":"navigator.id.getAssertion","challenge":"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}');
var presenceAndCounter = hexDecode("0100000001");
var signature = hexDecode("304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f");
// Import the key
// Assemble the client data
// Verify
Promise.all([
importPublicKey(pubKey),
deriveAppAndChallengeParam(appId, clientData)
])
.then(function(results) {
var importedKey = results[0];
var params = results[1];
var signedData = new Uint8Array(assembleSignedData(params.appParam, presenceAndCounter, params.challengeParam));
return verifySignature(importedKey, signedData, signature);
})
.then(function(verified) {
console.log("verified:", verified);
ok(true, "Utility methods work")
SimpleTest.finish();
})
.catch(function(err) {
console.log("error:", err);
ok(false, "Utility methods failed")
SimpleTest.finish();
});
});
</script>
</pre>
</body>
</html>

204
dom/u2f/tests/u2futil.js Normal file
View file

@ -0,0 +1,204 @@
// Used by local_addTest() / local_completeTest()
var _countCompletions = 0;
var _expectedCompletions = 0;
function handleEventMessage(event) {
if ("test" in event.data) {
let summary = event.data.test + ": " + event.data.msg;
log(event.data.status + ": " + summary);
ok(event.data.status, summary);
} else if ("done" in event.data) {
SimpleTest.finish();
} else {
ok(false, "Unexpected message in the test harness: " + event.data)
}
}
function log(msg) {
console.log(msg)
let logBox = document.getElementById("log");
if (logBox) {
logBox.textContent += "\n" + msg;
}
}
function local_is(value, expected, message) {
if (value === expected) {
local_ok(true, message);
} else {
local_ok(false, message + " unexpectedly: " + value + " !== " + expected);
}
}
function local_isnot(value, expected, message) {
if (value !== expected) {
local_ok(true, message);
} else {
local_ok(false, message + " unexpectedly: " + value + " === " + expected);
}
}
function local_ok(expression, message) {
let body = {"test": this.location.pathname, "status":expression, "msg": message}
parent.postMessage(body, "http://mochi.test:8888");
}
function local_doesThrow(fn, name) {
let gotException = false;
try {
fn();
} catch (ex) { gotException = true; }
local_ok(gotException, name);
};
function local_expectThisManyTests(count) {
if (_expectedCompletions > 0) {
local_ok(false, "Error: local_expectThisManyTests should only be called once.");
}
_expectedCompletions = count;
}
function local_completeTest() {
_countCompletions += 1
if (_countCompletions == _expectedCompletions) {
log("All tests completed.")
local_finished();
}
if (_countCompletions > _expectedCompletions) {
local_ok(false, "Error: local_completeTest called more than local_addTest.");
}
}
function local_finished() {
parent.postMessage({"done":true}, "http://mochi.test:8888");
}
function string2buffer(str) {
return (new Uint8Array(str.length)).map((x, i) => str.charCodeAt(i));
}
function buffer2string(buf) {
let str = "";
buf.map(x => str += String.fromCharCode(x));
return str;
}
function bytesToBase64(u8a){
let CHUNK_SZ = 0x8000;
let c = [];
for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return window.btoa(c.join(""));
}
function base64ToBytes(b64encoded) {
return new Uint8Array(window.atob(b64encoded).split("").map(function(c) {
return c.charCodeAt(0);
}));
}
function bytesToBase64UrlSafe(buf) {
return bytesToBase64(buf)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
function base64ToBytesUrlSafe(str) {
if (str.length % 4 == 1) {
throw "Improper b64 string";
}
var b64 = str.replace(/\-/g, "+").replace(/\_/g, "/");
while (b64.length % 4 != 0) {
b64 += "=";
}
return base64ToBytes(b64);
}
function hexEncode(buf) {
return Array.from(buf)
.map(x => ("0"+x.toString(16)).substr(-2))
.join("");
}
function hexDecode(str) {
return new Uint8Array(str.match(/../g).map(x => parseInt(x, 16)));
}
function importPublicKey(keyBytes) {
if (keyBytes[0] != 0x04 || keyBytes.byteLength != 65) {
throw "Bad public key octet string";
}
var jwk = {
kty: "EC",
crv: "P-256",
x: bytesToBase64UrlSafe(keyBytes.slice(1, 33)),
y: bytesToBase64UrlSafe(keyBytes.slice(33))
};
return crypto.subtle.importKey("jwk", jwk, {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"])
}
function deriveAppAndChallengeParam(appId, clientData) {
var appIdBuf = string2buffer(appId);
return Promise.all([
crypto.subtle.digest("SHA-256", appIdBuf),
crypto.subtle.digest("SHA-256", clientData)
])
.then(function(digests) {
return {
appParam: new Uint8Array(digests[0]),
challengeParam: new Uint8Array(digests[1]),
};
});
}
function assembleSignedData(appParam, presenceAndCounter, challengeParam) {
let signedData = new Uint8Array(32 + 1 + 4 + 32);
appParam.map((x, i) => signedData[0 + i] = x);
presenceAndCounter.map((x, i) => signedData[32 + i] = x);
challengeParam.map((x, i) => signedData[37 + i] = x);
return signedData;
}
function assembleRegistrationSignedData(appParam, challengeParam, keyHandle, pubKey) {
let signedData = new Uint8Array(1 + 32 + 32 + keyHandle.length + 65);
signedData[0] = 0x00;
appParam.map((x, i) => signedData[1 + i] = x);
challengeParam.map((x, i) => signedData[33 + i] = x);
keyHandle.map((x, i) => signedData[65 + i] = x);
pubKey.map((x, i) => signedData[65 + keyHandle.length + i] = x);
return signedData;
}
function sanitizeSigArray(arr) {
// ECDSA signature fields into WebCrypto must be exactly 32 bytes long, so
// this method strips leading padding bytes, if added, and also appends
// padding zeros, if needed.
if (arr.length > 32) {
arr = arr.slice(arr.length - 32)
}
let ret = new Uint8Array(32);
ret.set(arr, ret.length - arr.length);
return ret;
}
function verifySignature(key, data, derSig) {
let sigAsn1 = org.pkijs.fromBER(derSig.buffer);
let sigR = new Uint8Array(sigAsn1.result.value_block.value[0].value_block.value_hex);
let sigS = new Uint8Array(sigAsn1.result.value_block.value[1].value_block.value_hex);
// The resulting R and S values from the ASN.1 Sequence must be fit into 32
// bytes. Sometimes they have leading zeros, sometimes they're too short, it
// all depends on what lib generated the signature.
let R = sanitizeSigArray(sigR);
let S = sanitizeSigArray(sigS);
let sigData = new Uint8Array(R.length + S.length);
sigData.set(R);
sigData.set(S, R.length);
let alg = {name: "ECDSA", hash: "SHA-256"};
return crypto.subtle.verify(alg, key, sigData, data);
}