@blamejs/pki 0.8.60 → 0.9.0

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +7 -0
  3. package/index.js +55 -26
  4. package/lib/acme.js +5 -5
  5. package/lib/asn1-der.js +1 -1
  6. package/lib/attrcert-sign.js +2 -2
  7. package/lib/cbor-det.js +4 -1
  8. package/lib/cmc-build.js +15 -10
  9. package/lib/cmp-build.js +5 -5
  10. package/lib/cmp-session.js +6 -3
  11. package/lib/cmp-verify.js +1 -1
  12. package/lib/cms-compress.js +5 -2
  13. package/lib/cms-decrypt.js +17 -17
  14. package/lib/cms-digest.js +2 -3
  15. package/lib/cms-encrypt.js +17 -18
  16. package/lib/cms-sign.js +2 -2
  17. package/lib/composite-kem.js +28 -29
  18. package/lib/crl-sign.js +3 -3
  19. package/lib/crmf-sign.js +12 -18
  20. package/lib/csr-sign.js +1 -1
  21. package/lib/ct.js +25 -22
  22. package/lib/est.js +12 -12
  23. package/lib/framework-error.js +1 -1
  24. package/lib/guard-bytes.js +1 -1
  25. package/lib/guard-crypto.js +506 -14
  26. package/lib/guard-limits.js +3 -2
  27. package/lib/guard-secret.js +3 -2
  28. package/lib/hpke.js +26 -26
  29. package/lib/http-digest.js +5 -7
  30. package/lib/http-retry-after.js +4 -1
  31. package/lib/http-transport.js +6 -6
  32. package/lib/inspect.js +9 -4
  33. package/lib/jose.js +3 -3
  34. package/lib/key.js +41 -29
  35. package/lib/merkle.js +4 -4
  36. package/lib/ocsp.js +3 -4
  37. package/lib/path-validate.js +3 -3
  38. package/lib/pbes2.js +14 -11
  39. package/lib/pkcs12-build.js +10 -11
  40. package/lib/pki-build.js +3 -3
  41. package/lib/scep.js +8 -9
  42. package/lib/schema-engine.js +12 -12
  43. package/lib/schema-pkix.js +12 -12
  44. package/lib/schema-tsp.js +3 -3
  45. package/lib/shbs.js +3 -6
  46. package/lib/sign-scheme.js +4 -4
  47. package/lib/sigstore.js +10 -10
  48. package/lib/smime.js +12 -9
  49. package/lib/tlog.js +5 -4
  50. package/lib/tls-cert-compress.js +3 -3
  51. package/lib/tsp-sign.js +6 -6
  52. package/lib/tuf.js +6 -5
  53. package/lib/validator-cose.js +2 -2
  54. package/lib/webauthn-mds.js +4 -8
  55. package/lib/webauthn.js +5 -12
  56. package/lib/webcrypto.js +61 -46
  57. package/lib/x509-sign.js +3 -3
  58. package/package.json +1 -1
  59. package/sbom.cdx.json +6 -6
package/lib/smime.js CHANGED
@@ -33,6 +33,10 @@
33
33
  * algorithm-agnostic.
34
34
  */
35
35
 
36
+ /** @internal The record constructor, captured at load, for the reason the same capture in
37
+ * `lib/inspect.js` gives: requiring the guard-intrinsic captures arms the module whole, which counts
38
+ * 176 live runtime reads here. */
39
+ var _create = Object.create;
36
40
  var frameworkError = require("./framework-error.js");
37
41
  var mime = require("./mime.js");
38
42
  var cms = require("./cms-verify.js");
@@ -48,7 +52,6 @@ var _push = guard.list.append;
48
52
  var pkiBuild = require("./pki-build.js");
49
53
  var C = require("./constants.js");
50
54
  var nodeUtil = require("util");
51
- var nodeCrypto = require("crypto");
52
55
 
53
56
  var SmimeError = frameworkError.SmimeError;
54
57
 
@@ -591,7 +594,7 @@ function _micalgOf(p7Der) {
591
594
  return out.length ? out.sort().join(",") : null;
592
595
  }
593
596
 
594
- function _boundary() { return "----=_pki_smime_" + nodeCrypto.randomBytes(18).toString("hex"); }
597
+ function _boundary() { return "----=_pki_smime_" + guard.crypto.randomBytes(18).toString("hex"); }
595
598
 
596
599
  function _wrap64Crlf(s) {
597
600
  var out = "";
@@ -655,7 +658,7 @@ function _base64Body(der) {
655
658
  */
656
659
  async function sign(content, signers, opts) {
657
660
  content = _fixedContent(content);
658
- opts = opts || {};
661
+ opts = opts || _create(null);
659
662
  opts = _knownOpts(opts, SIGN_OPTS, "sign");
660
663
  signers = _snapshotSignersFitForMail(signers);
661
664
  var hp = opts.protectHeaders === true;
@@ -768,7 +771,7 @@ function _capped(msg) {
768
771
  * if (res.valid) { res.content; res.signers[0].sid; }
769
772
  */
770
773
  async function verify(message, opts) {
771
- opts = opts || {};
774
+ opts = opts || _create(null);
772
775
  opts = _knownOpts(opts, VERIFY_OPTS, "verify");
773
776
  var ent = mime.parse(message, SmimeError, "smime/bad-mime");
774
777
  var ct = ent.contentType;
@@ -878,7 +881,7 @@ function _cmsEncryptOpts(opts) {
878
881
  */
879
882
  async function encrypt(content, recipients, opts) {
880
883
  content = _fixedContent(content);
881
- opts = opts || {};
884
+ opts = opts || _create(null);
882
885
  opts = _knownOpts(opts, ENCRYPT_OPTS, "encrypt");
883
886
  var hp = opts.protectHeaders === true;
884
887
  var hpList = hp ? _hpHeaderList(opts.headers) : null;
@@ -935,7 +938,7 @@ async function encrypt(content, recipients, opts) {
935
938
  * res.authenticated; // true here (an AEAD content cipher); false for an enveloped-only CBC message, which carries no integrity
936
939
  */
937
940
  async function decrypt(message, keyMaterial, opts) {
938
- opts = opts || {};
941
+ opts = opts || _create(null);
939
942
  opts = _knownOpts(opts, DECRYPT_OPTS, "decrypt");
940
943
  var ent = mime.parse(message, SmimeError, "smime/bad-mime");
941
944
  var ct = ent.contentType;
@@ -985,7 +988,7 @@ async function decrypt(message, keyMaterial, opts) {
985
988
  */
986
989
  async function compress(content, opts) {
987
990
  content = _fixedContent(content);
988
- opts = opts || {};
991
+ opts = opts || _create(null);
989
992
  opts = _knownOpts(opts, COMPRESS_OPTS, "compress");
990
993
  var entity = _entityBytes(content, opts);
991
994
  var cOpts = {};
@@ -1019,7 +1022,7 @@ async function compress(content, opts) {
1019
1022
  * res.content; // the recovered inner MIME entity
1020
1023
  */
1021
1024
  async function decompress(message, opts) {
1022
- opts = opts || {};
1025
+ opts = opts || _create(null);
1023
1026
  opts = _knownOpts(opts, DECOMPRESS_OPTS, "decompress");
1024
1027
  var ent = mime.parse(message, SmimeError, "smime/bad-mime");
1025
1028
  var ct = ent.contentType;
@@ -1065,7 +1068,7 @@ var CERTS_ONLY_OPTS = Object.assign(Object.create(null), { crls: 1 });
1065
1068
  * var p7c = pki.smime.buildCertsOnly([leafDer]);
1066
1069
  */
1067
1070
  function buildCertsOnly(certs, opts) {
1068
- opts = opts || {};
1071
+ opts = opts || _create(null);
1069
1072
  opts = _knownOpts(opts, CERTS_ONLY_OPTS, "buildCertsOnly");
1070
1073
  var der = _toBuf(cms.certsOnly(certs, { crls: opts.crls }));
1071
1074
  return _capped(Buffer.concat([_pkcs7MimeHead("certs-only", "smime.p7c"), _base64Body(der), mime.CRLF]));
package/lib/tlog.js CHANGED
@@ -299,12 +299,13 @@ function _resolveKey(keyName, publicKey) {
299
299
  throw _err("tlog/bad-input", "publicKey must be the raw " + ED25519_RAW_BYTES +
300
300
  " Ed25519 bytes or a SubjectPublicKeyInfo, got " + bytes.length + " bytes", e);
301
301
  }
302
- var type = key.asymmetricKeyType;
302
+ var type = guard.crypto.keyType(key);
303
303
  if (type === "ed25519") {
304
304
  return _edKey(name, bytes, _subarray(bytes, bytes.length - ED25519_RAW_BYTES));
305
305
  }
306
306
  if (type === "ec") {
307
- var curve = key.asymmetricKeyDetails && key.asymmetricKeyDetails.namedCurve;
307
+ var keyDetails = guard.crypto.keyDetails(key);
308
+ var curve = keyDetails && keyDetails.namedCurve;
308
309
  if (EC_CURVES[curve] !== 1) {
309
310
  throw _err("tlog/bad-input", "a signed note names no key-ID derivation for the elliptic curve " +
310
311
  guard.text.showValue(_stringOf(curve)));
@@ -670,7 +671,7 @@ function parseCheckpoint(input) {
670
671
  */
671
672
  var _CHECKPOINT_OPTS = _assign(_create(null), { origin: 1 });
672
673
  async function verifyCheckpoint(input, keys, opts) {
673
- opts = opts || {};
674
+ opts = opts || _create(null);
674
675
  guard.identifier.assertKnownKeys(opts, _CHECKPOINT_OPTS, _err,
675
676
  "tlog/bad-input", "unknown verifyCheckpoint option ");
676
677
  /** @internal ONE read of the caller's input, shared by the decode and the verification below. Reading
@@ -1208,7 +1209,7 @@ async function _tilePath(lo, hi, m, size, unitAt) {
1208
1209
  */
1209
1210
  var _INCLUSION_PROOF_KEYS = _assign(_create(null), { index: 1, size: 1, read: 1 });
1210
1211
  async function inclusionProof(opts) {
1211
- opts = opts || {};
1212
+ opts = opts || _create(null);
1212
1213
  guard.identifier.assertKnownKeys(opts, _INCLUSION_PROOF_KEYS, _err,
1213
1214
  "tlog/bad-input", "unknown inclusionProof option ");
1214
1215
  var size = guard.range.uint64(opts.size, _err, "tlog/bad-input", "size");
@@ -167,7 +167,7 @@ function _resolveAllowed(opts) {
167
167
  * pki.schema.x509.parse(out.certificate.entries[0].certData).subject;
168
168
  */
169
169
  function decompressCertificate(bytes, opts) {
170
- opts = opts || {};
170
+ opts = opts || intrinsic.create(null);
171
171
  /** @internal Viewed, CAPPED, then COPIED, and only then are the options read. A view stays a window
172
172
  * onto the caller's buffer, and `_resolveCap` reads `opts`, which is caller code when it is an
173
173
  * accessor: a `maxOutputBytes` getter that wrote another valid message of the same length over the
@@ -262,7 +262,7 @@ function decompressCertificate(bytes, opts) {
262
262
  * pki.schema.x509.parse(msg.entries[0].certData).subject;
263
263
  */
264
264
  function parseCertificateMessage(bytes, opts) {
265
- opts = opts || {};
265
+ opts = opts || intrinsic.create(null);
266
266
  var type = opts.certificateType === undefined ? "X509" : opts.certificateType;
267
267
  if (type !== "X509" && type !== "RawPublicKey") {
268
268
  throw _err("tls/bad-input", "opts.certificateType must be \"X509\" or \"RawPublicKey\"");
@@ -315,7 +315,7 @@ function parseCertificateMessage(bytes, opts) {
315
315
  * wire.length < message.length; // true
316
316
  */
317
317
  function compressCertificate(certificateMessage, opts) {
318
- opts = opts || {};
318
+ opts = opts || intrinsic.create(null);
319
319
  /** @internal Copied, not viewed. `opts.algorithm` is read below, and reading an option is caller code
320
320
  * when it is an accessor, so over a view it would run between the message being looked at and being
321
321
  * compressed and replace what this verb compresses, after it had accepted the message it was handed.
package/lib/tsp-sign.js CHANGED
@@ -155,7 +155,7 @@ var _SIGN_OPTS = intrinsic.assign(intrinsic.create(null), {
155
155
  });
156
156
 
157
157
  function _sign(messageImprint, tsa, opts) {
158
- opts = opts || {};
158
+ opts = opts || intrinsic.create(null);
159
159
  if (messageImprint && typeof messageImprint === "object" && !Buffer.isBuffer(messageImprint)) {
160
160
  guard.identifier.assertKnownKeys(messageImprint, _IMPRINT_KEYS, _err, "tsp/bad-input", function (k) {
161
161
  return "unknown messageImprint field " + JSON.stringify(k) + " (the imprint is { hashAlgorithm, hashedMessage })";
@@ -169,7 +169,7 @@ function _sign(messageImprint, tsa, opts) {
169
169
  " (the TSA is { cert, key }; a signing option belongs in the third argument)";
170
170
  });
171
171
  }
172
- var mi = messageImprint || {};
172
+ var mi = messageImprint || intrinsic.create(null);
173
173
  if (!mi.hashAlgorithm || !NODE_DIGEST[guard.text.keyOf(mi.hashAlgorithm)]) throw _err("tsp/unsupported-algorithm", "messageImprint.hashAlgorithm must be a supported hash name");
174
174
  if (!guard.bytes.isByteSource(mi.hashedMessage)) throw _err("tsp/bad-input", "messageImprint.hashedMessage must be a BufferSource");
175
175
  var _signHmLen = guard.bytes.source(mi.hashedMessage, TspError, "tsp/bad-input", "messageImprint.hashedMessage").length;
@@ -292,10 +292,10 @@ function _assertImprint(mi) {
292
292
  var _REQUEST_OPTS = intrinsic.assign(intrinsic.create(null), { reqPolicy: 1, nonce: 1, certReq: 1, extensions: 1, pem: 1 });
293
293
 
294
294
  function request(messageImprint, opts) {
295
- opts = opts || {};
295
+ opts = opts || intrinsic.create(null);
296
296
  if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.request options must be an object");
297
297
  guard.identifier.assertKnownKeys(opts, _REQUEST_OPTS, _err, "tsp/bad-input", "pki.tsp.request has an unknown option ");
298
- var mi = messageImprint || {};
298
+ var mi = messageImprint || intrinsic.create(null);
299
299
  if (mi && typeof mi === "object" && !Buffer.isBuffer(mi)) {
300
300
  guard.identifier.assertKnownKeys(mi, _IMPRINT_KEYS, _err, "tsp/bad-input", function (k) {
301
301
  return "unknown messageImprint field " + JSON.stringify(k) +
@@ -379,7 +379,7 @@ function parseRequest(input) { return schemaTsp.parseRequest(input); }
379
379
  var _RESPONSE_OPTS = intrinsic.assign(intrinsic.create(null), { status: 1, statusString: 1, failInfo: 1, pem: 1 });
380
380
 
381
381
  function response(token, opts) {
382
- opts = opts || {};
382
+ opts = opts || intrinsic.create(null);
383
383
  if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.response options must be an object");
384
384
  guard.identifier.assertKnownKeys(opts, _RESPONSE_OPTS, _err, "tsp/bad-input", "pki.tsp.response has an unknown option ");
385
385
  var st = opts.status;
@@ -664,7 +664,7 @@ function _anyCritical(extensions) {
664
664
  }
665
665
 
666
666
  async function verify(token, data, opts) {
667
- opts = opts || {};
667
+ opts = opts || intrinsic.create(null);
668
668
  if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.verify options must be an object");
669
669
  guard.identifier.assertKnownKeys(opts, _VERIFY_OPTS, _err, "tsp/bad-input",
670
670
  "pki.tsp.verify has an unknown option (the anchor option is `trustAnchors`) ");
package/lib/tuf.js CHANGED
@@ -798,12 +798,13 @@ function _declarationOf(key) {
798
798
  function _checkedPublicKey(key, d) {
799
799
  if (d.nodeType === "ed25519") return null;
800
800
  var imported = _publicFromPem(key.keyval.public, "a " + key.keytype + " public key");
801
- if (imported.asymmetricKeyType !== d.nodeType) {
801
+ var importedType = guard.crypto.keyType(imported);
802
+ if (importedType !== d.nodeType) {
802
803
  throw _err("tuf/bad-key", "the key declared as " + key.keytype + " holds a " +
803
- guard.text.showValue(imported.asymmetricKeyType) + " public key, so the signature would be checked " +
804
+ guard.text.showValue(importedType) + " public key, so the signature would be checked " +
804
805
  "under an algorithm the metadata does not name");
805
806
  }
806
- var details = imported.asymmetricKeyDetails || {};
807
+ var details = guard.crypto.keyDetails(imported) || _create(null);
807
808
  if (d.curve !== null && details.namedCurve !== d.curve) {
808
809
  throw _err("tuf/bad-key", "the scheme " + key.scheme + " names the " + d.curve +
809
810
  " curve; the key is on " + guard.text.showValue(details.namedCurve));
@@ -888,7 +889,7 @@ function _publicFromPem(pem, label) {
888
889
  */
889
890
  var _VERIFY_KEYS = intrinsic.assign(_create(null), { metadata: 1, keys: 1, role: 1 });
890
891
  async function verifySignatures(opts) {
891
- opts = opts || {};
892
+ opts = opts || _create(null);
892
893
  guard.identifier.assertKnownKeys(opts, _VERIFY_KEYS, _err, "tuf/bad-input", "unknown verifySignatures option ");
893
894
  /** @internal Every caller-owned input is taken ONCE, at the top, and everything below reads the copy.
894
895
  * Validating the caller's object and then reading it again is two reads, and a member reached through an
@@ -1296,7 +1297,7 @@ function _asRoot(input, label) {
1296
1297
  */
1297
1298
  var _UPDATE_KEYS = intrinsic.assign(_create(null), { trustedRoot: 1, candidates: 1, now: 1 });
1298
1299
  async function updateRoot(opts) {
1299
- opts = opts || {};
1300
+ opts = opts || _create(null);
1300
1301
  guard.identifier.assertKnownKeys(opts, _UPDATE_KEYS, _err, "tuf/bad-input", "unknown updateRoot option ");
1301
1302
  /** @internal The instant is CAPTURED here, at the call, into a Date this function owns. A Date is
1302
1303
  * mutable and the caller keeps theirs, and the freeze-attack check runs at the END, after a signature
@@ -7,7 +7,7 @@ var cbor = require("./cbor-det");
7
7
  var asn1 = require("./asn1-der");
8
8
  var oid = require("./oid");
9
9
  var edwardsPoint = require("./edwards-point");
10
- var nodeCrypto = require("crypto");
10
+ var guard = require("./guard-all.js");
11
11
 
12
12
  var EC2_CRV_LEN = Object.assign(Object.create(null), { 1: 32, 2: 48, 3: 66 });
13
13
  var EC2_CRV_OID = Object.assign(Object.create(null), { 1: "prime256v1", 2: "secp384r1", 3: "secp521r1" });
@@ -114,7 +114,7 @@ function assertKeyMaterial(key, E, code, unsupportedCode) {
114
114
  if (!prof) throw new E(unsupportedCode || code, "unsupported credential key algorithm " + key.alg);
115
115
  if (prof.kty !== key.kty) throw bad("credential key algorithm " + key.alg + " is inconsistent with key type " + key.kty);
116
116
  if (prof.crv != null && prof.crv !== key.crv) throw bad("credential key algorithm " + key.alg + " requires a different curve");
117
- try { nodeCrypto.createPublicKey({ key: toSpki(key, E, code), format: "der", type: "spki" }); }
117
+ try { guard.crypto.publicKey({ key: toSpki(key, E, code), format: "der", type: "spki" }); }
118
118
  catch (e) { throw bad("the credential public key point is not valid for its curve", e); }
119
119
  if (Number(key.kty) === 1 && !edwardsPoint.validate(key.x, key.crv)) throw bad("the OKP credential public key is not a valid, full-order Edwards point");
120
120
  return key;
@@ -26,12 +26,6 @@ var _charAt = intrinsic.uncurry(String.prototype.charAt);
26
26
  var _strToLowerCase = intrinsic.toLowerCase;
27
27
  var _strSlice = intrinsic.uncurry(String.prototype.slice);
28
28
  var _strSplit = intrinsic.uncurry(String.prototype.split);
29
- /** @internal The hash constructor and the methods of the object it returns. What a digest here
30
- * produces is a metadata lookup key and the identity two certificates are told apart by, so a
31
- * replaced constructor, update or digest decides which entry governs an attestation. */
32
- var _createHash = _nodeCrypto.createHash;
33
- var _hashUpdate = intrinsic.uncurry(_nodeCrypto.createHash("sha256").update);
34
- var _hashDigest = intrinsic.uncurry(_nodeCrypto.createHash("sha256").digest);
35
29
  var jose = require("./jose");
36
30
  var rfc3339 = require("./rfc3339");
37
31
  var constants = require("./constants");
@@ -324,7 +318,7 @@ function _verifyMetadataBlob(blob, opts) {
324
318
  });
325
319
  var leaf = chain[0];
326
320
  _assertLeafSigns(leaf);
327
- var leafAlg = (leaf.subjectPublicKeyInfo.algorithm || {}).name;
321
+ var leafAlg = (leaf.subjectPublicKeyInfo.algorithm || intrinsic.create(null)).name;
328
322
  var leafSchemes = typeof leafAlg === "string" ? LEAF_SCHEMES_BY_SPKI_ALG[leafAlg] : undefined;
329
323
  if (!leafSchemes || !leafSchemes[algRow.scheme]) {
330
324
  throw _err("webauthn/unsupported-algorithm", "the metadata BLOB alg " + header.alg + " does not match the x5c leaf key type " + intrinsic.stringify(leafAlg));
@@ -702,7 +696,9 @@ function certKeyIdentifier(cert) {
702
696
  if (!pk || !intrinsic.isBuffer(pk.bytes)) {
703
697
  throw _err("webauthn/bad-input", "certKeyIdentifier expects a parsed certificate carrying a subject public key");
704
698
  }
705
- return _hashDigest(_hashUpdate(_createHash("sha1"), pk.bytes), "hex");
699
+ /** @internal A metadata lookup key, and the identity two certificates are told apart by, so
700
+ * whatever computes it decides which entry governs an attestation. */
701
+ return guard.crypto.digest("sha1", pk.bytes, { output: "hex" });
706
702
  }
707
703
 
708
704
  function metadataForKeyIdentifier(metadata, keyId) {
package/lib/webauthn.js CHANGED
@@ -54,7 +54,6 @@ var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
54
54
  var _promiseReject = intrinsic.uncurry(intrinsic.promiseReject);
55
55
  var jose = require("./jose");
56
56
  var mds = require("./webauthn-mds");
57
- var nodeCrypto = require("crypto");
58
57
 
59
58
  var WebauthnError = frameworkError.WebauthnError;
60
59
  function _err(code, message, cause) { return new WebauthnError(code, message, cause); }
@@ -76,16 +75,10 @@ var _subtleVerify = intrinsic.uncurry(subtle.verify);
76
75
  * while the bytes handed in were never the credential's. */
77
76
  var _subtleImportKey = intrinsic.uncurry(subtle.importKey);
78
77
 
79
- /** @internal The hash constructor is captured, and so are the methods of the object it returns. Every
80
- * attestation comparison in this module rests on a digest, so a replaced `createHash`, or a replaced
81
- * `update` or `digest` on the object it hands back, chooses what the comparison sees. */
82
- var _createHash = nodeCrypto.createHash;
83
- function _sha(name, buf) {
84
- var h = _createHash(name);
85
- return _hashDigest(_hashUpdate(h, buf));
86
- }
87
- var _hashUpdate = intrinsic.uncurry(nodeCrypto.createHash("sha256").update);
88
- var _hashDigest = intrinsic.uncurry(nodeCrypto.createHash("sha256").digest);
78
+ /** @internal Every attestation comparison in this module rests on a digest, so whatever computes one
79
+ * chooses what the comparison sees. The guard owns the hash construction and the methods of the
80
+ * object it returns. */
81
+ function _sha(name, buf) { return guard.crypto.digest(name, buf); }
89
82
  /** @internal The comparison goes through the captured operation. This is what decides that an
90
83
  * attestation statement's key is the credential's key, so a replacement answering zero makes a
91
84
  * different key match. */
@@ -275,7 +268,7 @@ function _certKeyAlgNames(cose) {
275
268
  return [];
276
269
  }
277
270
  function _assertCertKeyAlgorithm(cert, cose, E) {
278
- var alg = (cert.subjectPublicKeyInfo && cert.subjectPublicKeyInfo.algorithm) || {};
271
+ var alg = (cert.subjectPublicKeyInfo && cert.subjectPublicKeyInfo.algorithm) || intrinsic.create(null);
279
272
  var want = _certKeyAlgNames(cose);
280
273
  if (!want.length || intrinsic.indexOf(want, alg.name) < 0) {
281
274
  throw E("webauthn/key-mismatch", "the attestation certificate carries a " + intrinsic.stringify(alg.name) +