@blamejs/pki 0.8.31 → 0.8.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +393 -0
- package/README.md +66 -15
- package/bin/pki.js +716 -30
- package/examples/issue-a-certificate.js +90 -0
- package/examples/post-quantum.js +66 -0
- package/examples/read-a-certificate.js +56 -0
- package/examples/revoke-and-check.js +86 -0
- package/examples/sign-and-verify-cms.js +63 -0
- package/index.js +18 -5
- package/lib/acme.js +223 -74
- package/lib/alt-sig.js +441 -0
- package/lib/asn1-der.js +48 -25
- package/lib/attrcert-sign.js +27 -19
- package/lib/cbor-det.js +24 -14
- package/lib/cmc-build.js +9 -2
- package/lib/cmc-verify.js +14 -3
- package/lib/cmp-build.js +17 -5
- package/lib/cmp-session.js +24 -11
- package/lib/cmp-verify.js +28 -19
- package/lib/cms-compress.js +3 -1
- package/lib/cms-decrypt.js +102 -7
- package/lib/cms-encrypt.js +59 -7
- package/lib/cms-sign.js +31 -9
- package/lib/cms-verify.js +22 -18
- package/lib/composite-kem.js +30 -18
- package/lib/composite-sig.js +29 -14
- package/lib/constants.js +26 -0
- package/lib/crl-sign.js +111 -19
- package/lib/crmf-sign.js +167 -15
- package/lib/csr-sign.js +117 -6
- package/lib/ct.js +939 -48
- package/lib/est.js +29 -16
- package/lib/framework-error.js +12 -0
- package/lib/guard-async.js +1 -1
- package/lib/guard-bytes.js +103 -20
- package/lib/guard-crypto.js +205 -3
- package/lib/guard-encoding.js +6 -1
- package/lib/guard-identifier.js +55 -8
- package/lib/guard-intrinsic.js +43 -0
- package/lib/guard-json.js +122 -9
- package/lib/guard-limits.js +12 -1
- package/lib/guard-list.js +63 -8
- package/lib/guard-name.js +167 -20
- package/lib/guard-range.js +43 -5
- package/lib/guard-text.js +41 -0
- package/lib/guard-verdict.js +39 -8
- package/lib/hpke.js +688 -42
- package/lib/http-transport.js +156 -4
- package/lib/identity-match.js +4 -4
- package/lib/inspect.js +33 -6
- package/lib/jose.js +93 -25
- package/lib/key.js +18 -13
- package/lib/lint.js +165 -1
- package/lib/merkle.js +233 -27
- package/lib/ocsp.js +27 -14
- package/lib/oid.js +14 -4
- package/lib/path-validate.js +143 -61
- package/lib/pkcs12-build.js +7 -2
- package/lib/pki-build.js +31 -7
- package/lib/possession.js +575 -0
- package/lib/related-cert.js +708 -0
- package/lib/revocation-fetch.js +6 -6
- package/lib/scep.js +15 -5
- package/lib/schema-c509.js +5 -2
- package/lib/schema-cmc.js +1 -1
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +2 -15
- package/lib/schema-csr.js +69 -0
- package/lib/schema-engine.js +10 -3
- package/lib/schema-pkix.js +228 -2
- package/lib/schema-tsp.js +7 -6
- package/lib/sign-scheme.js +22 -9
- package/lib/sigstore.js +684 -168
- package/lib/sleep.js +8 -1
- package/lib/smime.js +22 -1
- package/lib/tlog.js +1235 -0
- package/lib/tls-cert-compress.js +25 -8
- package/lib/tsp-sign.js +120 -35
- package/lib/tuf.js +1395 -0
- package/lib/validator-tpm.js +4 -1
- package/lib/verify-seam.js +7 -1
- package/lib/webauthn-mds.js +41 -4
- package/lib/webauthn.js +43 -25
- package/lib/webcrypto.js +48 -46
- package/lib/x509-sign.js +225 -19
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
package/lib/validator-tpm.js
CHANGED
|
@@ -12,9 +12,12 @@ var TPM_ALG = Object.assign(Object.create(null), { RSA: 0x0001, SHA1: 0x0004, SH
|
|
|
12
12
|
var TPM_ALG_HASH = Object.create(null); TPM_ALG_HASH[TPM_ALG.SHA1] = "sha1"; TPM_ALG_HASH[TPM_ALG.SHA256] = "sha256"; TPM_ALG_HASH[TPM_ALG.SHA384] = "sha384"; TPM_ALG_HASH[TPM_ALG.SHA512] = "sha512";
|
|
13
13
|
var TPM_CURVE_TO_COSE = Object.create(null); TPM_CURVE_TO_COSE[0x0003] = 1; TPM_CURVE_TO_COSE[0x0004] = 2; TPM_CURVE_TO_COSE[0x0005] = 3;
|
|
14
14
|
|
|
15
|
+
/** @internal The comparison goes through the captured operation. This is what decides that the TPM
|
|
16
|
+
* public area holds the credential's key, so a replacement answering zero makes a different key
|
|
17
|
+
* match. */
|
|
15
18
|
function _ucmp(a, b) {
|
|
16
19
|
function strip(x) { var i = 0; while (i < x.length - 1 && x[i] === 0) i++; return x.subarray(i); }
|
|
17
|
-
return
|
|
20
|
+
return guard.bytes.compare(strip(a), strip(b)) === 0;
|
|
18
21
|
}
|
|
19
22
|
function _uintBytes(n) {
|
|
20
23
|
var hex = n.toString(16); if (hex.length % 2) hex = "0" + hex;
|
package/lib/verify-seam.js
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
|
|
6
6
|
var guard = require("./guard-all");
|
|
7
|
+
var _seamIntrinsic = require("./guard-intrinsic");
|
|
8
|
+
/** @internal The refusal below is a RESOLVED promise carrying `false`, and `Promise.resolve` builds
|
|
9
|
+
* from its RECEIVER. Read from the global binding at the call, that refusal is constructed by whatever
|
|
10
|
+
* the binding holds, so a replacement settling truthy turns it into an acceptance. */
|
|
11
|
+
var _Promise = _seamIntrinsic.Promise;
|
|
12
|
+
var _promiseResolve = _seamIntrinsic.uncurry(_seamIntrinsic.promiseResolve);
|
|
7
13
|
|
|
8
14
|
function makeSeam(name, ErrorClass, code) {
|
|
9
15
|
var engine = null;
|
|
@@ -15,7 +21,7 @@ function makeSeam(name, ErrorClass, code) {
|
|
|
15
21
|
if (engine == null) {
|
|
16
22
|
throw new ErrorClass(code, "the " + name + " signature engine is not initialized (require pki before use)");
|
|
17
23
|
}
|
|
18
|
-
if (!guard.crypto.isOctetAligned(signature)) return
|
|
24
|
+
if (!guard.crypto.isOctetAligned(signature)) return _promiseResolve(_Promise, false);
|
|
19
25
|
return engine(sigAlg, signature.bytes, spkiBytes, preimage);
|
|
20
26
|
},
|
|
21
27
|
};
|
package/lib/webauthn-mds.js
CHANGED
|
@@ -8,7 +8,15 @@ var frameworkError = require("./framework-error");
|
|
|
8
8
|
var x509 = require("./schema-x509");
|
|
9
9
|
var guard = require("./guard-all");
|
|
10
10
|
var intrinsic = require("./guard-intrinsic");
|
|
11
|
+
/** @internal The seed of the fold below is a RESOLVED promise carrying `false`, the verdict for a chain
|
|
12
|
+
* that matched no anchor, and `Promise.resolve` builds from its RECEIVER. Read from the global binding,
|
|
13
|
+
* that seed is constructed by whatever the binding holds, so a replacement settling truthy starts the
|
|
14
|
+
* fold from "trusted" and every chain is trusted by default. */
|
|
15
|
+
var _Promise = intrinsic.Promise;
|
|
16
|
+
var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
11
17
|
var _hasOwn = intrinsic.hasOwn;
|
|
18
|
+
var _reverse = intrinsic.reverse;
|
|
19
|
+
var _arrSlice = intrinsic.arraySlice;
|
|
12
20
|
var _charAt = intrinsic.uncurry(String.prototype.charAt);
|
|
13
21
|
var jose = require("./jose");
|
|
14
22
|
var rfc3339 = require("./rfc3339");
|
|
@@ -21,6 +29,11 @@ var nodeCrypto = require("crypto");
|
|
|
21
29
|
|
|
22
30
|
var oid = require("./oid");
|
|
23
31
|
var pkix = require("./schema-pkix");
|
|
32
|
+
/** @internal The import and the verify a metadata BLOB's signature check runs, captured at load. One
|
|
33
|
+
* `verify` answering true accepts a BLOB no authority signed, and the whole authenticator trust list
|
|
34
|
+
* comes out of that BLOB. */
|
|
35
|
+
var _subtleImportKey = intrinsic.uncurry(webcrypto.webcrypto.subtle.importKey);
|
|
36
|
+
var _subtleVerify = intrinsic.uncurry(webcrypto.webcrypto.subtle.verify);
|
|
24
37
|
var WebauthnError = frameworkError.WebauthnError;
|
|
25
38
|
var _KU_NS = pkix.makeNS("webauthn", WebauthnError, oid);
|
|
26
39
|
function _err(code, message, cause) { return new WebauthnError(code, message, cause); }
|
|
@@ -28,6 +41,17 @@ var _KEYID_HEX_CI = pkix.charTable("0123456789abcdefABCDEF");
|
|
|
28
41
|
var _KEYID_HEX_LOWER = pkix.charTable("0123456789abcdef");
|
|
29
42
|
var C = constants.LIMITS;
|
|
30
43
|
|
|
44
|
+
/** @internal The BLOB members this module reads as integers, and so the members whose number tokens must
|
|
45
|
+
* denote exactly the integer they are read as. `no` is the one that decides something: `_assertFresh`
|
|
46
|
+
* admits a BLOB only when its `no` exceeds the one a relying party holds, so a rounded spelling compares
|
|
47
|
+
* as an integer it is not and a stale catalogue is accepted as a newer one.
|
|
48
|
+
*
|
|
49
|
+
* Named individually rather than applied to the document, because a metadata statement is ordinary JSON
|
|
50
|
+
* and carries genuine doubles: the published BLOB has members whose values are not integers, among them
|
|
51
|
+
* the biometric `selfAttestedFRR` and `selfAttestedFAR` rates and `iAPARThreshold`. A document-wide rule
|
|
52
|
+
* refuses the real catalogue. */
|
|
53
|
+
var EXACT_INTEGER_BLOB_MEMBERS = intrinsic.assign(intrinsic.create(null), { no: 1 });
|
|
54
|
+
|
|
31
55
|
var LEAF_SCHEMES_BY_SPKI_ALG = Object.assign(Object.create(null), {
|
|
32
56
|
ecPublicKey: { ECDSA: 1 },
|
|
33
57
|
rsaEncryption: { "RSASSA-PKCS1-v1_5": 1, "RSA-PSS": 1 },
|
|
@@ -193,6 +217,8 @@ function _verifyMetadataBlob(blob, opts) {
|
|
|
193
217
|
}
|
|
194
218
|
var header, sig;
|
|
195
219
|
try {
|
|
220
|
+
/** @internal The header carries `alg`, `typ` and the `x5c` chain, all strings, and no number this
|
|
221
|
+
* module reads as an integer, so there is nothing here for the rule to apply to. */
|
|
196
222
|
header = guard.json.parse(Buffer.from(jose.base64url.decode(segs[0])), _err, {
|
|
197
223
|
maxBytes: C.MDS_BLOB_HEADER_MAX_BYTES, maxDepth: C.JSON_MAX_DEPTH,
|
|
198
224
|
tooLarge: "webauthn/too-large", badJson: "webauthn/bad-metadata-blob",
|
|
@@ -261,9 +287,9 @@ function _verifyMetadataBlob(blob, opts) {
|
|
|
261
287
|
}
|
|
262
288
|
var params = _blobAlgParams(algRow, leafAlg);
|
|
263
289
|
var signingInput = Buffer.from(segs[0] + "." + segs[1], "ascii");
|
|
264
|
-
return webcrypto.webcrypto.subtle
|
|
290
|
+
return _subtleImportKey(webcrypto.webcrypto.subtle, "spki", leaf.subjectPublicKeyInfo.bytes, params.imp, false, ["verify"])
|
|
265
291
|
.then(function (key) {
|
|
266
|
-
return webcrypto.webcrypto.subtle
|
|
292
|
+
return _subtleVerify(webcrypto.webcrypto.subtle, params.ver, key, sig, signingInput)
|
|
267
293
|
.catch(function (e) { throw _err("webauthn/verify-error", "the metadata BLOB signature could not be evaluated under its x5c leaf key", e); });
|
|
268
294
|
}, function (e) { throw _err("webauthn/unsupported-algorithm", "the metadata BLOB x5c leaf key could not be imported for " + header.alg, e); })
|
|
269
295
|
.then(function (ok) {
|
|
@@ -278,7 +304,7 @@ function _verifyMetadataBlob(blob, opts) {
|
|
|
278
304
|
function _chainToAnchor(chain, anchors, at, what, code) {
|
|
279
305
|
var subject = what || "metadata BLOB certificate chain";
|
|
280
306
|
var faultCode = code || "webauthn/metadata-untrusted";
|
|
281
|
-
var ordered =
|
|
307
|
+
var ordered = _reverse(_arrSlice(chain));
|
|
282
308
|
var lastFault = null;
|
|
283
309
|
return anchors.reduce(function (p, anchor) {
|
|
284
310
|
return p.then(function (done) {
|
|
@@ -294,7 +320,7 @@ function _chainToAnchor(chain, anchors, at, what, code) {
|
|
|
294
320
|
parameters: anchor.subjectPublicKeyInfo.algorithm.parameters },
|
|
295
321
|
}).then(function (r) { return !!(r && r.valid); }, function (e) { lastFault = e; return false; });
|
|
296
322
|
});
|
|
297
|
-
},
|
|
323
|
+
}, _promiseResolve(_Promise, false)).then(function (trusted) {
|
|
298
324
|
if (!trusted) {
|
|
299
325
|
throw _err(faultCode, "the " + subject + " does not validate to any of the roots it must reach", lastFault);
|
|
300
326
|
}
|
|
@@ -321,8 +347,19 @@ function assertFresh(metadata, at, label) {
|
|
|
321
347
|
function _parsePayload(seg, at, opts) {
|
|
322
348
|
var payload;
|
|
323
349
|
try {
|
|
350
|
+
/** @internal The exact-integer rule applies to `no` ALONE, not to the document. `no` is the rollback
|
|
351
|
+
* counter: `_assertFresh` admits a BLOB only when its `no` exceeds the one a relying party already
|
|
352
|
+
* holds, and `1.0000000000000001` converts to the Number 1, so a stale BLOB spelled that way passed
|
|
353
|
+
* the `Number.isSafeInteger` check as the integer it is not and compared as that integer.
|
|
354
|
+
*
|
|
355
|
+
* Document-wide would refuse the real catalogue. A metadata statement is ordinary JSON and carries
|
|
356
|
+
* genuine doubles: the published BLOB has ten members whose values are not integers, among them
|
|
357
|
+
* `selfAttestedFRR` and `selfAttestedFAR`, the biometric false-reject and false-accept rates, and
|
|
358
|
+
* `iAPARThreshold`. Measured on the live BLOB from mds3.fidoalliance.org rather than read off the
|
|
359
|
+
* specification, because the catalogue is what has to be ingestible. */
|
|
324
360
|
payload = guard.json.parse(Buffer.from(jose.base64url.decode(seg)), _err, {
|
|
325
361
|
maxBytes: C.MDS_BLOB_MAX_BYTES, maxDepth: C.JSON_MAX_DEPTH,
|
|
362
|
+
exactIntegerMembers: EXACT_INTEGER_BLOB_MEMBERS,
|
|
326
363
|
tooLarge: "webauthn/too-large", badJson: "webauthn/bad-metadata-blob",
|
|
327
364
|
tooDeep: "webauthn/bad-metadata-blob", duplicateMember: "webauthn/bad-metadata-blob",
|
|
328
365
|
badInput: "webauthn/bad-metadata-blob", label: "the metadata BLOB payload",
|
package/lib/webauthn.js
CHANGED
|
@@ -36,6 +36,18 @@ var edwardsPoint = require("./edwards-point");
|
|
|
36
36
|
var guard = require("./guard-all");
|
|
37
37
|
var intrinsic = require("./guard-intrinsic");
|
|
38
38
|
var _hasOwn = intrinsic.hasOwn;
|
|
39
|
+
var _reverse = intrinsic.reverse;
|
|
40
|
+
var _arrSlice = intrinsic.arraySlice;
|
|
41
|
+
/** @internal `resolve` and `reject` build their promise from the RECEIVER, so reading the global
|
|
42
|
+
* binding at the call hands the construction to whatever that binding holds. Two shapes here decide a
|
|
43
|
+
* verdict: the seeds the attestation trust-path reductions fold over, where a replacement settling
|
|
44
|
+
* without awaiting the chain emits a verdict for a path that was never walked, and the refusals
|
|
45
|
+
* returned as rejected promises, where a replacement whose `reject` resolves turns an unsupported
|
|
46
|
+
* format or a missing attested credential into a pass. The constructor and both operations are taken
|
|
47
|
+
* at load. */
|
|
48
|
+
var _Promise = intrinsic.Promise;
|
|
49
|
+
var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
50
|
+
var _promiseReject = intrinsic.uncurry(intrinsic.promiseReject);
|
|
39
51
|
var jose = require("./jose");
|
|
40
52
|
var mds = require("./webauthn-mds");
|
|
41
53
|
var nodeCrypto = require("crypto");
|
|
@@ -52,11 +64,17 @@ function _decodeExt(cert, oidName) {
|
|
|
52
64
|
return { critical: ext.critical, value: dec(ext.value) };
|
|
53
65
|
}
|
|
54
66
|
var subtle = webcrypto.webcrypto.subtle;
|
|
67
|
+
/** @internal The verify operation, captured at load. One answering true accepts an assertion or an
|
|
68
|
+
* attestation statement that the credential's key never signed, which is the whole verdict. */
|
|
69
|
+
var _subtleVerify = intrinsic.uncurry(subtle.verify);
|
|
55
70
|
|
|
56
71
|
function _sha(name, buf) { return nodeCrypto.createHash(name).update(buf).digest(); }
|
|
72
|
+
/** @internal The comparison goes through the captured operation. This is what decides that an
|
|
73
|
+
* attestation statement's key is the credential's key, so a replacement answering zero makes a
|
|
74
|
+
* different key match. */
|
|
57
75
|
function _ucmp(a, b) {
|
|
58
76
|
function strip(x) { var i = 0; while (i < x.length - 1 && x[i] === 0) i++; return x.subarray(i); }
|
|
59
|
-
return
|
|
77
|
+
return intrinsic.compare(strip(a), strip(b)) === 0;
|
|
60
78
|
}
|
|
61
79
|
function _isInteger(node) { return !!node && !node.constructed && node.tagClass === "universal" && node.tagNumber === asn1.TAGS.INTEGER; }
|
|
62
80
|
var COSE_ALG_HASH = intrinsic.assign(intrinsic.create(null), { "-7": "sha256", "-9": "sha256", "-257": "sha256", "-37": "sha256", "-35": "sha384", "-51": "sha384", "-258": "sha384", "-38": "sha384", "-36": "sha512", "-52": "sha512", "-259": "sha512", "-39": "sha512", "-65535": "sha1" });
|
|
@@ -183,7 +201,7 @@ function _verifySig(alg, sig, spkiBytes, message, E) {
|
|
|
183
201
|
if (imp.name === "Ed25519" || imp.name === "Ed448") _requireValidEdPoint(spkiBytes, imp.name);
|
|
184
202
|
var s = d.ecdsa ? _derEcdsaToRaw(sig, d.imp.namedCurve) : sig;
|
|
185
203
|
return subtle.importKey("spki", spkiBytes, imp, false, ["verify"])
|
|
186
|
-
.then(function (key) { return subtle
|
|
204
|
+
.then(function (key) { return _subtleVerify(subtle, ver, key, s, message); })
|
|
187
205
|
.catch(function (e) { throw _err("webauthn/verify-error", "the attestation signature could not be evaluated", e); });
|
|
188
206
|
}
|
|
189
207
|
var ED_OID_NAME = intrinsic.create(null);
|
|
@@ -406,10 +424,10 @@ function _applyCallerRoots(res, supplied, vopts, onlyPaths) {
|
|
|
406
424
|
return paths.reduce(function (p, info) {
|
|
407
425
|
return p.then(function () {
|
|
408
426
|
var pathAt = vopts.time !== undefined ? vopts.time : (info.at || at);
|
|
409
|
-
return mds.chainToAnchor(info.tp
|
|
427
|
+
return mds.chainToAnchor(_reverse(_arrSlice(info.tp)), roots, pathAt,
|
|
410
428
|
"attestation trust path (against the roots supplied as opts.rootCertificates)");
|
|
411
429
|
});
|
|
412
|
-
},
|
|
430
|
+
}, _promiseResolve(_Promise, undefined)).then(function () {
|
|
413
431
|
return guard.verdict.of(res, { anchoredTo: _anchoredRoutes(res, "rootCertificates") });
|
|
414
432
|
});
|
|
415
433
|
}
|
|
@@ -558,7 +576,7 @@ var VERIFIERS = intrinsic.assign(intrinsic.create(null), {
|
|
|
558
576
|
var embedded = _appleNonce(ext.value);
|
|
559
577
|
if (!embedded.equals(nonce)) throw _err("webauthn/verify-failed", "the apple attestation nonce does not equal SHA-256(authData || clientDataHash)");
|
|
560
578
|
_certPubKeyEqualsCose(leaf, att.authData.credentialPublicKey, _err);
|
|
561
|
-
return
|
|
579
|
+
return _promiseResolve(_Promise, _result("apple", "AnonCA", chain, att));
|
|
562
580
|
},
|
|
563
581
|
|
|
564
582
|
"android-key": function (att, clientDataHash) {
|
|
@@ -706,12 +724,12 @@ var VERIFIERS = intrinsic.assign(intrinsic.create(null), {
|
|
|
706
724
|
var out = [];
|
|
707
725
|
return elements.reduce(function (p, e) {
|
|
708
726
|
return p.then(function () {
|
|
709
|
-
return
|
|
727
|
+
return _promiseResolve(_Promise, undefined).then(function () { return VERIFIERS[e.fmt](e.att, clientDataHash, opts); })
|
|
710
728
|
.then(function (r) { guard.list.append(out, r); }, function (err) {
|
|
711
729
|
throw _err("webauthn/compound-element-failed", "compound element " + e.index + " (format '" + e.fmt + "') did not verify", err);
|
|
712
730
|
});
|
|
713
731
|
});
|
|
714
|
-
},
|
|
732
|
+
}, _promiseResolve(_Promise, undefined)).then(function () {
|
|
715
733
|
return guard.verdict.of(_result("compound", "Compound", [], att), { compound: out });
|
|
716
734
|
});
|
|
717
735
|
},
|
|
@@ -720,7 +738,7 @@ var VERIFIERS = intrinsic.assign(intrinsic.create(null), {
|
|
|
720
738
|
if (att.attStmt.children.length !== 0) {
|
|
721
739
|
throw _err("webauthn/bad-att-stmt", "the none attestation statement MUST be an empty map (WebAuthn 8.7)");
|
|
722
740
|
}
|
|
723
|
-
return
|
|
741
|
+
return _promiseResolve(_Promise, _result("none", "None", [], att));
|
|
724
742
|
},
|
|
725
743
|
});
|
|
726
744
|
|
|
@@ -750,7 +768,7 @@ function _safetyNetChainTrusted(chain, roots, time) {
|
|
|
750
768
|
} catch (e) { throw _err("webauthn/bad-input", label + " is not a decodable certificate", e); }
|
|
751
769
|
}, _err, "webauthn/bad-input", label);
|
|
752
770
|
});
|
|
753
|
-
} catch (e) { return
|
|
771
|
+
} catch (e) { return _promiseReject(_Promise, e); }
|
|
754
772
|
return mds.chainToAnchor(chain, anchors, time === undefined ? new Date() : time,
|
|
755
773
|
"android-safetynet x5c certificate chain", "webauthn/safetynet-cert-untrusted");
|
|
756
774
|
}
|
|
@@ -758,7 +776,7 @@ function _safetyNetChainTrusted(chain, roots, time) {
|
|
|
758
776
|
function _result(fmt, attestationType, chain, att) {
|
|
759
777
|
return guard.verdict.of({
|
|
760
778
|
valid: true, attestationVerified: true, fmt: fmt, attestationType: attestationType,
|
|
761
|
-
trustPath: (chain || [])
|
|
779
|
+
trustPath: _reverse(_arrSlice(chain || [])),
|
|
762
780
|
aaguid: att.authData.aaguid,
|
|
763
781
|
credentialId: att.authData.credentialId,
|
|
764
782
|
credentialPublicKey: att.authData.credentialPublicKey,
|
|
@@ -911,7 +929,7 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
911
929
|
_FORMAT_SCOPED_BOOLEAN_OPTS.forEach(function (k) {
|
|
912
930
|
if (opts[k] !== undefined && typeof opts[k] !== "boolean") throw _err("webauthn/bad-input", "opts." + k + " must be a boolean");
|
|
913
931
|
});
|
|
914
|
-
} catch (e) { return
|
|
932
|
+
} catch (e) { return _promiseReject(_Promise, e); }
|
|
915
933
|
var att;
|
|
916
934
|
var attBytes, cdh, clientData = null;
|
|
917
935
|
try {
|
|
@@ -939,20 +957,20 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
939
957
|
cdh = _bytesArg(clientDataHash, "clientDataHash");
|
|
940
958
|
if (cdh.length !== 32) throw _err("webauthn/bad-input", "clientDataHash must be a 32-byte SHA-256 digest");
|
|
941
959
|
}
|
|
942
|
-
} catch (e) { return
|
|
960
|
+
} catch (e) { return _promiseReject(_Promise, e); }
|
|
943
961
|
attestationObject = attBytes;
|
|
944
962
|
clientDataHash = cdh;
|
|
945
|
-
try { att = parseAttestationObject(attestationObject); } catch (e) { return
|
|
963
|
+
try { att = parseAttestationObject(attestationObject); } catch (e) { return _promiseReject(_Promise, e); }
|
|
946
964
|
if (!att.authData.flags.at || !att.authData.credentialPublicKey) {
|
|
947
|
-
return
|
|
965
|
+
return _promiseReject(_Promise, _err("webauthn/bad-auth-data", "attestation requires attestedCredentialData (the AT flag must be set)"));
|
|
948
966
|
}
|
|
949
967
|
var verifier = VERIFIERS[att.fmt];
|
|
950
|
-
if (!verifier) return
|
|
968
|
+
if (!verifier) return _promiseReject(_Promise, _err("webauthn/unsupported-format", "attestation statement format '" + att.fmt + "' is not supported"));
|
|
951
969
|
if (opts.tpmPolicy !== undefined && !_formatCarries(att, "tpm")) {
|
|
952
|
-
return
|
|
970
|
+
return _promiseReject(_Promise, _err("webauthn/tpm-policy", "opts.tpmPolicy requires a TPM attestation, but this attestation is format '" + att.fmt + "', which carries no TPM public area"));
|
|
953
971
|
}
|
|
954
972
|
if (opts.requireCtsProfileMatch === true && !_formatCarries(att, "android-safetynet")) {
|
|
955
|
-
return
|
|
973
|
+
return _promiseReject(_Promise, _err("webauthn/safetynet-cts-profile", "opts.requireCtsProfileMatch requires an android-safetynet attestation, but this attestation is format '" + att.fmt + "', whose statement carries no device-integrity signals"));
|
|
956
974
|
}
|
|
957
975
|
var bindingChecked, vopts;
|
|
958
976
|
try {
|
|
@@ -960,8 +978,8 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
960
978
|
vopts = guard.identifier.ownOptions(opts);
|
|
961
979
|
vopts.rootCertificates = _snapshotRoots(opts.rootCertificates);
|
|
962
980
|
vopts.safetyNetRoots = _snapshotRoots(opts.safetyNetRoots);
|
|
963
|
-
} catch (e) { return
|
|
964
|
-
return
|
|
981
|
+
} catch (e) { return _promiseReject(_Promise, e); }
|
|
982
|
+
return _promiseResolve(_Promise, undefined).then(function () { return verifier(att, clientDataHash, vopts); })
|
|
965
983
|
.then(function (res) {
|
|
966
984
|
return guard.verdict.of(res, { clientData: clientData });
|
|
967
985
|
})
|
|
@@ -977,10 +995,10 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
977
995
|
}
|
|
978
996
|
return anchored;
|
|
979
997
|
}
|
|
980
|
-
return
|
|
998
|
+
return _promiseResolve(_Promise, undefined).then(function () { return _applyMetadata(res, att, vopts); })
|
|
981
999
|
.then(function (out) { return guard.verdict.of(out, { anchoredTo: _anchoredRoutes(out, "metadata") }); }, function (e) {
|
|
982
1000
|
if (!e || e.code !== "webauthn/metadata-not-found" || vopts.rootCertificates === undefined) throw e;
|
|
983
|
-
return
|
|
1001
|
+
return _promiseResolve(_Promise, _applyCallerRoots(res, vopts.rootCertificates, vopts, e.missedPaths))
|
|
984
1002
|
.then(function (withRoots) {
|
|
985
1003
|
/** @internal The element coverage the metadata attempt measured is the whole
|
|
986
1004
|
* statement's, so it travels on the error rather than being recomputed from the
|
|
@@ -1060,10 +1078,10 @@ function _applyMetadata(res, att, opts) {
|
|
|
1060
1078
|
return p.then(function () {
|
|
1061
1079
|
var g = item.g, info = item.info;
|
|
1062
1080
|
var pathAt = opts.time !== undefined ? opts.time : (info.at || at);
|
|
1063
|
-
return mds.chainToAnchor(info.tp
|
|
1081
|
+
return mds.chainToAnchor(_reverse(_arrSlice(info.tp)), g.anchors, pathAt,
|
|
1064
1082
|
"attestation trust path for " + g.identifier + " (against the roots its metadata entry registers)");
|
|
1065
1083
|
});
|
|
1066
|
-
},
|
|
1084
|
+
}, _promiseResolve(_Promise, undefined))
|
|
1067
1085
|
.then(function () {
|
|
1068
1086
|
if (missed) throw missed;
|
|
1069
1087
|
var primary = applied[0];
|
|
@@ -1504,8 +1522,8 @@ function _snapshotAssertion(input) {
|
|
|
1504
1522
|
|
|
1505
1523
|
function verifyAssertion(input) {
|
|
1506
1524
|
var frozen;
|
|
1507
|
-
try { frozen = _snapshotAssertion(input); } catch (e) { return
|
|
1508
|
-
return
|
|
1525
|
+
try { frozen = _snapshotAssertion(input); } catch (e) { return _promiseReject(_Promise, e); }
|
|
1526
|
+
return _promiseResolve(_Promise, undefined).then(function () {
|
|
1509
1527
|
input = frozen;
|
|
1510
1528
|
var authData = parseAuthenticatorData(input.authenticatorData);
|
|
1511
1529
|
input.signature = _bytesArg(input.signature, "signature");
|
package/lib/webcrypto.js
CHANGED
|
@@ -92,6 +92,12 @@ var _bigIntToString = intrinsic.bigIntToString;
|
|
|
92
92
|
var _push = intrinsic.push;
|
|
93
93
|
var _concat = intrinsic.concat;
|
|
94
94
|
var _resolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
95
|
+
/** @internal The promise constructor, captured at load. `promiseResolve` builds its promise from the
|
|
96
|
+
* RECEIVER, so reading the global binding at the call handed that construction over: measured, a
|
|
97
|
+
* replacement whose constructor filled the message buffer made `signOverTbs` sign rewritten bytes
|
|
98
|
+
* through the key-adoption path below, and the signature then verified against the rewritten message
|
|
99
|
+
* and failed against the caller's. */
|
|
100
|
+
var _Promise = intrinsic.Promise;
|
|
95
101
|
var _types = intrinsic.types;
|
|
96
102
|
|
|
97
103
|
var WebCryptoError = frameworkError.defineClass("WebCryptoError", { withCause: true });
|
|
@@ -104,7 +110,7 @@ function _toBuf(data, who) {
|
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
function _toArrayBuffer(buf) {
|
|
107
|
-
return
|
|
113
|
+
return guard.bytes.toArrayBuffer(buf);
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
function _wcErr(code, msg) { return new WebCryptoError(code, msg); }
|
|
@@ -352,12 +358,12 @@ SubtleCrypto.prototype.generateKey = async function generateKey(algorithm, extra
|
|
|
352
358
|
if (name === "AES-GCM" || name === "AES-CBC" || name === "AES-CTR" || name === "AES-KW") {
|
|
353
359
|
var bits = alg.length;
|
|
354
360
|
if (bits !== 128 && bits !== 192 && bits !== 256) throw new WebCryptoError("webcrypto/syntax", name + ": length must be 128/192/256");
|
|
355
|
-
var secret =
|
|
361
|
+
var secret = guard.crypto.secretKey(nodeCrypto.randomBytes(bits / 8));
|
|
356
362
|
return new CryptoKey("secret", extractable, { name: name, length: bits }, usages, secret);
|
|
357
363
|
}
|
|
358
364
|
if (name === "HMAC") {
|
|
359
365
|
var lenBits = _hmacLengthBits(alg, "generateKey HMAC");
|
|
360
|
-
var hkey =
|
|
366
|
+
var hkey = guard.crypto.secretKey(nodeCrypto.randomBytes(lenBits / 8));
|
|
361
367
|
return new CryptoKey("secret", extractable, { name: name, hash: { name: (typeof alg.hash === "string" ? alg.hash : alg.hash.name) }, length: lenBits }, usages, hkey);
|
|
362
368
|
}
|
|
363
369
|
|
|
@@ -430,27 +436,25 @@ SubtleCrypto.prototype.sign = async function sign(algorithm, key, data) {
|
|
|
430
436
|
if (!SIGN_VERIFY_NAMES[name]) throw new WebCryptoError("webcrypto/not-supported", "sign: unsupported algorithm " + _stringify(name));
|
|
431
437
|
_requireAlgMatch(alg, key, "sign");
|
|
432
438
|
if (name === "RSASSA-PKCS1-V1_5") {
|
|
433
|
-
return _toArrayBuffer(
|
|
439
|
+
return _toArrayBuffer(guard.crypto.sign(_hashNode(key.algorithm.hash, "sign"), buf, key._handle));
|
|
434
440
|
}
|
|
435
441
|
if (name === "RSA-PSS") {
|
|
436
|
-
return _toArrayBuffer(
|
|
437
|
-
key: key._handle, padding:
|
|
438
|
-
saltLength: (typeof alg.saltLength === "number" ? alg.saltLength :
|
|
442
|
+
return _toArrayBuffer(guard.crypto.sign(_hashNode(key.algorithm.hash, "sign"), buf, {
|
|
443
|
+
key: key._handle, padding: guard.crypto.padding.RSA_PKCS1_PSS,
|
|
444
|
+
saltLength: (typeof alg.saltLength === "number" ? alg.saltLength : guard.crypto.saltLength.DIGEST),
|
|
439
445
|
}));
|
|
440
446
|
}
|
|
441
447
|
if (name === "ECDSA") {
|
|
442
|
-
return _toArrayBuffer(
|
|
448
|
+
return _toArrayBuffer(guard.crypto.sign(_hashNode(alg.hash, "sign"), buf, { key: key._handle, dsaEncoding: "ieee-p1363" }));
|
|
443
449
|
}
|
|
444
450
|
if (ML_DSA_NODE[name]) {
|
|
445
|
-
return _toArrayBuffer(
|
|
451
|
+
return _toArrayBuffer(guard.crypto.sign(null, buf, _mldsaKeyArg(alg, key._handle, "sign")));
|
|
446
452
|
}
|
|
447
453
|
if (name === "ED25519" || name === "ED448" || SLH_DSA_NODE[name]) {
|
|
448
|
-
return _toArrayBuffer(
|
|
454
|
+
return _toArrayBuffer(guard.crypto.sign(null, buf, key._handle));
|
|
449
455
|
}
|
|
450
456
|
if (name === "HMAC") {
|
|
451
|
-
|
|
452
|
-
hm.update(buf);
|
|
453
|
-
return _toArrayBuffer(hm.digest());
|
|
457
|
+
return _toArrayBuffer(guard.crypto.hmac(_hashNode(key.algorithm.hash, "sign"), key._handle, buf));
|
|
454
458
|
}
|
|
455
459
|
throw new WebCryptoError("webcrypto/not-supported", "sign: unsupported algorithm " + _stringify(name));
|
|
456
460
|
};
|
|
@@ -464,27 +468,25 @@ SubtleCrypto.prototype.verify = async function verify(algorithm, key, signature,
|
|
|
464
468
|
if (!SIGN_VERIFY_NAMES[name]) throw new WebCryptoError("webcrypto/not-supported", "verify: unsupported algorithm " + _stringify(name));
|
|
465
469
|
_requireAlgMatch(alg, key, "verify");
|
|
466
470
|
if (name === "RSASSA-PKCS1-V1_5") {
|
|
467
|
-
return
|
|
471
|
+
return guard.crypto.verify(_hashNode(key.algorithm.hash, "verify"), buf, key._handle, sig);
|
|
468
472
|
}
|
|
469
473
|
if (name === "RSA-PSS") {
|
|
470
|
-
return
|
|
471
|
-
key: key._handle, padding:
|
|
472
|
-
saltLength: (typeof alg.saltLength === "number" ? alg.saltLength :
|
|
474
|
+
return guard.crypto.verify(_hashNode(key.algorithm.hash, "verify"), buf, {
|
|
475
|
+
key: key._handle, padding: guard.crypto.padding.RSA_PKCS1_PSS,
|
|
476
|
+
saltLength: (typeof alg.saltLength === "number" ? alg.saltLength : guard.crypto.saltLength.DIGEST),
|
|
473
477
|
}, sig);
|
|
474
478
|
}
|
|
475
479
|
if (name === "ECDSA") {
|
|
476
|
-
return
|
|
480
|
+
return guard.crypto.verify(_hashNode(alg.hash, "verify"), buf, { key: key._handle, dsaEncoding: "ieee-p1363" }, sig);
|
|
477
481
|
}
|
|
478
482
|
if (ML_DSA_NODE[name]) {
|
|
479
|
-
return
|
|
483
|
+
return guard.crypto.verify(null, buf, _mldsaKeyArg(alg, key._handle, "verify"), sig);
|
|
480
484
|
}
|
|
481
485
|
if (name === "ED25519" || name === "ED448" || SLH_DSA_NODE[name]) {
|
|
482
|
-
return
|
|
486
|
+
return guard.crypto.verify(null, buf, key._handle, sig);
|
|
483
487
|
}
|
|
484
488
|
if (name === "HMAC") {
|
|
485
|
-
var
|
|
486
|
-
hm.update(buf);
|
|
487
|
-
var digest = hm.digest();
|
|
489
|
+
var digest = guard.crypto.hmac(_hashNode(key.algorithm.hash, "verify"), key._handle, buf);
|
|
488
490
|
return guard.crypto.constantTimeEqual(digest, sig);
|
|
489
491
|
}
|
|
490
492
|
throw new WebCryptoError("webcrypto/not-supported", "verify: unsupported algorithm " + _stringify(name));
|
|
@@ -504,7 +506,7 @@ SubtleCrypto.prototype.encrypt = async function encrypt(algorithm, key, data) {
|
|
|
504
506
|
_requireAlgMatch(alg, key, "encrypt");
|
|
505
507
|
if (name === "RSA-OAEP") {
|
|
506
508
|
return _toArrayBuffer(nodeCrypto.publicEncrypt({
|
|
507
|
-
key: key._handle, padding:
|
|
509
|
+
key: key._handle, padding: guard.crypto.padding.RSA_PKCS1_OAEP,
|
|
508
510
|
oaepHash: _hashNode(key.algorithm.hash, "encrypt"),
|
|
509
511
|
oaepLabel: alg.label ? _toBuf(alg.label, "encrypt label") : undefined,
|
|
510
512
|
}, buf));
|
|
@@ -552,7 +554,7 @@ SubtleCrypto.prototype.decrypt = async function decrypt(algorithm, key, data) {
|
|
|
552
554
|
_requireAlgMatch(alg, key, "decrypt");
|
|
553
555
|
if (name === "RSA-OAEP") {
|
|
554
556
|
var oaepOut = nodeCrypto.privateDecrypt({
|
|
555
|
-
key: key._handle, padding:
|
|
557
|
+
key: key._handle, padding: guard.crypto.padding.RSA_PKCS1_OAEP,
|
|
556
558
|
oaepHash: _hashNode(key.algorithm.hash, "decrypt"),
|
|
557
559
|
oaepLabel: alg.label ? _toBuf(alg.label, "decrypt label") : undefined,
|
|
558
560
|
}, buf);
|
|
@@ -598,7 +600,7 @@ SubtleCrypto.prototype.decrypt = async function decrypt(algorithm, key, data) {
|
|
|
598
600
|
};
|
|
599
601
|
|
|
600
602
|
function _withSecretBytes(key, fn) {
|
|
601
|
-
var kb = key._handle
|
|
603
|
+
var kb = guard.crypto.exportKey(key._handle);
|
|
602
604
|
var deferred = false;
|
|
603
605
|
try {
|
|
604
606
|
var out = fn(kb);
|
|
@@ -629,7 +631,7 @@ var _PBKDF2_MAX_CONCURRENT = _max(1, (_parseInt(process.env.UV_THREADPOOL_SIZE,
|
|
|
629
631
|
var _pbkdf2InFlight = 0;
|
|
630
632
|
var _pbkdf2Waiters = [];
|
|
631
633
|
function _pbkdf2Async(pw, salt, iterations, keylen, digest) {
|
|
632
|
-
return new
|
|
634
|
+
return new _Promise(function (resolve, reject) {
|
|
633
635
|
function start() {
|
|
634
636
|
_pbkdf2InFlight++;
|
|
635
637
|
function done(err, derived) {
|
|
@@ -932,7 +934,7 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
|
|
|
932
934
|
if (SECRET_KEY_NAMES[name]) {
|
|
933
935
|
var raw = _toBuf(keyData, "importKey raw");
|
|
934
936
|
_assertAesImportLen(name, raw.length);
|
|
935
|
-
var secret =
|
|
937
|
+
var secret = guard.crypto.secretKey(raw);
|
|
936
938
|
var symAlg = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash, "importKey raw HMAC"), length: raw.length * 8 } : { name: name, length: raw.length * 8 };
|
|
937
939
|
return new CryptoKey("secret", extractable, symAlg, usages, secret);
|
|
938
940
|
}
|
|
@@ -942,13 +944,13 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
|
|
|
942
944
|
if (format === "spki") {
|
|
943
945
|
var spkiBuf = _toBuf(keyData, "importKey spki");
|
|
944
946
|
_preValidateSpki(spkiBuf, name);
|
|
945
|
-
var pub = _nodeKey(function () { return
|
|
947
|
+
var pub = _nodeKey(function () { return guard.crypto.publicKey({ key: spkiBuf, format: "der", type: "spki" }); }, "importKey spki");
|
|
946
948
|
return new CryptoKey("public", true, _algFromImport(name, alg, pub), usages, pub);
|
|
947
949
|
}
|
|
948
950
|
if (format === "pkcs8") {
|
|
949
951
|
var p8Buf = _toBuf(keyData, "importKey pkcs8");
|
|
950
952
|
_preValidatePkcs8(p8Buf, name);
|
|
951
|
-
var priv = _nodeKey(function () { return
|
|
953
|
+
var priv = _nodeKey(function () { return guard.crypto.privateKey({ key: p8Buf, format: "der", type: "pkcs8" }); }, "importKey pkcs8");
|
|
952
954
|
return new CryptoKey("private", extractable, _algFromImport(name, alg, priv), usages, priv);
|
|
953
955
|
}
|
|
954
956
|
if (format === "jwk") {
|
|
@@ -959,13 +961,13 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
|
|
|
959
961
|
if (jwk.kty === "oct") {
|
|
960
962
|
var kbuf = _b64urlToBuf(jwk.k, "JWK oct key material");
|
|
961
963
|
_assertAesImportLen(name, kbuf.length);
|
|
962
|
-
var s2 =
|
|
964
|
+
var s2 = guard.crypto.secretKey(kbuf);
|
|
963
965
|
var a2 = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash, "importKey jwk HMAC"), length: kbuf.length * 8 } : { name: name, length: kbuf.length * 8 };
|
|
964
966
|
return new CryptoKey("secret", extractable, a2, usages, s2);
|
|
965
967
|
}
|
|
966
968
|
var isPrivate = _hasOwn(jwk, "d") ||
|
|
967
969
|
(jwk.kty === "AKP" && _hasOwn(jwk, "priv"));
|
|
968
|
-
var ko = _nodeKey(function () { return isPrivate ?
|
|
970
|
+
var ko = _nodeKey(function () { return isPrivate ? guard.crypto.privateKey({ key: jwk, format: "jwk" }) : guard.crypto.publicKey({ key: jwk, format: "jwk" }); }, "importKey jwk");
|
|
969
971
|
return new CryptoKey(isPrivate ? "private" : "public", isPrivate ? extractable : true, _algFromImport(name, alg, ko), usages, ko);
|
|
970
972
|
}
|
|
971
973
|
throw new WebCryptoError("webcrypto/not-supported", "importKey: unsupported format " + _stringify(format));
|
|
@@ -974,14 +976,14 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
|
|
|
974
976
|
function _importRawPublic(name, alg, raw, extractable, usages) {
|
|
975
977
|
if (name === "ED25519" || name === "ED448" || name === "X25519" || name === "X448") {
|
|
976
978
|
var jwk = { kty: "OKP", crv: (name === "ED25519" ? "Ed25519" : name === "ED448" ? "Ed448" : name === "X25519" ? "X25519" : "X448"), x: _bufToB64url(raw) };
|
|
977
|
-
var ko =
|
|
979
|
+
var ko = guard.crypto.publicKey({ key: jwk, format: "jwk" });
|
|
978
980
|
return new CryptoKey("public", true, { name: _stdName(name) }, usages, ko);
|
|
979
981
|
}
|
|
980
982
|
if (name === "ECDSA" || name === "ECDH") {
|
|
981
983
|
var fb = CURVE_FIELD_BYTES[alg.namedCurve];
|
|
982
984
|
if (!fb || raw[0] !== 0x04 || raw.length !== 1 + 2 * fb) throw new WebCryptoError("webcrypto/data", "importKey raw EC: expected an uncompressed point for " + alg.namedCurve);
|
|
983
985
|
var ecjwk = { kty: "EC", crv: alg.namedCurve, x: _bufToB64url(_subarray(raw, 1, 1 + fb)), y: _bufToB64url(_subarray(raw, 1 + fb)) };
|
|
984
|
-
var eck =
|
|
986
|
+
var eck = guard.crypto.publicKey({ key: ecjwk, format: "jwk" });
|
|
985
987
|
return new CryptoKey("public", true, { name: name, namedCurve: alg.namedCurve }, usages, eck);
|
|
986
988
|
}
|
|
987
989
|
throw new WebCryptoError("webcrypto/not-supported", "importKey raw: unsupported public-key algorithm " + _stringify(name));
|
|
@@ -1028,7 +1030,7 @@ function _algFromImport(name, alg, keyObject) {
|
|
|
1028
1030
|
|
|
1029
1031
|
function _curveFromKey(ko) {
|
|
1030
1032
|
try {
|
|
1031
|
-
var jwk =
|
|
1033
|
+
var jwk = guard.crypto.exportKey(ko, { format: "jwk" });
|
|
1032
1034
|
var curves = guard.identifier.optionNames(CURVE_NODE);
|
|
1033
1035
|
for (var k = 0; k < curves.length; k++) { if (jwk.crv === curves[k]) return curves[k]; }
|
|
1034
1036
|
}
|
|
@@ -1066,15 +1068,15 @@ function _curveFromKey(ko) {
|
|
|
1066
1068
|
SubtleCrypto.prototype.exportKey = async function exportKey(format, key) {
|
|
1067
1069
|
_requireOwnKey(key, "exportKey");
|
|
1068
1070
|
if (!key.extractable) throw new WebCryptoError("webcrypto/invalid-access", "key is not extractable");
|
|
1069
|
-
if (format === "jwk") return key._handle
|
|
1071
|
+
if (format === "jwk") return guard.crypto.exportKey(key._handle, { format: "jwk" });
|
|
1070
1072
|
if (key.type === "secret") {
|
|
1071
1073
|
return _withSecretBytes(key, function (raw) {
|
|
1072
1074
|
if (format === "raw") return _toArrayBuffer(raw);
|
|
1073
1075
|
throw new WebCryptoError("webcrypto/not-supported", "exportKey: secret keys support 'raw' / 'jwk' only");
|
|
1074
1076
|
});
|
|
1075
1077
|
}
|
|
1076
|
-
if (format === "spki") return _toArrayBuffer(key._handle
|
|
1077
|
-
if (format === "pkcs8") return _toArrayBuffer(key._handle
|
|
1078
|
+
if (format === "spki") return _toArrayBuffer(guard.crypto.exportKey(key._handle, { format: "der", type: "spki" }));
|
|
1079
|
+
if (format === "pkcs8") return _toArrayBuffer(guard.crypto.exportKey(key._handle, { format: "der", type: "pkcs8" }));
|
|
1078
1080
|
if (format === "raw") {
|
|
1079
1081
|
if (key.type !== "public") {
|
|
1080
1082
|
throw new WebCryptoError("webcrypto/not-supported",
|
|
@@ -1087,7 +1089,7 @@ SubtleCrypto.prototype.exportKey = async function exportKey(format, key) {
|
|
|
1087
1089
|
};
|
|
1088
1090
|
|
|
1089
1091
|
function _rawPublic(key) {
|
|
1090
|
-
var jwk = key._handle
|
|
1092
|
+
var jwk = guard.crypto.exportKey(key._handle, { format: "jwk" });
|
|
1091
1093
|
if (jwk.kty === "OKP") return _b64urlToBuf(jwk.x);
|
|
1092
1094
|
if (jwk.kty === "EC") return _bufferConcat([_bufferFrom([0x04]), _b64urlToBuf(jwk.x), _b64urlToBuf(jwk.y)]);
|
|
1093
1095
|
throw new WebCryptoError("webcrypto/not-supported", "exportKey raw: unsupported key type " + jwk.kty);
|
|
@@ -1163,7 +1165,7 @@ function exportAnyKey(key, E, code, want) {
|
|
|
1163
1165
|
var asJwk = format === "jwk";
|
|
1164
1166
|
function wrap(b) { return asJwk ? b : _bufferFrom(b); }
|
|
1165
1167
|
if (key instanceof CryptoKey) {
|
|
1166
|
-
return _resolve(
|
|
1168
|
+
return _resolve(_Promise)
|
|
1167
1169
|
.then(function () { return _crypto.subtle.exportKey(format, key); })
|
|
1168
1170
|
.then(wrap);
|
|
1169
1171
|
}
|
|
@@ -1172,14 +1174,14 @@ function exportAnyKey(key, E, code, want) {
|
|
|
1172
1174
|
}
|
|
1173
1175
|
var handle = key._handle;
|
|
1174
1176
|
if (handle instanceof nodeCrypto.KeyObject && handle.type === key.type) {
|
|
1175
|
-
return _resolve(
|
|
1176
|
-
if (asJwk) return
|
|
1177
|
-
return format === "raw" ?
|
|
1177
|
+
return _resolve(_Promise).then(function () {
|
|
1178
|
+
if (asJwk) return guard.crypto.exportKey(handle, { format: "jwk" });
|
|
1179
|
+
return format === "raw" ? guard.crypto.exportKey(handle) : guard.crypto.exportKey(handle, { format: "der", type: format });
|
|
1178
1180
|
}).then(wrap, function (e) {
|
|
1179
1181
|
throw E(code, "the CryptoKey comes from a different copy of this WebCrypto engine and its key material could not be read; import it through pki.webcrypto.subtle, or pass the key as DER", e);
|
|
1180
1182
|
});
|
|
1181
1183
|
}
|
|
1182
|
-
return _resolve(
|
|
1184
|
+
return _resolve(_Promise)
|
|
1183
1185
|
.then(function () { return nodeCrypto.webcrypto.subtle.exportKey(format, key); })
|
|
1184
1186
|
.then(wrap, function (e) {
|
|
1185
1187
|
throw E(code, "the CryptoKey comes from a different WebCrypto implementation and could not be exported for re-import; import it through pki.webcrypto.subtle, or pass the key as DER", e);
|
|
@@ -1188,14 +1190,14 @@ function exportAnyKey(key, E, code, want) {
|
|
|
1188
1190
|
|
|
1189
1191
|
// @internal
|
|
1190
1192
|
function adoptKey(key, importParams, usages, E, code) {
|
|
1191
|
-
if (key instanceof CryptoKey) return _resolve(
|
|
1193
|
+
if (key instanceof CryptoKey) return _resolve(_Promise, key);
|
|
1192
1194
|
if (!_isArray(key.usages)) throw E(code, "a CryptoKey carrying its permitted usages is required");
|
|
1193
1195
|
for (var i = 0; i < usages.length; i++) {
|
|
1194
1196
|
if (_indexOf(key.usages, usages[i]) === -1) {
|
|
1195
1197
|
throw E(code, "the CryptoKey is not permitted for '" + usages[i] + "' (usages: " + _join(key.usages, ",") + ")");
|
|
1196
1198
|
}
|
|
1197
1199
|
}
|
|
1198
|
-
return _resolve(
|
|
1200
|
+
return _resolve(_Promise)
|
|
1199
1201
|
.then(function () { return exportAnyKey(key, E, code); })
|
|
1200
1202
|
.then(function (der) {
|
|
1201
1203
|
return _crypto.subtle.importKey(_EXPORT_FORMAT[key.type], der, importParams || key.algorithm, false, usages);
|