Bug 1264472 - Use nsRunnables in FIDO U2F. r=keeler

- Move the AppID/FacetID algorithm into its own (potentially reentrant) method
  to facilitate Bug 1244959
- Change the Register and Sign operations to be Runnables so that in the future
  they can be executed after (future) remote fetches
- Clean up error handling
- Remove unnecessary remote-load Facet test files; we'll re-add some form of
  them when the remote load algorithm is completed

MozReview-Commit-ID: 4K1q6ovzhgf

--HG--
extra : transplant_source : /%7F/%96o1%3E%5E%17%20%A2%D0%AA%10%21%88%19%D9%B3%C9
extra : histedit_source : 4d3c61294951920a22e1f1eb7846a2a03f7cd2f0
This commit is contained in:
J.C. Jones 2016-04-18 14:49:07 -07:00
Родитель d8d938eb8b
Коммит f55c5966d7
13 изменённых файлов: 698 добавлений и 730 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -34,6 +34,88 @@ class U2FSignCallback;
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
};
class U2FTask : public nsRunnable
{
public:
U2FTask(const nsAString& aOrigin,
const nsAString& aAppId);
nsString mOrigin;
nsString mAppId;
virtual
void ReturnError(ErrorCode code) = 0;
protected:
virtual ~U2FTask();
};
class U2FRegisterTask final : public nsNSSShutDownObject,
public U2FTask
{
public:
U2FRegisterTask(const nsAString& aOrigin,
const nsAString& aAppId,
const Sequence<RegisterRequest>& aRegisterRequests,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FRegisterCallback* aCallback,
const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
// No NSS resources to release.
virtual
void virtualDestroyNSSReference() override {};
void ReturnError(ErrorCode code) override;
NS_DECL_NSIRUNNABLE
private:
~U2FRegisterTask();
Sequence<RegisterRequest> mRegisterRequests;
Sequence<RegisteredKey> mRegisteredKeys;
RefPtr<U2FRegisterCallback> mCallback;
nsCOMPtr<nsINSSU2FToken> mNSSToken;
};
class U2FSignTask final : public nsNSSShutDownObject,
public U2FTask
{
public:
U2FSignTask(const nsAString& aOrigin,
const nsAString& aAppId,
const nsAString& aChallenge,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FSignCallback* aCallback,
const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
// No NSS resources to release.
virtual
void virtualDestroyNSSReference() override {};
void ReturnError(ErrorCode code) override;
NS_DECL_NSIRUNNABLE
private:
~U2FSignTask();
nsString mChallenge;
Sequence<RegisteredKey> mRegisteredKeys;
RefPtr<U2FSignCallback> mCallback;
nsCOMPtr<nsINSSU2FToken> mNSSToken;
};
class U2F final : public nsISupports,
public nsWrapperCache,
public nsNSSShutDownObject
@ -82,37 +164,7 @@ private:
USBToken mUSBToken;
nsCOMPtr<nsINSSU2FToken> mNSSToken;
static const nsString FinishEnrollment;
static const nsString GetAssertion;
~U2F();
nsresult
AssembleClientData(const nsAString& aTyp,
const nsAString& aChallenge,
CryptoBuffer& aClientData) const;
// ValidAppID determines whether the supplied FIDO AppID is valid for
// the current FacetID, e.g., the current origin. If the supplied
// aAppId param is null or empty, it will be filled in per the algorithm.
// See https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-appid-and-facets.html
// for a description of the algorithm.
bool
ValidAppID(/* in/out */ nsString& aAppId) const;
nsresult
NSSTokenIsCompatible(const nsString& versionString, bool* isCompatible);
nsresult
NSSTokenIsRegistered(CryptoBuffer& keyHandle, bool* isRegistered);
nsresult
NSSTokenRegister(CryptoBuffer& application, CryptoBuffer& challenge,
CryptoBuffer& registrationData);
nsresult
NSSTokenSign(CryptoBuffer& keyHandle, CryptoBuffer& application,
CryptoBuffer& challenge, CryptoBuffer& signatureData);
};
} // namespace dom

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

@ -1,8 +0,0 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://fido.example.com"
]
}]
}

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

@ -1 +0,0 @@
Content-Type: application/fido.trusted-apps+json

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

@ -1,6 +0,0 @@
# This file isn't actually JSON, so it shouldn't successfully parse.
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
},{}]
}

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

@ -1 +0,0 @@
Content-Type: application/fido.trusted-apps+json

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

@ -1,9 +0,0 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://example.net",
"http://www.example.com"
]
}]
}

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

@ -1 +0,0 @@
Content-Type: application/fido.trusted-apps+json

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

@ -1,8 +0,0 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://fido.example.com"
]
}]
}

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

@ -5,16 +5,8 @@ support-files =
test_frame_appid_facet.html
test_frame_register.html
test_frame_register_sign.html
test_frame_appid_facet_remoteload.html
test_frame_appid_facet_insecure.html
test_frame_appid_facet_subdomain.html
facet/facetList.txt
facet/facetList-good
facet/facetList-good^headers^
facet/facetList-no_overlap
facet/facetList-no_overlap^headers^
facet/facetList-invalid_format
facet/facetList-invalid_format^headers^
pkijs/common.js
pkijs/asn1.js
pkijs/x509_schema.js

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

@ -26,7 +26,6 @@ function() {
"https://example.com/tests/dom/u2f/tests/test_frame_register_sign.html",
"http://mochi.test:8888/tests/dom/u2f/tests/test_frame_appid_facet_insecure.html",
"https://example.com/tests/dom/u2f/tests/test_frame_appid_facet.html",
"https://example.com/tests/dom/u2f/tests/test_frame_appid_facet_remoteload.html",
"https://test1.example.com/tests/dom/u2f/tests/test_frame_appid_facet_subdomain.html"
];

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

@ -1,57 +0,0 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for Remote AppId Load 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");
// TODO: Must support remote loads of AppID manifests first.
//
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList.txt", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not permit this AppId contentType");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetListMissing", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not permit with a missing AppID list");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-good", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 0, "The AppId should permit example.com");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-no_overlap", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not permit with a missing AppID list");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-invalid_format", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not fail gracefully on invalid formatted facet lists");
// });
local_finished();
</script>
</body>
</html>

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

@ -82,9 +82,9 @@ function() {
var clientDataJSON = "";
base64ToBytesUrlSafe(regResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
var clientData = JSON.parse(clientDataJSON);
local_is(clientData.typ, "navigator.id.finishEnrollment", "Data type matches");
local_is(clientData.challenge, state.regRequest.challenge, "Register challenge matches");
local_is(clientData.origin, window.location.origin, "Origins are the same");
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))
@ -161,9 +161,9 @@ function() {
var clientDataJSON = "";
base64ToBytesUrlSafe(signResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
var clientData = JSON.parse(clientDataJSON);
local_is(clientData.typ, "navigator.id.getAssertion", "Data type matches");
local_is(clientData.challenge, state.signChallenge, "Sign challenge matches");
local_is(clientData.origin, window.location.origin, "Origins are the same");
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);