зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1330138 - Divide U2F and WebAuthn into separate directories; r=jcj
MozReview-Commit-ID: FCCSL6XWhTf --HG-- rename : dom/u2f/NSSU2FTokenRemote.cpp => dom/webauthn/NSSU2FTokenRemote.cpp rename : dom/u2f/NSSU2FTokenRemote.h => dom/webauthn/NSSU2FTokenRemote.h rename : dom/u2f/ScopedCredential.cpp => dom/webauthn/ScopedCredential.cpp rename : dom/u2f/ScopedCredential.h => dom/webauthn/ScopedCredential.h rename : dom/u2f/ScopedCredentialInfo.cpp => dom/webauthn/ScopedCredentialInfo.cpp rename : dom/u2f/ScopedCredentialInfo.h => dom/webauthn/ScopedCredentialInfo.h rename : dom/u2f/WebAuthnAssertion.cpp => dom/webauthn/WebAuthnAssertion.cpp rename : dom/u2f/WebAuthnAssertion.h => dom/webauthn/WebAuthnAssertion.h rename : dom/u2f/WebAuthnAttestation.cpp => dom/webauthn/WebAuthnAttestation.cpp rename : dom/u2f/WebAuthnAttestation.h => dom/webauthn/WebAuthnAttestation.h rename : dom/u2f/tests/test_webauthn_get_assertion.html => dom/webauthn/tests/test_webauthn_get_assertion.html rename : dom/u2f/tests/test_webauthn_loopback.html => dom/webauthn/tests/test_webauthn_loopback.html rename : dom/u2f/tests/test_webauthn_make_credential.html => dom/webauthn/tests/test_webauthn_make_credential.html rename : dom/u2f/tests/test_webauthn_no_token.html => dom/webauthn/tests/test_webauthn_no_token.html rename : dom/u2f/tests/test_webauthn_sameorigin.html => dom/webauthn/tests/test_webauthn_sameorigin.html
This commit is contained in:
Родитель
4fca25988e
Коммит
14bcecc66f
|
@ -85,6 +85,7 @@ DIRS += [
|
|||
'promise',
|
||||
'smil',
|
||||
'url',
|
||||
'webauthn',
|
||||
'webidl',
|
||||
'xbl',
|
||||
'xml',
|
||||
|
|
|
@ -42,7 +42,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
|
||||
|
||||
static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
|
||||
static mozilla::LazyLogModule gU2FLog("u2f");
|
||||
|
||||
static nsresult
|
||||
AssembleClientData(const nsAString& aOrigin, const nsAString& aTyp,
|
||||
|
@ -81,7 +81,7 @@ U2FStatus::WaitGroupAdd()
|
|||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
mCount += 1;
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("U2FStatus::WaitGroupAdd, now %d", mCount));
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ U2FStatus::WaitGroupDone()
|
|||
|
||||
MOZ_ASSERT(mCount > 0);
|
||||
mCount -= 1;
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("U2FStatus::WaitGroupDone, now %d", mCount));
|
||||
if (mCount == 0) {
|
||||
mReentrantMonitor.NotifyAll();
|
||||
|
@ -103,7 +103,7 @@ void
|
|||
U2FStatus::WaitGroupWait()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("U2FStatus::WaitGroupWait, now %d", mCount));
|
||||
|
||||
while (mCount > 0) {
|
||||
|
@ -111,7 +111,7 @@ U2FStatus::WaitGroupWait()
|
|||
}
|
||||
|
||||
MOZ_ASSERT(mCount == 0);
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("U2FStatus::Wait completed, now count=%d stopped=%d", mCount,
|
||||
mIsStopped));
|
||||
}
|
||||
|
@ -627,7 +627,7 @@ U2FRegisterRunnable::Run()
|
|||
U2FPrepPromise::All(AbstractThread::MainThread(), prepPromiseList)
|
||||
->Then(AbstractThread::MainThread(), __func__,
|
||||
[status] (const nsTArray<Authenticator>& aTokens) {
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("ALL: None of the RegisteredKeys were recognized. n=%d",
|
||||
aTokens.Length()));
|
||||
|
||||
|
@ -784,7 +784,7 @@ U2FSignRunnable::U2FSignRunnable(const nsAString& aOrigin,
|
|||
nsresult rv = AssembleClientData(aOrigin, kGetAssertion, aChallenge,
|
||||
mClientData);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Warning,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Warning,
|
||||
("Failed to AssembleClientData for the U2FSignRunnable."));
|
||||
return;
|
||||
}
|
||||
|
@ -976,7 +976,7 @@ U2F::Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv)
|
|||
}
|
||||
|
||||
if (!EnsureNSSInitializedChromeOrContent()) {
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("Failed to get NSS context for U2F"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
|
@ -984,7 +984,7 @@ U2F::Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv)
|
|||
|
||||
// This only functions in e10s mode
|
||||
if (XRE_IsParentProcess()) {
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("Is non-e10s Process, U2F not available"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
|
|
|
@ -5,27 +5,14 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'NSSU2FTokenRemote.h',
|
||||
'ScopedCredential.h',
|
||||
'ScopedCredentialInfo.h',
|
||||
'U2F.h',
|
||||
'U2FAuthenticator.h',
|
||||
'USBToken.h',
|
||||
'WebAuthentication.h',
|
||||
'WebAuthnAssertion.h',
|
||||
'WebAuthnAttestation.h',
|
||||
'WebAuthnRequest.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'NSSU2FTokenRemote.cpp',
|
||||
'ScopedCredential.cpp',
|
||||
'ScopedCredentialInfo.cpp',
|
||||
'U2F.cpp',
|
||||
'USBToken.cpp',
|
||||
'WebAuthentication.cpp',
|
||||
'WebAuthnAssertion.cpp',
|
||||
'WebAuthnAttestation.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -27,18 +27,3 @@ skip-if = !e10s
|
|||
skip-if = !e10s
|
||||
[test_appid_facet_subdomain.html]
|
||||
skip-if = !e10s
|
||||
[test_webauthn_loopback.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_no_token.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_make_credential.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_get_assertion.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_sameorigin.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
|
@ -13,10 +13,13 @@
|
|||
#include "pkix/Input.h"
|
||||
#include "pkixutil.h"
|
||||
|
||||
#define PREF_U2F_SOFTTOKEN_ENABLED "security.webauth.u2f_enable_softtoken"
|
||||
#define PREF_U2F_USBTOKEN_ENABLED "security.webauth.u2f_enable_usbtoken"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
|
||||
static mozilla::LazyLogModule gWebauthLog("webauthn");
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef mozilla_dom_WebAuthentication_h
|
||||
#define mozilla_dom_WebAuthentication_h
|
||||
|
||||
#include "hasht.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
|
@ -18,6 +19,7 @@
|
|||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "U2FAuthenticator.h"
|
|
@ -15,7 +15,7 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
|
||||
//extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
|
||||
|
||||
// WebAuthnRequest tracks the completion of a single WebAuthn request that
|
||||
// may run on multiple kinds of authenticators, and be subject to a deadline.
|
||||
|
@ -32,9 +32,9 @@ public:
|
|||
|
||||
void AddActiveToken(const char* aCallSite)
|
||||
{
|
||||
MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
("WebAuthnRequest is tracking a new token, called from [%s]",
|
||||
aCallSite));
|
||||
// MOZ_LOG(gWebauthLog, LogLevel::Debug,
|
||||
// ("WebAuthnRequest is tracking a new token, called from [%s]",
|
||||
// aCallSite));
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
MOZ_ASSERT(!IsComplete());
|
||||
mCountTokens += 1;
|
|
@ -0,0 +1,38 @@
|
|||
# -*- 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',
|
||||
'ScopedCredential.h',
|
||||
'ScopedCredentialInfo.h',
|
||||
'WebAuthentication.h',
|
||||
'WebAuthnAssertion.h',
|
||||
'WebAuthnAttestation.h',
|
||||
'WebAuthnRequest.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'NSSU2FTokenRemote.cpp',
|
||||
'ScopedCredential.cpp',
|
||||
'ScopedCredentialInfo.cpp',
|
||||
'WebAuthentication.cpp',
|
||||
'WebAuthnAssertion.cpp',
|
||||
'WebAuthnAttestation.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/dom/crypto',
|
||||
'/security/manager/ssl',
|
||||
'/security/pkix/include',
|
||||
'/security/pkix/lib',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
|
@ -0,0 +1,23 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
pkijs/asn1.js
|
||||
pkijs/common.js
|
||||
pkijs/x509_schema.js
|
||||
pkijs/x509_simpl.js
|
||||
u2futil.js
|
||||
|
||||
[test_webauthn_loopback.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_no_token.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_make_credential.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_get_assertion.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
||||
[test_webauthn_sameorigin.html]
|
||||
skip-if = !e10s
|
||||
scheme = https
|
|
@ -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.
|
|
@ -0,0 +1 @@
|
|||
PKIjs and ASN1js are from https://pkijs.org/ and https://asn1js.org/.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,227 @@
|
|||
// 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 decodeU2FRegistration(aRegData) {
|
||||
if (aRegData[0] != 0x05) {
|
||||
return Promise.reject("Sentinal byte != 0x05");
|
||||
}
|
||||
|
||||
let keyHandleLength = aRegData[66];
|
||||
let u2fRegObj = {
|
||||
publicKeyBytes: aRegData.slice(1, 66),
|
||||
keyHandleBytes: aRegData.slice(67, 67 + keyHandleLength),
|
||||
attestationBytes: aRegData.slice(67 + keyHandleLength)
|
||||
}
|
||||
|
||||
u2fRegObj.keyHandle = bytesToBase64UrlSafe(u2fRegObj.keyHandleBytes);
|
||||
|
||||
return importPublicKey(u2fRegObj.publicKeyBytes)
|
||||
.then(function(keyObj) {
|
||||
u2fRegObj.publicKey = keyObj;
|
||||
return u2fRegObj;
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
console.log("Verifying these bytes: " + bytesToBase64UrlSafe(data));
|
||||
|
||||
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);
|
||||
}
|
Загрузка…
Ссылка в новой задаче