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
This commit is contained in:
J.C. Jones
2016-04-18 14:49:07 -07:00
parent d3e5c70cba
commit 204704718b
13 changed files with 698 additions and 730 deletions
+610 -594
View File
File diff suppressed because it is too large Load Diff
+82 -30
View File
@@ -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
-8
View File
@@ -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
-9
View File
@@ -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
-8
View File
@@ -1,8 +0,0 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://fido.example.com"
]
}]
}
-8
View File
@@ -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
-1
View File
@@ -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>
+6 -6
View File
@@ -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);