@blamejs/pki 0.8.11 → 0.8.14
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 +47 -0
- package/README.md +3 -2
- package/index.js +6 -0
- package/lib/acme.js +13 -5
- package/lib/attrcert-sign.js +3 -3
- package/lib/cmc-build.js +9 -9
- package/lib/cmc-verify.js +5 -5
- package/lib/cmp-build.js +3 -4
- package/lib/cmp-session.js +6 -6
- package/lib/cms-verify.js +4 -4
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/ct.js +2 -2
- package/lib/est.js +12 -13
- package/lib/framework-error.js +3 -0
- package/lib/guard-header.js +15 -1
- package/lib/guard-identifier.js +34 -0
- package/lib/hpke.js +7 -2
- package/lib/http-transport.js +5 -6
- package/lib/identity-match.js +4 -3
- package/lib/ocsp.js +5 -3
- package/lib/oid.js +1 -1
- package/lib/path-validate.js +380 -111
- package/lib/pki-build.js +1 -1
- package/lib/revocation-fetch.js +1 -1
- package/lib/scep.js +21 -9
- package/lib/schema-all.js +10 -1
- package/lib/schema-engine.js +7 -2
- package/lib/schema-pkix.js +40 -0
- package/lib/schema-trustanchor.js +469 -0
- package/lib/schema-x509.js +66 -33
- package/lib/smime.js +5 -1
- package/lib/trust.js +192 -11
- package/lib/trustanchor-build.js +278 -0
- package/lib/validator-tpm.js +1 -1
- package/lib/webauthn-mds.js +1 -1
- package/lib/webauthn.js +9 -10
- package/lib/webcrypto.js +3 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/trust.js
CHANGED
|
@@ -46,11 +46,14 @@ var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
|
46
46
|
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
47
47
|
var x509 = require("./schema-x509");
|
|
48
48
|
var pkix = require("./schema-pkix");
|
|
49
|
+
var oid = require("./oid");
|
|
50
|
+
var trustanchorSchema = require("./schema-trustanchor");
|
|
49
51
|
|
|
50
52
|
var TrustError = errors.TrustError;
|
|
51
53
|
var LIMITS = constants.LIMITS;
|
|
52
54
|
|
|
53
55
|
function E(code, message, cause) { return new TrustError(code, message, cause); }
|
|
56
|
+
var _NS = pkix.makeNS("trust", TrustError, oid);
|
|
54
57
|
|
|
55
58
|
var PURPOSES = ["serverAuth", "emailProtection", "codeSigning"];
|
|
56
59
|
|
|
@@ -351,14 +354,30 @@ function _mkAnchor(cert, meta) {
|
|
|
351
354
|
label: meta.label,
|
|
352
355
|
mozillaCaPolicy: meta.mozillaCaPolicy,
|
|
353
356
|
};
|
|
354
|
-
|
|
357
|
+
/** @internal The certificate's own basic constraints are associated with the anchor, so its
|
|
358
|
+
* pathLenConstraint bounds the path below it (RFC 5937 sec. 3.2). */
|
|
359
|
+
var certPathLen = pkix.anchorPathLenFromExtensions(cert.extensions, _NS, "the trust anchor certificate");
|
|
360
|
+
if (certPathLen !== null) {
|
|
361
|
+
var cc = intrinsic.create(null);
|
|
362
|
+
cc.pathLenConstraint = certPathLen;
|
|
363
|
+
guard.verdict.set(entry, "constraints", cc);
|
|
364
|
+
}
|
|
365
|
+
var derived = {
|
|
355
366
|
name: _copyName(cert.subject),
|
|
356
367
|
publicKey: Buffer.from(spki.bytes),
|
|
357
368
|
algorithm: spki.algorithm.oid,
|
|
358
369
|
parameters: spki.algorithm.parameters == null ? spki.algorithm.parameters : Buffer.from(spki.algorithm.parameters),
|
|
359
|
-
purposes:
|
|
370
|
+
purposes: _purposesOrNull(meta.purposes),
|
|
360
371
|
distrustAfter: _copyDistrustAfter(meta.distrustAfter),
|
|
361
|
-
}
|
|
372
|
+
};
|
|
373
|
+
/** @internal An independent copy, so editing the entry's own constraints cannot change what
|
|
374
|
+
* `anchor()` enforces; it reads this record where there is one. */
|
|
375
|
+
if (certPathLen !== null) {
|
|
376
|
+
var dc = intrinsic.create(null);
|
|
377
|
+
dc.pathLenConstraint = certPathLen;
|
|
378
|
+
derived.constraints = dc;
|
|
379
|
+
}
|
|
380
|
+
_DERIVED_FROM.set(entry, derived);
|
|
362
381
|
return entry;
|
|
363
382
|
}
|
|
364
383
|
var _DERIVED_FROM = new WeakMap();
|
|
@@ -371,27 +390,39 @@ function _copyPurposes(src) {
|
|
|
371
390
|
});
|
|
372
391
|
}
|
|
373
392
|
|
|
393
|
+
/** @internal An EXPLICIT null purposes set stays null; an absent one still defaults to all-false,
|
|
394
|
+
* which is the fail-closed reading of a store entry that named no purpose.
|
|
395
|
+
*
|
|
396
|
+
* The two differ because the formats do. A store that lists a root and grants it nothing says
|
|
397
|
+
* something, and all-false is how that is written. A format carrying no purpose bits at all, such
|
|
398
|
+
* as an RFC 5914 trust anchor, says nothing: turning that into all-false would read as "trusted
|
|
399
|
+
* for no purpose" and make `pki.path.validate` demand a purpose to check a constraint the anchor
|
|
400
|
+
* never stated. The `anchor` entry gate directly below reads a null the same way, refusing an
|
|
401
|
+
* entry whose `purposes` is `!= null` and letting a null through as absent. */
|
|
402
|
+
function _purposesOrNull(src) { return src === null ? null : _copyPurposes(src); }
|
|
403
|
+
|
|
374
404
|
function _copyDistrustAfter(src) {
|
|
375
405
|
var out = intrinsic.create(null);
|
|
376
406
|
if (!src || typeof src !== "object") return out;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
407
|
+
var names = guard.identifier.optionNames(src);
|
|
408
|
+
for (var i = 0; i < names.length; i++) {
|
|
409
|
+
out[names[i]] = guard.time.isDate(src[names[i]]) ? new Date(guard.time.instantOf(src[names[i]])) : src[names[i]];
|
|
410
|
+
}
|
|
380
411
|
return out;
|
|
381
412
|
}
|
|
382
413
|
|
|
383
414
|
function _copyName(name) {
|
|
384
415
|
if (!name || !Array.isArray(name.rdns)) return name;
|
|
385
|
-
var out =
|
|
416
|
+
var out = guard.identifier.ownOptions(name);
|
|
386
417
|
out.rdns = guard.list.copyMap(name.rdns, function (rdn) {
|
|
387
|
-
return Array.isArray(rdn) ? guard.list.copyMap(rdn, function (atv) { return
|
|
418
|
+
return Array.isArray(rdn) ? guard.list.copyMap(rdn, function (atv) { return guard.identifier.ownOptions(atv); }) : rdn;
|
|
388
419
|
});
|
|
389
420
|
out.bytes = Buffer.isBuffer(name.bytes) ? Buffer.from(name.bytes) : name.bytes;
|
|
390
421
|
return out;
|
|
391
422
|
}
|
|
392
423
|
|
|
393
424
|
function _datesEqual(x, y) {
|
|
394
|
-
var kx =
|
|
425
|
+
var kx = guard.identifier.optionNames(x).sort(), ky = guard.identifier.optionNames(y).sort();
|
|
395
426
|
if (kx.join(",") !== ky.join(",")) return false;
|
|
396
427
|
return kx.every(function (k) { return guard.time.instantOf(x[k]) === guard.time.instantOf(y[k]); });
|
|
397
428
|
}
|
|
@@ -929,7 +960,7 @@ function anchor(entry, opts) {
|
|
|
929
960
|
throw E("trust/purpose-not-trusted", "this root is not a trusted delegator for " + opts.purpose);
|
|
930
961
|
}
|
|
931
962
|
}
|
|
932
|
-
if (!derived && (entry.purposes != null || (entry.distrustAfter &&
|
|
963
|
+
if (!derived && (entry.purposes != null || (entry.distrustAfter && guard.identifier.optionNames(entry.distrustAfter).length))) {
|
|
933
964
|
throw E("trust/bad-input", "this entry carries a trust store's per-purpose metadata but is not the entry the store produced -- it has been rebuilt, and the metadata is a statement about the key the store read, not about whichever key the copy now names. Pass pki.trust.parseCertdata / parseCcadbCsv output unmodified");
|
|
934
965
|
}
|
|
935
966
|
var nameConstraints = _copyNameConstraints(opts.nameConstraints);
|
|
@@ -940,18 +971,168 @@ function anchor(entry, opts) {
|
|
|
940
971
|
parameters: Buffer.isBuffer(meta.parameters) ? Buffer.from(meta.parameters)
|
|
941
972
|
: (meta.parameters !== undefined ? meta.parameters : null),
|
|
942
973
|
distrustAfter: _copyDistrustAfter(meta.distrustAfter),
|
|
943
|
-
purposes:
|
|
974
|
+
purposes: _purposesOrNull(meta.purposes),
|
|
944
975
|
};
|
|
945
976
|
/** @internal Set only when an overlay was supplied, so an anchor without one is the object this
|
|
946
977
|
* verb returned before the option existed, and defined rather than assigned: an accessor planted
|
|
947
978
|
* on Object.prototype under this name would otherwise take the assignment and leave the anchor
|
|
948
979
|
* carrying no restriction at all. */
|
|
949
980
|
if (nameConstraints) guard.verdict.set(out, "nameConstraints", nameConstraints);
|
|
981
|
+
/** @internal The RFC 5914 anchor's own constraints, kept separate from the `nameConstraints`
|
|
982
|
+
* overlay a caller may also supply: RFC 5937 sec. 3.2 intersects every permitting set rather than
|
|
983
|
+
* letting one replace another, so the two have to reach the validator as two statements.
|
|
984
|
+
*
|
|
985
|
+
* Read from `meta`, which is the derived record when there is one and the caller's entry when
|
|
986
|
+
* there is not, so a copy of an entry keeps the restrictions the original carried. They can only
|
|
987
|
+
* narrow a validation, so an entry that states them is safe to honor whoever built it. */
|
|
988
|
+
if (meta.constraints) guard.verdict.set(out, "constraints", _copyConstraints(meta.constraints));
|
|
950
989
|
return out;
|
|
951
990
|
}
|
|
952
991
|
|
|
992
|
+
/** @internal Copied entry by entry, for the same reason every other anchor field is: what the
|
|
993
|
+
* validator enforces must be what this verb read, not what a caller's object answers later. */
|
|
994
|
+
function _copyConstraints(c) {
|
|
995
|
+
var out = intrinsic.create(null);
|
|
996
|
+
if (c.nameConstraints) {
|
|
997
|
+
out.nameConstraints = {
|
|
998
|
+
permitted: _copySubtrees(c.nameConstraints.permitted, "permitted"),
|
|
999
|
+
excluded: _copySubtrees(c.nameConstraints.excluded, "excluded"),
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
if (c.policySet) out.policySet = intrinsic.map(c.policySet, function (p) { return p; });
|
|
1003
|
+
if (c.policyFlags) {
|
|
1004
|
+
var f = intrinsic.create(null);
|
|
1005
|
+
intrinsic.forEach(trustanchorSchema.policyFlagBits, function (k) { f[k] = c.policyFlags[k] === true; });
|
|
1006
|
+
out.policyFlags = f;
|
|
1007
|
+
}
|
|
1008
|
+
if (c.pathLenConstraint !== undefined) out.pathLenConstraint = c.pathLenConstraint;
|
|
1009
|
+
return out;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/** @internal An RFC 5914 anchor's constraints, in the shape `pki.path.validate` reads them. Each
|
|
1013
|
+
* field is absent rather than null when the anchor states nothing, so an anchor that constrains
|
|
1014
|
+
* nothing is the object this module produced before the format existed. */
|
|
1015
|
+
function _anchorConstraintsOf(taInfo) {
|
|
1016
|
+
var cp = taInfo.certPath;
|
|
1017
|
+
/** @internal `exts` is where RFC 5914 sec. 2.6 says an anchor associates any extension outside
|
|
1018
|
+
* the four `certPath` already carries, and basic constraints is one of them, so a path length
|
|
1019
|
+
* stated there binds as much as one stated in `certPath` (RFC 5937 sec. 3.2). Where both state
|
|
1020
|
+
* one, the stricter wins: an anchor may only narrow. */
|
|
1021
|
+
var extsPathLen = pkix.anchorPathLenFromExtensions(taInfo.exts, _NS, "the TrustAnchorInfo exts");
|
|
1022
|
+
if (cp === null) {
|
|
1023
|
+
if (extsPathLen === null) return null;
|
|
1024
|
+
var only = intrinsic.create(null);
|
|
1025
|
+
only.pathLenConstraint = extsPathLen;
|
|
1026
|
+
return only;
|
|
1027
|
+
}
|
|
1028
|
+
var out = intrinsic.create(null);
|
|
1029
|
+
var any = false;
|
|
1030
|
+
if (cp.nameConstr !== null) {
|
|
1031
|
+
out.nameConstraints = {
|
|
1032
|
+
permitted: intrinsic.map(cp.nameConstr.permittedSubtrees || [], _subtreeOf),
|
|
1033
|
+
excluded: intrinsic.map(cp.nameConstr.excludedSubtrees || [], _subtreeOf),
|
|
1034
|
+
};
|
|
1035
|
+
any = true;
|
|
1036
|
+
}
|
|
1037
|
+
if (cp.policySet !== null) {
|
|
1038
|
+
out.policySet = intrinsic.map(cp.policySet, function (p) { return p.policyIdentifier; });
|
|
1039
|
+
any = true;
|
|
1040
|
+
}
|
|
1041
|
+
if (cp.policyFlags !== null) { out.policyFlags = cp.policyFlags; any = true; }
|
|
1042
|
+
var pathLen = pkix.lowerPathLen(cp.pathLenConstraint, extsPathLen);
|
|
1043
|
+
if (pathLen !== null) { out.pathLenConstraint = pathLen; any = true; }
|
|
1044
|
+
return any ? out : null;
|
|
1045
|
+
}
|
|
1046
|
+
function _subtreeOf(st) { return { tag: st.base.tagNumber, base: st.base.value }; }
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* @primitive pki.trust.parseTrustAnchorList
|
|
1050
|
+
* @signature pki.trust.parseTrustAnchorList(input, opts?) -> { anchors }
|
|
1051
|
+
* @since 0.8.13
|
|
1052
|
+
* @status stable
|
|
1053
|
+
* @spec RFC 5914, RFC 5937 sec. 3.2
|
|
1054
|
+
* @related pki.trust.anchor, pki.schema.trustanchor.parse, pki.path.validate
|
|
1055
|
+
*
|
|
1056
|
+
* Read an RFC 5914 `TrustAnchorList` into the trust-store entries `pki.trust.anchor` takes, the
|
|
1057
|
+
* same shape `parseCertdata` and `parseCcadbCsv` produce. Each entry carries the anchor's name,
|
|
1058
|
+
* public key and algorithm, so a list published by a root program drives `pki.path.validate`
|
|
1059
|
+
* without the operator mapping it by hand.
|
|
1060
|
+
*
|
|
1061
|
+
* A `taInfo` anchor's constraints ride on the entry and are applied as RFC 5937 section 3.2 states,
|
|
1062
|
+
* which is that the stricter of the anchor's value and the caller's always wins: its name
|
|
1063
|
+
* constraints intersect the caller's permitted subtrees and union its excluded ones, its
|
|
1064
|
+
* `policySet` intersects `opts.userInitialPolicySet`, each of its `policyFlags` may turn the
|
|
1065
|
+
* matching `initial*` option on and never off, and its `pathLenConstraint` bounds the path.
|
|
1066
|
+
*
|
|
1067
|
+
* The `certificate` and `tbsCert` arms carry no constraints of their own, so an entry from either
|
|
1068
|
+
* is the anchor that certificate's subject and key describe.
|
|
1069
|
+
*
|
|
1070
|
+
* @opts
|
|
1071
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (integer) -- decode caps for the parse.
|
|
1072
|
+
* @example
|
|
1073
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
1074
|
+
* var der = pki.trustanchor.build({ anchors: [{ taInfo: {
|
|
1075
|
+
* pubKey: await pki.key.export(pair.publicKey), keyId: Buffer.alloc(20, 1),
|
|
1076
|
+
* certPath: { taName: "CN=Example Root" } } }] });
|
|
1077
|
+
* pki.trust.parseTrustAnchorList(der).anchors.length; // -> 1
|
|
1078
|
+
*/
|
|
1079
|
+
function parseTrustAnchorList(input, opts) {
|
|
1080
|
+
var list = trustanchorSchema.parse(input, opts);
|
|
1081
|
+
var out = [];
|
|
1082
|
+
intrinsic.forEach(list.anchors, function (a, i) {
|
|
1083
|
+
guard.list.append(out, _entryFromAnchor(a, i));
|
|
1084
|
+
});
|
|
1085
|
+
return { anchors: out };
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function _entryFromAnchor(a, i) {
|
|
1089
|
+
if (a.kind === "certificate" || a.kind === "tbsCert") {
|
|
1090
|
+
var cert = a.kind === "certificate" ? a.certificate : a.tbsCert;
|
|
1091
|
+
return _mkAnchor(cert, { distrustAfter: {}, purposes: null, label: null, mozillaCaPolicy: false });
|
|
1092
|
+
}
|
|
1093
|
+
var info = a.taInfo;
|
|
1094
|
+
if (info.certPath === null) {
|
|
1095
|
+
throw E("trust/bad-input", "anchors[" + i + "] is a TrustAnchorInfo carrying no certPath, so it names no " +
|
|
1096
|
+
"distinguished name to chain to; a trust anchor needs one (RFC 5914 sec. 2)");
|
|
1097
|
+
}
|
|
1098
|
+
var spki = info.pubKey;
|
|
1099
|
+
var constraints = _anchorConstraintsOf(info);
|
|
1100
|
+
var entry = {
|
|
1101
|
+
name: info.certPath.taName,
|
|
1102
|
+
publicKey: spki.bytes,
|
|
1103
|
+
algorithm: spki.algorithm.oid,
|
|
1104
|
+
parameters: spki.algorithm.parameters,
|
|
1105
|
+
subjectDer: info.certPath.taName.bytes,
|
|
1106
|
+
distrustAfter: {},
|
|
1107
|
+
purposes: null,
|
|
1108
|
+
label: info.taTitle,
|
|
1109
|
+
mozillaCaPolicy: false,
|
|
1110
|
+
};
|
|
1111
|
+
/** @internal On the ENTRY, not only in the derived record behind it. The entry carries a name, a
|
|
1112
|
+
* public key and an algorithm, which is exactly the anchor tuple `pki.path.validate` accepts, so
|
|
1113
|
+
* an operator passing `parseTrustAnchorList(der).anchors` straight to `trustAnchors` is doing the
|
|
1114
|
+
* obvious thing. Keeping the constraints out of reach of that call would hand them an
|
|
1115
|
+
* UNCONSTRAINED anchor and silently drop every restriction the list stated. */
|
|
1116
|
+
if (constraints) guard.verdict.set(entry, "constraints", constraints);
|
|
1117
|
+
_DERIVED_FROM.set(entry, {
|
|
1118
|
+
name: _copyName(info.certPath.taName),
|
|
1119
|
+
publicKey: intrinsic.bufferFrom(spki.bytes),
|
|
1120
|
+
algorithm: spki.algorithm.oid,
|
|
1121
|
+
parameters: spki.algorithm.parameters == null ? spki.algorithm.parameters : intrinsic.bufferFrom(spki.algorithm.parameters),
|
|
1122
|
+
purposes: null,
|
|
1123
|
+
distrustAfter: _copyDistrustAfter({}),
|
|
1124
|
+
/** @internal Its OWN copy, not the object the entry carries. The derived record is what
|
|
1125
|
+
* `anchor` trusts in preference to the entry, precisely because the entry is reachable: a
|
|
1126
|
+
* caller who edits `entry.constraints` would otherwise be editing what the anchor enforces,
|
|
1127
|
+
* which is the one thing this record exists to prevent for the name and the key. */
|
|
1128
|
+
constraints: constraints === null ? null : _copyConstraints(constraints),
|
|
1129
|
+
});
|
|
1130
|
+
return entry;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
953
1133
|
module.exports = {
|
|
954
1134
|
parseCertdata: parseCertdata,
|
|
955
1135
|
parseCcadbCsv: parseCcadbCsv,
|
|
1136
|
+
parseTrustAnchorList: parseTrustAnchorList,
|
|
956
1137
|
anchor: anchor,
|
|
957
1138
|
};
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* @module pki.trustanchor
|
|
6
|
+
* @nav Trust
|
|
7
|
+
* @title Trust anchor lists
|
|
8
|
+
* @order 178
|
|
9
|
+
* @slug trustanchor-build
|
|
10
|
+
*
|
|
11
|
+
* @intro
|
|
12
|
+
* Write the RFC 5914 `TrustAnchorList` a root program publishes: each anchor
|
|
13
|
+
* as a certificate, as a bare `TBSCertificate`, or as a `TrustAnchorInfo`
|
|
14
|
+
* carrying the name it is known by and the constraints a relying party
|
|
15
|
+
* applies to paths beneath it. Every anchor is held to the same rules
|
|
16
|
+
* `pki.schema.trustanchor.parse` holds one to, and the result round-trips
|
|
17
|
+
* through that parser before it is returned.
|
|
18
|
+
*
|
|
19
|
+
* @card
|
|
20
|
+
* Build RFC 5914 trust anchor lists: the anchor carries its own policies,
|
|
21
|
+
* name constraints and path length.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
var asn1 = require("./asn1-der");
|
|
25
|
+
var schema = require("./schema-engine");
|
|
26
|
+
var pkix = require("./schema-pkix");
|
|
27
|
+
var trustanchor = require("./schema-trustanchor");
|
|
28
|
+
var x509 = require("./schema-x509");
|
|
29
|
+
var pkiBuild = require("./pki-build");
|
|
30
|
+
var oid = require("./oid");
|
|
31
|
+
var frameworkError = require("./framework-error");
|
|
32
|
+
var intrinsic = require("./guard-intrinsic");
|
|
33
|
+
var guard = require("./guard-all");
|
|
34
|
+
|
|
35
|
+
var TrustAnchorError = frameworkError.TrustAnchorError;
|
|
36
|
+
var b = asn1.build;
|
|
37
|
+
|
|
38
|
+
function _err(code, message, cause) { return new TrustAnchorError(code, message, cause); }
|
|
39
|
+
function O(n) { return oid.byName(n); }
|
|
40
|
+
|
|
41
|
+
var NS = pkix.makeNS("trustanchor", TrustAnchorError, oid);
|
|
42
|
+
var NAME_SCHEMA = pkix.name(NS);
|
|
43
|
+
var SPKI_SCHEMA = pkix.spki(NS);
|
|
44
|
+
var _b = pkiBuild.makeBuilder({ ErrorClass: TrustAnchorError, prefix: "trustanchor", O: O, NS: NS,
|
|
45
|
+
NAME_SCHEMA: NAME_SCHEMA, SPKI_SCHEMA: SPKI_SCHEMA, EXT_DECODERS: {} });
|
|
46
|
+
|
|
47
|
+
var CERT_EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
|
|
48
|
+
var OID_NAME_CONSTRAINTS = oid.byName("nameConstraints");
|
|
49
|
+
|
|
50
|
+
var _ANCHOR_KEYS = intrinsic.assign(intrinsic.create(null), { certificate: 1, tbsCert: 1, taInfo: 1 });
|
|
51
|
+
var _INFO_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
52
|
+
pubKey: 1, keyId: 1, taTitle: 1, certPath: 1, exts: 1, taTitleLangTag: 1 });
|
|
53
|
+
var _CERT_PATH_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
54
|
+
taName: 1, certificate: 1, policySet: 1, policyFlags: 1, nameConstr: 1, pathLenConstraint: 1 });
|
|
55
|
+
var _FLAG_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
56
|
+
inhibitPolicyMapping: 1, requireExplicitPolicy: 1, inhibitAnyPolicy: 1 });
|
|
57
|
+
var _BUILD_KEYS = intrinsic.assign(intrinsic.create(null), { anchors: 1, pem: 1 });
|
|
58
|
+
|
|
59
|
+
/** @internal The shape check comes first, so every nested record reports a typed refusal naming
|
|
60
|
+
* the field instead of a native TypeError from the first property read below it. */
|
|
61
|
+
function _known(obj, allowed, what) {
|
|
62
|
+
guard.identifier.assertPlainRecord(obj, _err, "trustanchor/bad-input", what);
|
|
63
|
+
guard.identifier.assertKnownKeys(obj, allowed, _err, "trustanchor/bad-input", "unknown " + what + " field ");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _policySetDer(list) {
|
|
67
|
+
if (!intrinsic.isArray(list) || list.length === 0) {
|
|
68
|
+
throw _err("trustanchor/bad-input", "certPath.policySet must be a non-empty array of policy identifiers");
|
|
69
|
+
}
|
|
70
|
+
return b.sequence(intrinsic.map(list, function (p) {
|
|
71
|
+
if (typeof p !== "string" || !oid.isDottedDecimal(p)) {
|
|
72
|
+
throw _err("trustanchor/bad-input", "certPath.policySet entries must be dotted-decimal policy identifiers");
|
|
73
|
+
}
|
|
74
|
+
/** @internal RFC 5914 sec. 2 forbids the qualifiers, so there is no place to put one and a
|
|
75
|
+
* caller cannot supply one: the element is the identifier alone. */
|
|
76
|
+
return b.sequence([b.oid(p)]);
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var FLAG_BITS = trustanchor.policyFlagBits;
|
|
81
|
+
function _policyFlagsDer(flags) {
|
|
82
|
+
_known(flags, _FLAG_KEYS, "certPath.policyFlags");
|
|
83
|
+
var bits = [];
|
|
84
|
+
for (var i = 0; i < FLAG_BITS.length; i++) {
|
|
85
|
+
var v = flags[FLAG_BITS[i]];
|
|
86
|
+
if (v === undefined || v === false) continue;
|
|
87
|
+
if (v !== true) throw _err("trustanchor/bad-input", "certPath.policyFlags." + FLAG_BITS[i] + " must be a boolean");
|
|
88
|
+
guard.list.append(bits, i);
|
|
89
|
+
}
|
|
90
|
+
if (bits.length === 0) return null;
|
|
91
|
+
return b.implicit(2, b.namedBitString(bits));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function _certPathDer(spec) {
|
|
95
|
+
_known(spec, _CERT_PATH_KEYS, "certPath");
|
|
96
|
+
var nameDer = _b.encodeName(spec.taName);
|
|
97
|
+
if (_b.isEmptyName(nameDer)) {
|
|
98
|
+
throw _err("trustanchor/bad-input", "certPath.taName must be a non-empty distinguished name (RFC 5914 sec. 2)");
|
|
99
|
+
}
|
|
100
|
+
var kids = [b.raw(nameDer)];
|
|
101
|
+
/** @internal The RFC 5914 module is IMPLICIT TAGS, so a context tag REPLACES the member's own
|
|
102
|
+
* universal tag rather than wrapping it. `b.implicit` re-tags the value it is given; only the
|
|
103
|
+
* three members the module writes EXPLICIT beside get a wrapper. */
|
|
104
|
+
if (spec.certificate !== undefined) {
|
|
105
|
+
guard.list.append(kids, b.implicit(0, _b.reqDer(spec.certificate, "certPath.certificate")));
|
|
106
|
+
}
|
|
107
|
+
var hasPolicySet = spec.policySet !== undefined;
|
|
108
|
+
if (hasPolicySet) guard.list.append(kids, b.implicit(1, _policySetDer(spec.policySet)));
|
|
109
|
+
if (spec.policyFlags !== undefined) {
|
|
110
|
+
if (!spec.policyFlags || typeof spec.policyFlags !== "object") {
|
|
111
|
+
throw _err("trustanchor/bad-input", "certPath.policyFlags must be an object of boolean flags");
|
|
112
|
+
}
|
|
113
|
+
/** @internal RFC 5914 sec. 2: "This bit MUST be set to FALSE if policySet is absent." Refused
|
|
114
|
+
* here as well as at the parse, so the builder cannot emit what the parser would reject. */
|
|
115
|
+
if (spec.policyFlags.requireExplicitPolicy === true && !hasPolicySet) {
|
|
116
|
+
throw _err("trustanchor/bad-input", "certPath.policyFlags.requireExplicitPolicy requires a policySet (RFC 5914 sec. 2)");
|
|
117
|
+
}
|
|
118
|
+
var flagsDer = _policyFlagsDer(spec.policyFlags);
|
|
119
|
+
if (flagsDer !== null) guard.list.append(kids,flagsDer);
|
|
120
|
+
}
|
|
121
|
+
if (spec.nameConstr !== undefined) {
|
|
122
|
+
var ncDer = _b.reqDer(spec.nameConstr, "certPath.nameConstr");
|
|
123
|
+
try { CERT_EXT_DECODERS[OID_NAME_CONSTRAINTS](ncDer); }
|
|
124
|
+
catch (e) { throw _err("trustanchor/bad-input", "certPath.nameConstr is not a well-formed NameConstraints (RFC 5280 sec. 4.2.1.10)", e); }
|
|
125
|
+
guard.list.append(kids, b.implicit(3, ncDer));
|
|
126
|
+
}
|
|
127
|
+
if (spec.pathLenConstraint !== undefined) {
|
|
128
|
+
var n = spec.pathLenConstraint;
|
|
129
|
+
if (!intrinsic.isInteger(n) || n < 0) {
|
|
130
|
+
throw _err("trustanchor/bad-input", "certPath.pathLenConstraint must be a non-negative integer (INTEGER (0..MAX), RFC 5914 sec. 2)");
|
|
131
|
+
}
|
|
132
|
+
guard.list.append(kids,b.implicit(4, b.integer(intrinsic.BigInt(n))));
|
|
133
|
+
}
|
|
134
|
+
return b.sequence(kids);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function _taInfoDer(spec) {
|
|
138
|
+
_known(spec, _INFO_KEYS, "taInfo");
|
|
139
|
+
/** @internal The version is the DEFAULT v1 and DER omits it, so it is not a field a caller sets
|
|
140
|
+
* and there is exactly one encoding of a v1 anchor. */
|
|
141
|
+
var spki = _b.reqDer(spec.pubKey, "taInfo.pubKey");
|
|
142
|
+
_b.assertValidSpki(spki, "taInfo.pubKey");
|
|
143
|
+
var keyId = guard.bytes.snapshotSource(spec.keyId, TrustAnchorError, "trustanchor/bad-input", "taInfo.keyId");
|
|
144
|
+
if (keyId.length === 0) throw _err("trustanchor/bad-input", "taInfo.keyId must not be empty");
|
|
145
|
+
var kids = [b.raw(spki), b.octetString(keyId)];
|
|
146
|
+
if (spec.taTitle !== undefined) {
|
|
147
|
+
/** @internal Counted the way the reader counts, in characters rather than UTF-16 code units,
|
|
148
|
+
* so the writer admits exactly the titles the reader does. */
|
|
149
|
+
if (typeof spec.taTitle !== "string") {
|
|
150
|
+
throw _err("trustanchor/bad-input", "taInfo.taTitle must be a string");
|
|
151
|
+
}
|
|
152
|
+
var titleLen = trustanchor.titleChars(spec.taTitle);
|
|
153
|
+
if (titleLen < 1 || titleLen > 64) {
|
|
154
|
+
throw _err("trustanchor/bad-input", "taInfo.taTitle must be 1 to 64 characters (SIZE (1..64), RFC 5914 sec. 2), got " + titleLen);
|
|
155
|
+
}
|
|
156
|
+
guard.list.append(kids,b.utf8(spec.taTitle));
|
|
157
|
+
}
|
|
158
|
+
if (spec.certPath !== undefined) guard.list.append(kids,_certPathDer(spec.certPath));
|
|
159
|
+
if (spec.exts !== undefined) {
|
|
160
|
+
if (!intrinsic.isArray(spec.exts) || spec.exts.length === 0) {
|
|
161
|
+
throw _err("trustanchor/bad-input", "taInfo.exts must be a non-empty array of pre-encoded Extension DER");
|
|
162
|
+
}
|
|
163
|
+
var extDers = intrinsic.map(spec.exts, function (e, i) {
|
|
164
|
+
var der = _b.reqDer(e, "taInfo.exts[" + i + "]");
|
|
165
|
+
var extnId;
|
|
166
|
+
try { extnId = asn1.read.oid(asn1.decode(der).children[0]); }
|
|
167
|
+
catch (err) { throw _err("trustanchor/bad-input", "taInfo.exts[" + i + "] is not a well-formed Extension", err); }
|
|
168
|
+
/** @internal RFC 5914 sec. 3 names four extension types that MUST NOT appear here. The parser
|
|
169
|
+
* ignores one that does, as the RFC says to; the builder refuses to write one, so this
|
|
170
|
+
* toolkit never produces an anchor carrying a constraint nothing will read. */
|
|
171
|
+
if (trustanchor.excludedExtensionOids[extnId] === true) {
|
|
172
|
+
throw _err("trustanchor/bad-input", "taInfo.exts must not carry " + (oid.name(extnId) || extnId) +
|
|
173
|
+
", which RFC 5914 sec. 3 excludes and a reader ignores; state it in certPath instead");
|
|
174
|
+
}
|
|
175
|
+
return b.raw(der);
|
|
176
|
+
});
|
|
177
|
+
guard.list.append(kids,b.explicit(1, b.sequence(extDers)));
|
|
178
|
+
}
|
|
179
|
+
if (spec.taTitleLangTag !== undefined) {
|
|
180
|
+
/** @internal The type carries no SIZE constraint, so the reader accepts an empty language tag
|
|
181
|
+
* and this refuses to write one: an empty tag names no language, and the field exists to say
|
|
182
|
+
* which language the title is in. The docstring states it, since the module does not. */
|
|
183
|
+
if (typeof spec.taTitleLangTag !== "string" || spec.taTitleLangTag.length === 0) {
|
|
184
|
+
throw _err("trustanchor/bad-input", "taInfo.taTitleLangTag must be a non-empty string; an empty one names no language");
|
|
185
|
+
}
|
|
186
|
+
/** @internal `[2] UTF8String` with no EXPLICIT beside it in the module, so the context tag
|
|
187
|
+
* replaces the UTF8String tag rather than wrapping it. */
|
|
188
|
+
guard.list.append(kids, b.implicit(2, b.utf8(spec.taTitleLangTag)));
|
|
189
|
+
}
|
|
190
|
+
return b.sequence(kids);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function _anchorDer(spec, i) {
|
|
194
|
+
if (!spec || typeof spec !== "object" || intrinsic.isArray(spec)) {
|
|
195
|
+
throw _err("trustanchor/bad-input", "anchors[" + i + "] must be an object naming exactly one of certificate, tbsCert or taInfo");
|
|
196
|
+
}
|
|
197
|
+
_known(spec, _ANCHOR_KEYS, "anchors[" + i + "]");
|
|
198
|
+
var named = [];
|
|
199
|
+
intrinsic.forEach(["certificate", "tbsCert", "taInfo"], function (k) {
|
|
200
|
+
if (spec[k] !== undefined) guard.list.append(named, k);
|
|
201
|
+
});
|
|
202
|
+
if (named.length !== 1) {
|
|
203
|
+
throw _err("trustanchor/bad-input", "anchors[" + i + "] must name exactly one of certificate, tbsCert or taInfo, got " +
|
|
204
|
+
(named.length === 0 ? "none" : intrinsic.join(named, " and ")));
|
|
205
|
+
}
|
|
206
|
+
if (named[0] === "certificate") return b.raw(_b.reqDer(spec.certificate, "anchors[" + i + "].certificate"));
|
|
207
|
+
if (named[0] === "tbsCert") return b.explicit(1, b.raw(_b.reqDer(spec.tbsCert, "anchors[" + i + "].tbsCert")));
|
|
208
|
+
return b.explicit(2, _taInfoDer(spec.taInfo));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @primitive pki.trustanchor.build
|
|
213
|
+
* @signature pki.trustanchor.build(spec, opts?) -> Buffer | string
|
|
214
|
+
* @since 0.8.13
|
|
215
|
+
* @status stable
|
|
216
|
+
* @spec RFC 5914
|
|
217
|
+
* @related pki.schema.trustanchor.parse, pki.trust.parseTrustAnchorList
|
|
218
|
+
*
|
|
219
|
+
* Build an RFC 5914 `TrustAnchorList`, the structure a root program publishes. `spec.anchors` is a
|
|
220
|
+
* non-empty array, each entry naming exactly one of `certificate` (pre-encoded Certificate DER),
|
|
221
|
+
* `tbsCert` (pre-encoded TBSCertificate DER) or `taInfo`.
|
|
222
|
+
*
|
|
223
|
+
* A `taInfo` entry is `{ pubKey, keyId, taTitle?, certPath?, exts?, taTitleLangTag? }`, where
|
|
224
|
+
* `pubKey` is SubjectPublicKeyInfo DER and `keyId` the key identifier bytes. `certPath` carries the
|
|
225
|
+
* constraints: `taName` (a distinguished name string or an array of relative names), `certificate`,
|
|
226
|
+
* `policySet` (an array of policy identifiers), `policyFlags`
|
|
227
|
+
* (`{ inhibitPolicyMapping, requireExplicitPolicy, inhibitAnyPolicy }`), `nameConstr` (pre-encoded
|
|
228
|
+
* NameConstraints DER) and `pathLenConstraint`.
|
|
229
|
+
*
|
|
230
|
+
* Every rule the parser enforces is enforced here, so this toolkit does not emit an anchor it would
|
|
231
|
+
* refuse to read: an empty `taName`, a `requireExplicitPolicy` with no `policySet`, a negative
|
|
232
|
+
* `pathLenConstraint`, and an `exts` entry naming one of the four extension types RFC 5914
|
|
233
|
+
* section 3 excludes. The result round-trips through `pki.schema.trustanchor.parse` before it is
|
|
234
|
+
* returned.
|
|
235
|
+
*
|
|
236
|
+
* The version is not a field a caller sets: v1 is the ASN.1 DEFAULT and DER omits it, so every v1
|
|
237
|
+
* anchor has one encoding.
|
|
238
|
+
*
|
|
239
|
+
* Two refusals are stricter than the module, on the principle that what this
|
|
240
|
+
* writes is what an operator publishes: `taTitleLangTag` may not be the empty string, which
|
|
241
|
+
* names no language, and `exts` may not carry one of the four extension types RFC 5914 section 3
|
|
242
|
+
* excludes, which a reader is told to ignore and which `certPath` states properly.
|
|
243
|
+
*
|
|
244
|
+
* @opts
|
|
245
|
+
* - `pem` (boolean) -- return a PEM `TRUST ANCHOR LIST` string instead of DER.
|
|
246
|
+
* @example
|
|
247
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
248
|
+
* var der = pki.trustanchor.build({ anchors: [{ taInfo: {
|
|
249
|
+
* pubKey: await pki.key.export(pair.publicKey),
|
|
250
|
+
* keyId: Buffer.alloc(20, 1),
|
|
251
|
+
* taTitle: "Example Root CA",
|
|
252
|
+
* certPath: { taName: "CN=Example Root", pathLenConstraint: 2 } } }] });
|
|
253
|
+
* pki.schema.trustanchor.parse(der).anchors[0].taInfo.certPath.pathLenConstraint; // -> 2
|
|
254
|
+
*/
|
|
255
|
+
function build(spec, opts) {
|
|
256
|
+
if (!spec || typeof spec !== "object" || intrinsic.isArray(spec)) {
|
|
257
|
+
throw _err("trustanchor/bad-input", "build expects a { anchors } specification object");
|
|
258
|
+
}
|
|
259
|
+
_known(spec, _BUILD_KEYS, "build");
|
|
260
|
+
var checked = guard.identifier.optionsObject(opts, _err, "trustanchor/bad-input", "pki.trustanchor.build options");
|
|
261
|
+
guard.identifier.assertKnownKeys(checked, intrinsic.assign(intrinsic.create(null), { pem: 1 }),
|
|
262
|
+
_err, "trustanchor/bad-input", "unknown pki.trustanchor.build option ");
|
|
263
|
+
if (!intrinsic.isArray(spec.anchors) || spec.anchors.length === 0) {
|
|
264
|
+
throw _err("trustanchor/bad-input", "build: anchors must be a non-empty array (TrustAnchorList is SIZE (1..MAX))");
|
|
265
|
+
}
|
|
266
|
+
var der = b.sequence(intrinsic.map(spec.anchors, function (a, i) { return _anchorDer(a, i); }));
|
|
267
|
+
/** @internal The reader reads the writer's output back before it is returned, so a shape the two
|
|
268
|
+
* disagree about fails the build instead of reaching an operator. */
|
|
269
|
+
try { trustanchor.parse(der); }
|
|
270
|
+
catch (e) { throw _err("trustanchor/bad-input", "the built trust anchor list does not parse: " + guard.text.describeThrown(e), e); }
|
|
271
|
+
if (checked.pem === true) return trustanchor.pemEncode(der);
|
|
272
|
+
return der;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
void schema;
|
|
276
|
+
void x509;
|
|
277
|
+
|
|
278
|
+
module.exports = { build: build };
|
package/lib/validator-tpm.js
CHANGED
|
@@ -122,7 +122,7 @@ function normalizeObjectAttributePolicy(policy, E, code) {
|
|
|
122
122
|
var explicit = policy.objectAttributes;
|
|
123
123
|
if (explicit !== undefined) {
|
|
124
124
|
if (!explicit || typeof explicit !== "object" || Array.isArray(explicit)) throw new E(code, "opts.tpmPolicy.objectAttributes must be an object");
|
|
125
|
-
|
|
125
|
+
guard.identifier.optionNames(explicit).forEach(function (n) {
|
|
126
126
|
if (TPMA_OBJECT_BITS[n] === undefined) throw new E(code, "opts.tpmPolicy.objectAttributes has an unknown attribute " + JSON.stringify(n));
|
|
127
127
|
if (typeof explicit[n] !== "boolean") throw new E(code, "opts.tpmPolicy.objectAttributes." + n + " must be a boolean");
|
|
128
128
|
want[n] = explicit[n];
|
package/lib/webauthn-mds.js
CHANGED
|
@@ -440,7 +440,7 @@ function metadataAnchors(entry, opts) {
|
|
|
440
440
|
opts = opts || {};
|
|
441
441
|
if (typeof opts !== "object" || Array.isArray(opts)) throw _err("webauthn/bad-input", "metadataAnchors opts must be an object");
|
|
442
442
|
guard.identifier.assertKnownKeys(opts, _ANCHOR_OPTS, _err, "webauthn/bad-input", "metadataAnchors opts has an unknown key ");
|
|
443
|
-
opts =
|
|
443
|
+
opts = guard.identifier.ownOptions(opts);
|
|
444
444
|
if (opts.time !== undefined) guard.time.assertValid(opts.time, _err, "webauthn/bad-input", "opts.time");
|
|
445
445
|
if (opts.metadata !== undefined) {
|
|
446
446
|
if (!isVerifiedResult(opts.metadata)) {
|
package/lib/webauthn.js
CHANGED
|
@@ -906,7 +906,7 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
906
906
|
try {
|
|
907
907
|
if (!_isPlainObject(opts)) throw _err("webauthn/bad-input", "opts must be an object");
|
|
908
908
|
guard.identifier.assertKnownKeys(opts, _VERIFY_OPTS, _err, "webauthn/bad-input", "opts has an unknown key ");
|
|
909
|
-
opts =
|
|
909
|
+
opts = guard.identifier.ownOptions(opts);
|
|
910
910
|
if (opts.time !== undefined) guard.time.assertValid(opts.time, _err, "webauthn/bad-input", "opts.time");
|
|
911
911
|
_FORMAT_SCOPED_BOOLEAN_OPTS.forEach(function (k) {
|
|
912
912
|
if (opts[k] !== undefined && typeof opts[k] !== "boolean") throw _err("webauthn/bad-input", "opts." + k + " must be a boolean");
|
|
@@ -957,10 +957,9 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
957
957
|
var bindingChecked, vopts;
|
|
958
958
|
try {
|
|
959
959
|
bindingChecked = _applyBindings(att.authData, att.authData.credentialPublicKey, opts);
|
|
960
|
-
vopts =
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
});
|
|
960
|
+
vopts = guard.identifier.ownOptions(opts);
|
|
961
|
+
vopts.rootCertificates = _snapshotRoots(opts.rootCertificates);
|
|
962
|
+
vopts.safetyNetRoots = _snapshotRoots(opts.safetyNetRoots);
|
|
964
963
|
} catch (e) { return Promise.reject(e); }
|
|
965
964
|
return Promise.resolve().then(function () { return verifier(att, clientDataHash, vopts); })
|
|
966
965
|
.then(function (res) {
|
|
@@ -1478,8 +1477,7 @@ var _ASSERT_OPTS = Object.assign(Object.create(null), {
|
|
|
1478
1477
|
function _snapshotAssertion(input) {
|
|
1479
1478
|
if (!_isPlainObject(input)) throw _err("webauthn/bad-input", "pki.webauthn.verifyAssertion takes an options object");
|
|
1480
1479
|
guard.identifier.assertKnownKeys(input, _ASSERT_OPTS, _err, "webauthn/bad-input", "verifyAssertion input has an unknown key ");
|
|
1481
|
-
var out =
|
|
1482
|
-
for (k in input) { if (_hasOwn(input, k)) out[k] = input[k]; }
|
|
1480
|
+
var out = guard.identifier.ownOptions(input);
|
|
1483
1481
|
["authenticatorData", "clientDataJSON", "clientDataHash", "signature", "expectedChallenge"].forEach(function (f) {
|
|
1484
1482
|
if (_isBufferSource(out[f])) out[f] = guard.bytes.snapshotSource(out[f], WebauthnError, "webauthn/bad-input", f);
|
|
1485
1483
|
});
|
|
@@ -1487,9 +1485,10 @@ function _snapshotAssertion(input) {
|
|
|
1487
1485
|
out.credentialPublicKey = guard.bytes.snapshotSource(out.credentialPublicKey, WebauthnError,
|
|
1488
1486
|
"webauthn/bad-input", "credentialPublicKey");
|
|
1489
1487
|
} else if (_isPlainObject(out.credentialPublicKey)) {
|
|
1490
|
-
var key = intrinsic.create(null)
|
|
1491
|
-
|
|
1492
|
-
|
|
1488
|
+
var key = intrinsic.create(null);
|
|
1489
|
+
var coseNames = guard.identifier.optionNames(out.credentialPublicKey);
|
|
1490
|
+
for (var ci = 0; ci < coseNames.length; ci++) {
|
|
1491
|
+
var kk = coseNames[ci];
|
|
1493
1492
|
var v;
|
|
1494
1493
|
try { v = out.credentialPublicKey[kk]; }
|
|
1495
1494
|
catch (e) { throw _err("webauthn/bad-cose-key", "credentialPublicKey." + kk + " could not be read", e); }
|
package/lib/webcrypto.js
CHANGED
|
@@ -116,8 +116,7 @@ function _normalizeAlg(algorithm, who) {
|
|
|
116
116
|
if (!alg || typeof alg.name !== "string") {
|
|
117
117
|
throw new WebCryptoError("webcrypto/syntax", (who || "operation") + ": algorithm must be a string or an object with a name");
|
|
118
118
|
}
|
|
119
|
-
var out =
|
|
120
|
-
for (var k in alg) { if (_hasOwn(alg, k)) out[k] = alg[k]; }
|
|
119
|
+
var out = guard.identifier.ownOptions(alg);
|
|
121
120
|
out.name = _toUpperCase(alg.name);
|
|
122
121
|
return out;
|
|
123
122
|
}
|
|
@@ -1030,7 +1029,8 @@ function _algFromImport(name, alg, keyObject) {
|
|
|
1030
1029
|
function _curveFromKey(ko) {
|
|
1031
1030
|
try {
|
|
1032
1031
|
var jwk = ko.export({ format: "jwk" });
|
|
1033
|
-
|
|
1032
|
+
var curves = guard.identifier.optionNames(CURVE_NODE);
|
|
1033
|
+
for (var k = 0; k < curves.length; k++) { if (jwk.crv === curves[k]) return curves[k]; }
|
|
1034
1034
|
}
|
|
1035
1035
|
catch (_e) { }
|
|
1036
1036
|
return undefined;
|
package/package.json
CHANGED