@blamejs/pki 0.8.59 → 0.8.61
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 +57 -0
- package/lib/acme.js +15 -7
- package/lib/alt-sig.js +3 -1
- package/lib/asn1-der.js +82 -34
- package/lib/attrcert-sign.js +19 -16
- package/lib/byte-writer.js +63 -13
- package/lib/cbor-det.js +9 -3
- package/lib/cmc-build.js +26 -18
- package/lib/cmp-build.js +35 -33
- package/lib/cmp-session.js +19 -9
- package/lib/cms-decrypt.js +37 -23
- package/lib/cms-digest.js +2 -3
- package/lib/cms-encrypt.js +38 -33
- package/lib/cms-sign.js +65 -43
- package/lib/cms-verify.js +8 -4
- package/lib/composite-kem.js +28 -29
- package/lib/crl-sign.js +24 -17
- package/lib/crmf-sign.js +11 -17
- package/lib/csr-sign.js +5 -2
- package/lib/ct.js +38 -25
- package/lib/est.js +12 -9
- package/lib/guard-bytes.js +5 -1
- package/lib/guard-crypto.js +506 -14
- package/lib/guard-der.js +6 -1
- package/lib/guard-intrinsic.js +12 -4
- package/lib/guard-list.js +64 -0
- package/lib/guard-name.js +1 -1
- package/lib/guard-secret.js +3 -2
- package/lib/hpke.js +34 -27
- package/lib/http-digest.js +5 -7
- package/lib/http-transport.js +12 -3
- package/lib/inspect.js +10 -2
- package/lib/jose.js +3 -1
- package/lib/key.js +41 -29
- package/lib/lint.js +236 -152
- package/lib/mime.js +9 -5
- package/lib/ocsp.js +26 -24
- package/lib/path-validate.js +43 -13
- package/lib/pbes2.js +12 -9
- package/lib/pkcs11-uri.js +3 -1
- package/lib/pkcs12-build.js +19 -17
- package/lib/pki-build.js +17 -13
- package/lib/possession.js +3 -1
- package/lib/rc2.js +6 -2
- package/lib/revocation-fetch.js +78 -19
- package/lib/scep.js +21 -14
- package/lib/schema-attrcert.js +4 -2
- package/lib/schema-c509.js +94 -78
- package/lib/schema-cmc.js +5 -2
- package/lib/schema-cmp.js +6 -3
- package/lib/schema-cms.js +7 -4
- package/lib/schema-crl.js +1 -1
- package/lib/schema-crmf.js +1 -1
- package/lib/schema-csr.js +3 -3
- package/lib/schema-engine.js +36 -18
- package/lib/schema-pkcs1.js +7 -3
- package/lib/schema-pkcs12.js +9 -6
- package/lib/schema-pkix.js +19 -3
- package/lib/schema-sec1.js +4 -1
- package/lib/schema-tsp.js +7 -4
- package/lib/shbs.js +10 -9
- package/lib/sign-scheme.js +2 -2
- package/lib/sigstore.js +9 -9
- package/lib/smime.js +19 -16
- package/lib/tlog.js +20 -13
- package/lib/tuf.js +7 -4
- package/lib/validator-cose.js +2 -2
- package/lib/validator-keydesc.js +4 -1
- package/lib/validator-tls.js +7 -2
- package/lib/webauthn-mds.js +3 -7
- package/lib/webauthn.js +4 -11
- package/lib/webcrypto.js +70 -49
- package/lib/x509-sign.js +20 -15
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cbor-det.js
CHANGED
|
@@ -40,6 +40,12 @@ var constants = require("./constants");
|
|
|
40
40
|
var frameworkError = require("./framework-error");
|
|
41
41
|
var asn1 = require("./asn1-der");
|
|
42
42
|
var guard = require("./guard-all");
|
|
43
|
+
/** @internal The append every list in this module is built with. `push` STORES at the index, and a
|
|
44
|
+
* store walks the prototype chain for a setter, so an accessor inherited at an array index receives
|
|
45
|
+
* each element as it is appended and the index then reads back whatever its getter answers. The
|
|
46
|
+
* decoded children below are what every C509 field is read out of, so a replaced element there is a
|
|
47
|
+
* field this codec never decoded. */
|
|
48
|
+
var _push = guard.list.append;
|
|
43
49
|
|
|
44
50
|
var CborError = frameworkError.CborError;
|
|
45
51
|
|
|
@@ -141,7 +147,7 @@ function _decodeItem(buf, start, limit, depth, maxD, rules, state) {
|
|
|
141
147
|
var cp = contentStart;
|
|
142
148
|
for (var i = 0n; i < argument; i++) {
|
|
143
149
|
var el = _decodeItem(buf, cp, limit, depth + 1, maxD, rules, state);
|
|
144
|
-
children
|
|
150
|
+
_push(children, el.node);
|
|
145
151
|
cp = el.end;
|
|
146
152
|
}
|
|
147
153
|
end = cp;
|
|
@@ -158,7 +164,7 @@ function _decodeItem(buf, start, limit, depth, maxD, rules, state) {
|
|
|
158
164
|
if (cmp > 0) throw new CborError("cbor/unsorted-map-keys", "map keys are not in bytewise-ascending order");
|
|
159
165
|
if (cmp === 0) throw new CborError("cbor/duplicate-map-key", "duplicate map key");
|
|
160
166
|
}
|
|
161
|
-
children
|
|
167
|
+
_push(children, [k.node, v.node]);
|
|
162
168
|
}
|
|
163
169
|
end = mp;
|
|
164
170
|
} else if (mt === 6) {
|
|
@@ -682,7 +688,7 @@ var build = Object.assign(Object.create(null), {
|
|
|
682
688
|
enc = guard.list.sortedCopy(enc, function (a, b) { return guard.bytes.compare(a[0], b[0]); });
|
|
683
689
|
for (var i = 1; i < enc.length; i++) { if (guard.bytes.compare(enc[i - 1][0], enc[i][0]) === 0) throw new CborError("cbor/duplicate-map-key", "build.map: duplicate map key"); }
|
|
684
690
|
var flat = [_head(5, pairs.length, "build.map")];
|
|
685
|
-
enc.forEach(function (p) { flat
|
|
691
|
+
enc.forEach(function (p) { _push(flat, p[0]); _push(flat, p[1]); });
|
|
686
692
|
return _assertDecodes(Buffer.concat(flat), "build.map");
|
|
687
693
|
},
|
|
688
694
|
tag: function (tagNum, inner) { return _assertDecodes(Buffer.concat([_head(6, tagNum, "build.tag"), _cbItem(inner, "build.tag content")]), "build.tag"); },
|
package/lib/cmc-build.js
CHANGED
|
@@ -22,6 +22,9 @@ var key = require("./key");
|
|
|
22
22
|
var _createPublicKey = nodeCrypto.createPublicKey;
|
|
23
23
|
var guard = require("./guard-all");
|
|
24
24
|
var intrinsic = require("./guard-intrinsic");
|
|
25
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
26
|
+
* consults a setter inherited there, and these lists become the controls a request carries. */
|
|
27
|
+
var _push = intrinsic.append;
|
|
25
28
|
var _hasOwn = intrinsic.hasOwn;
|
|
26
29
|
var _isArray = intrinsic.isArray;
|
|
27
30
|
var _isBuffer = intrinsic.isBuffer;
|
|
@@ -449,7 +452,7 @@ function _assertSecret(secret, what) {
|
|
|
449
452
|
}
|
|
450
453
|
|
|
451
454
|
function _ownSecret(buf, owned) {
|
|
452
|
-
owned
|
|
455
|
+
_push(owned,buf);
|
|
453
456
|
return buf;
|
|
454
457
|
}
|
|
455
458
|
|
|
@@ -467,8 +470,8 @@ function identityProofV2(secret, reqSequenceBytes, identity) {
|
|
|
467
470
|
_ownSecret(Buffer.from(identity, "utf8"), owned),
|
|
468
471
|
]), owned);
|
|
469
472
|
}
|
|
470
|
-
key =
|
|
471
|
-
var witness =
|
|
473
|
+
key = guard.crypto.digest("sha256", material);
|
|
474
|
+
var witness = guard.crypto.hmac("sha256", key, reqSequenceBytes);
|
|
472
475
|
return b.sequence([algId(OID_SHA256), algId(OID_HMAC_SHA256), b.octetString(witness)]);
|
|
473
476
|
} finally {
|
|
474
477
|
if (key) guard.secret.zeroize(key, CmcError, "cmc/bad-input", "the Identity Proof MAC key");
|
|
@@ -538,7 +541,7 @@ function _provesRequestKey(opening, outgoing) {
|
|
|
538
541
|
/** @internal Keys of different types are never the same key, and comparing them through the
|
|
539
542
|
* runtime's equality leaves a fault queued in the crypto library that surfaces on the next
|
|
540
543
|
* unrelated key import in the process, so the type decides that case first. */
|
|
541
|
-
if (requested
|
|
544
|
+
if (guard.crypto.keyType(requested) !== guard.crypto.keyType(opening.key)) return false;
|
|
542
545
|
return intrinsic.keyEquals(requested, opening.key);
|
|
543
546
|
}
|
|
544
547
|
/** @internal A key the runtime engine cannot read has no KeyObject to compare, and a composite
|
|
@@ -604,7 +607,7 @@ async function _recoverProofValue(decoded, recipient, witnessHash) {
|
|
|
604
607
|
continue; // allow:swallow-unverified this recipient is not addressed to the supplied key; the witness decides
|
|
605
608
|
}
|
|
606
609
|
var candidate = opened.content;
|
|
607
|
-
var computed =
|
|
610
|
+
var computed = guard.crypto.digest(witnessHash, candidate);
|
|
608
611
|
var matches = computed.length === decoded.witness.length &&
|
|
609
612
|
guard.crypto.constantTimeEqual(computed, decoded.witness);
|
|
610
613
|
guard.secret.zeroize(computed, CmcError, "cmc/bad-input", "the computed witness");
|
|
@@ -671,7 +674,7 @@ async function answerPopChallenge(popChallenge, ids) {
|
|
|
671
674
|
* in the challenge at all: neither side re-encodes it. */
|
|
672
675
|
macKey = _bufferFrom(proofValue.length > POP_MAC_KEY_MAX_BYTES
|
|
673
676
|
? _subarray(proofValue, 0, POP_MAC_KEY_MAX_BYTES) : proofValue);
|
|
674
|
-
var thePOP =
|
|
677
|
+
var thePOP = guard.crypto.hmac(macHash, macKey, decoded.requestBytes);
|
|
675
678
|
return { algBytes: decoded.thePOPAlgID.bytes, thePOP: thePOP, ids: ids,
|
|
676
679
|
recipientSpki: await _openingPublicKey(recipient, recovered.recipientType),
|
|
677
680
|
challengedRequest: _requestIdentity(decoded.request) };
|
|
@@ -685,13 +688,18 @@ async function answerPopChallenge(popChallenge, ids) {
|
|
|
685
688
|
|
|
686
689
|
function popLinkWitnessV2(secret, R) {
|
|
687
690
|
_assertSecret(secret, "a POP Link Witness V2 control");
|
|
688
|
-
|
|
691
|
+
/** @internal The secret's octets, held so the finally can wipe them. Handing the string and an
|
|
692
|
+
* encoding to a digest instead leaves the copy inside the provider, where nothing clears it. */
|
|
693
|
+
var material = _bufferFrom(secret, "utf8");
|
|
694
|
+
var key = null;
|
|
689
695
|
try {
|
|
696
|
+
key = guard.crypto.digest("sha256", material);
|
|
690
697
|
/** @internal Inside the try, so a MAC that faults on its message still wipes the key above it. */
|
|
691
|
-
var witness =
|
|
698
|
+
var witness = guard.crypto.hmac("sha256", key, R);
|
|
692
699
|
return b.sequence([algId(OID_SHA256), algId(OID_HMAC_SHA256), b.octetString(witness)]);
|
|
693
700
|
} finally {
|
|
694
|
-
guard.secret.zeroize(
|
|
701
|
+
guard.secret.zeroize(material, CmcError, "cmc/bad-input", "the POP Link secret");
|
|
702
|
+
if (key !== null) guard.secret.zeroize(key, CmcError, "cmc/bad-input", "the POP Link MAC key");
|
|
695
703
|
}
|
|
696
704
|
}
|
|
697
705
|
|
|
@@ -804,15 +812,15 @@ async function _build(spec, signer, opts) {
|
|
|
804
812
|
|
|
805
813
|
_assertNoDuplicateBinding(callerControls, spec);
|
|
806
814
|
if (spec.transactionId != null) {
|
|
807
|
-
controls
|
|
815
|
+
_push(controls,encodeControl(OID_TRANSACTION_ID,
|
|
808
816
|
[b.integer(_asBigInt(spec.transactionId, "spec.transactionId"))], ids));
|
|
809
817
|
}
|
|
810
818
|
if (spec.senderNonce != null) {
|
|
811
|
-
controls
|
|
819
|
+
_push(controls,encodeControl(OID_SENDER_NONCE,
|
|
812
820
|
[b.octetString(_der(spec.senderNonce, "spec.senderNonce"))], ids));
|
|
813
821
|
}
|
|
814
822
|
if (spec.dataReturn != null) {
|
|
815
|
-
controls
|
|
823
|
+
_push(controls,encodeControl(OID_DATA_RETURN,
|
|
816
824
|
[b.octetString(_der(spec.dataReturn, "spec.dataReturn"))], ids));
|
|
817
825
|
}
|
|
818
826
|
|
|
@@ -839,8 +847,8 @@ async function _build(spec, signer, opts) {
|
|
|
839
847
|
if (identity != null && typeof identity !== "string") {
|
|
840
848
|
throw E("cmc/bad-input", "identityProof.identity is the UTF8String the Identification control carries");
|
|
841
849
|
}
|
|
842
|
-
if (identity != null) controls
|
|
843
|
-
controls
|
|
850
|
+
if (identity != null) _push(controls,encodeControl(OID_IDENTIFICATION, [b.utf8(identity)], ids));
|
|
851
|
+
_push(controls,encodeControl(OID_IDENTITY_PROOF_V2,
|
|
844
852
|
[identityProofV2(spec.identityProof.secret, reqSequenceBytes, carriedIdentity)], ids));
|
|
845
853
|
}
|
|
846
854
|
|
|
@@ -932,9 +940,9 @@ async function _build(spec, signer, opts) {
|
|
|
932
940
|
if (typeof rBytes !== "number" || !Number.isInteger(rBytes) || rBytes < 1) {
|
|
933
941
|
throw E("cmc/bad-input", "popLinkRandomBytes must be a positive integer");
|
|
934
942
|
}
|
|
935
|
-
var R =
|
|
936
|
-
controls
|
|
937
|
-
controls
|
|
943
|
+
var R = guard.crypto.randomBytes(rBytes);
|
|
944
|
+
_push(controls,encodeControl(OID_POP_LINK_RANDOM, [b.octetString(R)], ids));
|
|
945
|
+
_push(controls,encodeControl(O("id-cmc-popLinkWitnessV2"),
|
|
938
946
|
[popLinkWitnessV2(spec.popLink.secret, R)], ids));
|
|
939
947
|
}
|
|
940
948
|
|
|
@@ -1016,7 +1024,7 @@ function _copySigners(signer, owned) {
|
|
|
1016
1024
|
return c;
|
|
1017
1025
|
}
|
|
1018
1026
|
function _own(buf, isSecret) {
|
|
1019
|
-
if (isSecret && owned) owned
|
|
1027
|
+
if (isSecret && owned) _push(owned,buf);
|
|
1020
1028
|
return buf;
|
|
1021
1029
|
}
|
|
1022
1030
|
return Array.isArray(signer) ? guard.list.copyMap(signer, one) : one(signer);
|
package/lib/cmp-build.js
CHANGED
|
@@ -56,7 +56,9 @@ var _bufferConcat = intrinsic.bufferConcat;
|
|
|
56
56
|
var _map = intrinsic.map;
|
|
57
57
|
var _keys = intrinsic.keys;
|
|
58
58
|
var _forEach = intrinsic.forEach;
|
|
59
|
-
|
|
59
|
+
/** @internal `append`, not a captured `push`: a store at an index walks the prototype chain for a
|
|
60
|
+
* setter, so an accessor there takes the element being appended. `append` defines it. */
|
|
61
|
+
var _push = intrinsic.append;
|
|
60
62
|
var _stringify = intrinsic.stringify;
|
|
61
63
|
var _bigInt = intrinsic.BigInt;
|
|
62
64
|
var _Date = intrinsic.Date;
|
|
@@ -167,7 +169,7 @@ function _encodeInfoTypeAndValue(itav) {
|
|
|
167
169
|
if (typeof itav.infoType !== "string" || O(itav.infoType) == null) throw _err("cmp/bad-name", "unknown infoType " + guard.text.showValue(itav.infoType));
|
|
168
170
|
var children = [b.oid(O(itav.infoType))];
|
|
169
171
|
var val = _encodeInfoValue(itav);
|
|
170
|
-
if (val != null) children
|
|
172
|
+
if (val != null) _push(children,val);
|
|
171
173
|
return b.sequence(children);
|
|
172
174
|
}
|
|
173
175
|
|
|
@@ -238,16 +240,16 @@ function _encodeHeader(headerSpec, protectionAlgDer, pvno) {
|
|
|
238
240
|
];
|
|
239
241
|
if (headerSpec.messageTime != null) {
|
|
240
242
|
guard.time.assertEncodable(headerSpec.messageTime, _err, "cmp/bad-input", "header.messageTime");
|
|
241
|
-
children
|
|
243
|
+
_push(children,b.explicit(0, b.generalizedTime(headerSpec.messageTime)));
|
|
242
244
|
}
|
|
243
|
-
if (protectionAlgDer != null) children
|
|
244
|
-
if (headerSpec.senderKID != null) children
|
|
245
|
-
if (headerSpec.recipKID != null) children
|
|
246
|
-
if (headerSpec.transactionID != null) children
|
|
247
|
-
if (headerSpec.senderNonce != null) children
|
|
248
|
-
if (headerSpec.recipNonce != null) children
|
|
249
|
-
if (headerSpec.freeText != null) children
|
|
250
|
-
if (headerSpec.generalInfo != null) children
|
|
245
|
+
if (protectionAlgDer != null) _push(children,b.explicit(1, b.raw(protectionAlgDer)));
|
|
246
|
+
if (headerSpec.senderKID != null) _push(children,b.explicit(2, _reqOctets(headerSpec.senderKID, "header.senderKID")));
|
|
247
|
+
if (headerSpec.recipKID != null) _push(children,b.explicit(3, _reqOctets(headerSpec.recipKID, "header.recipKID")));
|
|
248
|
+
if (headerSpec.transactionID != null) _push(children,b.explicit(4, _reqOctets(headerSpec.transactionID, "header.transactionID")));
|
|
249
|
+
if (headerSpec.senderNonce != null) _push(children,b.explicit(5, _reqOctets(headerSpec.senderNonce, "header.senderNonce")));
|
|
250
|
+
if (headerSpec.recipNonce != null) _push(children,b.explicit(6, _reqOctets(headerSpec.recipNonce, "header.recipNonce")));
|
|
251
|
+
if (headerSpec.freeText != null) _push(children,b.explicit(7, _encodePkiFreeText(headerSpec.freeText, "cmp/bad-freetext", "header.freeText")));
|
|
252
|
+
if (headerSpec.generalInfo != null) _push(children,b.explicit(8, _encodeGeneralInfo(headerSpec.generalInfo, "cmp/bad-general-info", "header.generalInfo")));
|
|
251
253
|
return b.sequence(children);
|
|
252
254
|
}
|
|
253
255
|
|
|
@@ -267,8 +269,8 @@ function _encodePkiStatusInfo(si, code) {
|
|
|
267
269
|
if (!si || typeof si !== "object" || Buffer.isBuffer(si)) throw _err(code, "a PKIStatusInfo must be an object { status, statusString?, failInfo? }");
|
|
268
270
|
if (typeof si.status !== "number" || !Number.isInteger(si.status) || si.status < 0 || si.status > 6) throw _err(code, "statusInfo.status must be a PKIStatus 0..6 (RFC 9810 sec. 5.2.3)");
|
|
269
271
|
var children = [b.integer(BigInt(si.status))];
|
|
270
|
-
if (si.statusString != null) children
|
|
271
|
-
if (si.failInfo != null) children
|
|
272
|
+
if (si.statusString != null) _push(children,_encodePkiFreeText(si.statusString, code, "statusInfo.statusString"));
|
|
273
|
+
if (si.failInfo != null) _push(children,_encodeFailInfo(si.failInfo, code));
|
|
272
274
|
return b.sequence(children);
|
|
273
275
|
}
|
|
274
276
|
|
|
@@ -278,10 +280,10 @@ function _encodeCertStatus(cs, state) {
|
|
|
278
280
|
_reqOctets(cs.certHash, "certConf certHash"),
|
|
279
281
|
b.integer(_reqIdInt(cs.certReqId, "cmp/bad-cert-status", "CertStatus certReqId")),
|
|
280
282
|
];
|
|
281
|
-
if (cs.statusInfo != null) children
|
|
283
|
+
if (cs.statusInfo != null) _push(children,_encodePkiStatusInfo(cs.statusInfo, "cmp/bad-cert-status"));
|
|
282
284
|
if (cs.hashAlg != null) {
|
|
283
285
|
if (typeof cs.hashAlg !== "string" || !CERT_CONF_HASH_ALGS[cs.hashAlg]) throw _err("cmp/bad-name", "certConf hashAlg must be a hash algorithm (sha256 / sha384 / sha512 / sha3-256 / sha3-512 / sha1); got " + guard.text.showValue(cs.hashAlg));
|
|
284
|
-
children
|
|
286
|
+
_push(children,b.explicit(0, _algIdNoParams(cs.hashAlg)));
|
|
285
287
|
state.usesCmp2021 = true;
|
|
286
288
|
}
|
|
287
289
|
return b.sequence(children);
|
|
@@ -317,7 +319,7 @@ function _encodeRevDetails(rd) {
|
|
|
317
319
|
certDetails = b.raw(crmf.buildCertTemplate(rd.certDetails));
|
|
318
320
|
}
|
|
319
321
|
var children = [certDetails];
|
|
320
|
-
if (rd.crlEntryDetails != null) children
|
|
322
|
+
if (rd.crlEntryDetails != null) _push(children,b.raw(_encodeCrlEntryDetails(rd.crlEntryDetails)));
|
|
321
323
|
return b.sequence(children);
|
|
322
324
|
}
|
|
323
325
|
|
|
@@ -419,12 +421,12 @@ function _encodeCertifiedKeyPair(ckp, state) {
|
|
|
419
421
|
var children = [_encodeCertOrEncCert(ckp, state)];
|
|
420
422
|
if (ckp.privateKey != null) {
|
|
421
423
|
var pk = _b.reqDer(ckp.privateKey, "certifiedKeyPair.privateKey (a pre-encoded EncryptedKey DER)");
|
|
422
|
-
children
|
|
424
|
+
_push(children,b.explicit(0, b.raw(pk)));
|
|
423
425
|
var pkNode;
|
|
424
426
|
try { pkNode = asn1.decode(pk); } catch (e) { throw _err("cmp/bad-cert-response", "certifiedKeyPair.privateKey is not valid DER", e); }
|
|
425
427
|
if (pkNode.tagClass === "context" && pkNode.tagNumber === 0) state.usesCmp2021 = true;
|
|
426
428
|
}
|
|
427
|
-
if (ckp.publicationInfo != null) children
|
|
429
|
+
if (ckp.publicationInfo != null) _push(children,b.explicit(1, b.raw(_b.reqDer(ckp.publicationInfo, "certifiedKeyPair.publicationInfo (a pre-encoded DER)"))));
|
|
428
430
|
return b.sequence(children);
|
|
429
431
|
}
|
|
430
432
|
|
|
@@ -435,8 +437,8 @@ function _encodeCertResponse(cr, state) {
|
|
|
435
437
|
if (hasCkp && cr.status.failInfo != null) throw _err("cmp/bad-cert-response", "a CertResponse must not carry both failInfo and certifiedKeyPair");
|
|
436
438
|
if (hasCkp && cr.status.status !== 0 && cr.status.status !== 1) throw _err("cmp/bad-cert-response", "a CertResponse certifiedKeyPair is allowed only under a granting status (accepted / grantedWithMods)");
|
|
437
439
|
var children = [b.integer(_reqIdInt(cr.certReqId, "cmp/bad-cert-response", "CertResponse certReqId")), _encodePkiStatusInfo(cr.status, "cmp/bad-cert-response")];
|
|
438
|
-
if (hasCkp) children
|
|
439
|
-
if (cr.rspInfo != null) children
|
|
440
|
+
if (hasCkp) _push(children,_encodeCertifiedKeyPair(cr.certifiedKeyPair, state));
|
|
441
|
+
if (cr.rspInfo != null) _push(children,_reqOctets(cr.rspInfo, "CertResponse.rspInfo"));
|
|
440
442
|
return b.sequence(children);
|
|
441
443
|
}
|
|
442
444
|
|
|
@@ -446,9 +448,9 @@ function _encodeCertRepMessage(spec, state, arm) {
|
|
|
446
448
|
var children = [];
|
|
447
449
|
if (spec.caPubs != null) {
|
|
448
450
|
if (!Array.isArray(spec.caPubs) || !spec.caPubs.length) throw _err("cmp/bad-cert-rep", arm + ".caPubs must be a non-empty array of certificate DERs");
|
|
449
|
-
children
|
|
451
|
+
_push(children,b.explicit(1, b.sequence(spec.caPubs.map(function (c) { return _certRaw(c, "cmp/bad-cert-rep", "a caPubs entry"); }))));
|
|
450
452
|
}
|
|
451
|
-
children
|
|
453
|
+
_push(children,b.sequence(spec.response.map(function (cr) { return _encodeCertResponse(cr, state); })));
|
|
452
454
|
return b.sequence(children);
|
|
453
455
|
}
|
|
454
456
|
|
|
@@ -468,11 +470,11 @@ function _encodeRevRepContent(spec) {
|
|
|
468
470
|
var children = [b.sequence(spec.status.map(function (si) { return _encodePkiStatusInfo(si, "cmp/bad-rev-rep"); }))];
|
|
469
471
|
if (spec.revCerts != null) {
|
|
470
472
|
if (!Array.isArray(spec.revCerts) || !spec.revCerts.length) throw _err("cmp/bad-rev-rep", "rp.revCerts must be a non-empty array of CertId");
|
|
471
|
-
children
|
|
473
|
+
_push(children,b.explicit(0, b.sequence(spec.revCerts.map(_encodeCertIdRr))));
|
|
472
474
|
}
|
|
473
475
|
if (spec.crls != null) {
|
|
474
476
|
if (!Array.isArray(spec.crls) || !spec.crls.length) throw _err("cmp/bad-rev-rep", "rp.crls must be a non-empty array of CertificateList DERs");
|
|
475
|
-
children
|
|
477
|
+
_push(children,b.explicit(1, b.sequence(spec.crls.map(function (c) {
|
|
476
478
|
var der = _b.reqDerSequence(c, "crls entry (a CertificateList DER)");
|
|
477
479
|
try { crl.parse(der); }
|
|
478
480
|
catch (e) { if (e instanceof CmpError) throw e; throw _err("cmp/bad-rev-rep", "a crls entry is not a valid CRL (CertificateList)", e); }
|
|
@@ -486,8 +488,8 @@ function _encodeErrorMsgContent(spec) {
|
|
|
486
488
|
if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("cmp/bad-error", "error must be an object { pKIStatusInfo, errorCode?, errorDetails? }");
|
|
487
489
|
if (spec.pKIStatusInfo == null) throw _err("cmp/bad-error", "error.pKIStatusInfo is required");
|
|
488
490
|
var children = [_encodePkiStatusInfo(spec.pKIStatusInfo, "cmp/bad-error")];
|
|
489
|
-
if (spec.errorCode != null) children
|
|
490
|
-
if (spec.errorDetails != null) children
|
|
491
|
+
if (spec.errorCode != null) _push(children,b.integer(_reqIdInt(spec.errorCode, "cmp/bad-error", "error.errorCode")));
|
|
492
|
+
if (spec.errorDetails != null) _push(children,_encodePkiFreeText(spec.errorDetails, "cmp/bad-error", "error.errorDetails"));
|
|
491
493
|
return b.sequence(children);
|
|
492
494
|
}
|
|
493
495
|
|
|
@@ -497,7 +499,7 @@ function _encodePollRepContent(list) {
|
|
|
497
499
|
if (!pr || typeof pr !== "object" || Buffer.isBuffer(pr)) throw _err("cmp/bad-poll-rep", "each pollRep entry must be an object");
|
|
498
500
|
if (typeof pr.checkAfter !== "number" || !Number.isInteger(pr.checkAfter) || pr.checkAfter < 0 || pr.checkAfter > 0x7fffffff) throw _err("cmp/bad-poll-rep", "pollRep checkAfter must be a non-negative uint31 delay in seconds (RFC 9810 sec. 5.3.22)");
|
|
499
501
|
var kids = [b.integer(_reqIdInt(pr.certReqId, "cmp/bad-poll-rep", "pollRep certReqId")), b.integer(BigInt(pr.checkAfter))];
|
|
500
|
-
if (pr.reason != null) kids
|
|
502
|
+
if (pr.reason != null) _push(kids,_encodePkiFreeText(pr.reason, "cmp/bad-poll-rep", "pollRep reason"));
|
|
501
503
|
return b.sequence(kids);
|
|
502
504
|
}));
|
|
503
505
|
}
|
|
@@ -506,14 +508,14 @@ function _encodeKeyRecRepContent(spec, state) {
|
|
|
506
508
|
if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("cmp/bad-key-rec-rep", "krp must be an object { status, newSigCert?, caCerts?, keyPairHist? }");
|
|
507
509
|
if (spec.status == null) throw _err("cmp/bad-key-rec-rep", "krp.status is required");
|
|
508
510
|
var children = [_encodePkiStatusInfo(spec.status, "cmp/bad-key-rec-rep")];
|
|
509
|
-
if (spec.newSigCert != null) children
|
|
511
|
+
if (spec.newSigCert != null) _push(children,b.explicit(0, _certRaw(spec.newSigCert, "cmp/bad-key-rec-rep", "krp.newSigCert")));
|
|
510
512
|
if (spec.caCerts != null) {
|
|
511
513
|
if (!Array.isArray(spec.caCerts) || !spec.caCerts.length) throw _err("cmp/bad-key-rec-rep", "krp.caCerts must be a non-empty array of certificate DERs");
|
|
512
|
-
children
|
|
514
|
+
_push(children,b.explicit(1, b.sequence(spec.caCerts.map(function (c) { return _certRaw(c, "cmp/bad-key-rec-rep", "a krp.caCerts entry"); }))));
|
|
513
515
|
}
|
|
514
516
|
if (spec.keyPairHist != null) {
|
|
515
517
|
if (!Array.isArray(spec.keyPairHist) || !spec.keyPairHist.length) throw _err("cmp/bad-key-rec-rep", "krp.keyPairHist must be a non-empty array of CertifiedKeyPair");
|
|
516
|
-
children
|
|
518
|
+
_push(children,b.explicit(2, b.sequence(spec.keyPairHist.map(function (ckp) { return _encodeCertifiedKeyPair(ckp, state); }))));
|
|
517
519
|
}
|
|
518
520
|
return b.sequence(children);
|
|
519
521
|
}
|
|
@@ -891,7 +893,7 @@ async function _build(message, opts) {
|
|
|
891
893
|
var protectionBits = await prot.computeBits(protectedPartDer);
|
|
892
894
|
var msgChildren = [headerTLV, bodyTLV, b.explicit(0, protectionBits)];
|
|
893
895
|
var extraCertsDers = _collectExtraCerts(opts, prot.certDer);
|
|
894
|
-
if (extraCertsDers.length) msgChildren
|
|
896
|
+
if (extraCertsDers.length) _push(msgChildren,b.explicit(1, b.sequence(extraCertsDers)));
|
|
895
897
|
var der = b.sequence(msgChildren);
|
|
896
898
|
|
|
897
899
|
cmp.parse(der);
|
|
@@ -905,14 +907,14 @@ async function _build(message, opts) {
|
|
|
905
907
|
|
|
906
908
|
function _collectExtraCerts(opts, protCertDer) {
|
|
907
909
|
var out = [];
|
|
908
|
-
if (protCertDer != null) out
|
|
910
|
+
if (protCertDer != null) _push(out,b.raw(protCertDer));
|
|
909
911
|
if (opts.extraCerts != null) {
|
|
910
912
|
if (!Array.isArray(opts.extraCerts)) throw _err("cmp/bad-extra-certs", "opts.extraCerts must be an array of certificate DERs");
|
|
911
913
|
opts.extraCerts.forEach(function (c) {
|
|
912
914
|
var der = _b.reqDerSequence(c, "extraCerts entry (a Certificate DER)");
|
|
913
915
|
try { x509.parse(der); }
|
|
914
916
|
catch (e) { if (e instanceof CmpError) throw e; throw _err("cmp/bad-extra-certs", "an extraCerts entry is not a valid X.509 certificate", e); }
|
|
915
|
-
out
|
|
917
|
+
_push(out,b.raw(der));
|
|
916
918
|
});
|
|
917
919
|
}
|
|
918
920
|
return out;
|
package/lib/cmp-session.js
CHANGED
|
@@ -15,6 +15,10 @@ var crmfSign = require("./crmf-sign");
|
|
|
15
15
|
var cmpBuild = require("./cmp-build");
|
|
16
16
|
var csr = require("./schema-csr");
|
|
17
17
|
var guard = require("./guard-all");
|
|
18
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
19
|
+
* consults a setter inherited there, and one of these lists is the accumulated CA certificates a
|
|
20
|
+
* later leg is authenticated against. */
|
|
21
|
+
var _push = guard.list.append;
|
|
18
22
|
var compositeSig = require("./composite-sig");
|
|
19
23
|
var constants = require("./constants");
|
|
20
24
|
var webcrypto = require("./webcrypto");
|
|
@@ -34,7 +38,7 @@ function setEngine(engine) { _engine = engine; }
|
|
|
34
38
|
var HASHLESS_SIG_OIDS = Object.create(null);
|
|
35
39
|
(function () {
|
|
36
40
|
var names = ["Ed25519", "Ed448", "id-ml-dsa-44", "id-ml-dsa-65", "id-ml-dsa-87"];
|
|
37
|
-
["sha2", "shake"].forEach(function (h) { ["128s", "128f", "192s", "192f", "256s", "256f"].forEach(function (s) { names
|
|
41
|
+
["sha2", "shake"].forEach(function (h) { ["128s", "128f", "192s", "192f", "256s", "256f"].forEach(function (s) { _push(names,"id-slh-dsa-" + h + "-" + s); }); });
|
|
38
42
|
names.forEach(function (n) { var o = oid.byName(n); if (o) HASHLESS_SIG_OIDS[o] = 1; });
|
|
39
43
|
})();
|
|
40
44
|
var SIG_OID_TO_HASH = Object.create(null);
|
|
@@ -136,11 +140,11 @@ function _asCertList(v) { return v == null ? [] : (Array.isArray(v) ? guard.list
|
|
|
136
140
|
function _boundedPool(base, added) {
|
|
137
141
|
var ceiling = constants.LIMITS.PATH_BUILD_MAX_CANDIDATES;
|
|
138
142
|
var seen = Object.create(null), distinctBase = [];
|
|
139
|
-
base.forEach(function (c) { var k = _certIdentity(c); if (k == null) { distinctBase
|
|
143
|
+
base.forEach(function (c) { var k = _certIdentity(c); if (k == null) { _push(distinctBase,c); return; } if (!seen[k]) { seen[k] = 1; _push(distinctBase,c); } });
|
|
140
144
|
var room = ceiling - distinctBase.length;
|
|
141
145
|
if (room <= 0) return distinctBase;
|
|
142
146
|
var useful = [];
|
|
143
|
-
added.forEach(function (c) { var k = _certIdentity(c); if (k != null && seen[k]) return; if (k != null) seen[k] = 1; useful
|
|
147
|
+
added.forEach(function (c) { var k = _certIdentity(c); if (k != null && seen[k]) return; if (k != null) seen[k] = 1; _push(useful,c); });
|
|
144
148
|
return distinctBase.concat(useful.slice(0, room));
|
|
145
149
|
}
|
|
146
150
|
function _certIdentity(cert) {
|
|
@@ -228,7 +232,7 @@ function _responseExtraCerts(responseBytes) {
|
|
|
228
232
|
seen[key] = true;
|
|
229
233
|
try { x509.parse(c); }
|
|
230
234
|
catch (_e) { continue; }
|
|
231
|
-
out
|
|
235
|
+
_push(out,Buffer.from(c));
|
|
232
236
|
}
|
|
233
237
|
return out;
|
|
234
238
|
}
|
|
@@ -682,7 +686,13 @@ function session(opts) {
|
|
|
682
686
|
if (!Buffer.isBuffer(c)) return;
|
|
683
687
|
var k = c.toString("base64");
|
|
684
688
|
if (caPubsSeen[k]) {
|
|
685
|
-
|
|
689
|
+
/** @internal The entry is read, removed and appended as three steps rather than through
|
|
690
|
+
* `splice`. `splice` RETURNS what it removed, so it builds that result through
|
|
691
|
+
* `ArraySpeciesCreate`, which reads the receiver's `constructor` and then its
|
|
692
|
+
* `Symbol.species`: a getter installed on `Array.prototype.constructor` is handed this
|
|
693
|
+
* accumulator as its `this` while the move is in progress, and the list holds the CA
|
|
694
|
+
* certificates a later leg is authenticated against. */
|
|
695
|
+
if (isGrantLeg) { for (var pi = 0; pi < caPubsAccum.length; pi++) { if (caPubsAccum[pi].toString("base64") === k) { var moved = caPubsAccum[pi]; guard.list.removeAt(caPubsAccum, pi); guard.list.append(caPubsAccum, moved); if (pi < caPubsWaitingCount) caPubsWaitingCount--; break; } } }
|
|
686
696
|
return;
|
|
687
697
|
}
|
|
688
698
|
try { x509.parse(c); } catch (e) { throw _err("cmp/bad-cert-response", "an authenticated caPubs entry is not a valid X.509 certificate", e); }
|
|
@@ -963,10 +973,10 @@ function session(opts) {
|
|
|
963
973
|
async function _validateLeaf(leaf, caPubs) {
|
|
964
974
|
if (_engine == null || opts.trustAnchors == null) return;
|
|
965
975
|
var caPubsList = [];
|
|
966
|
-
if (Array.isArray(caPubs)) caPubs.forEach(function (c) { if (Buffer.isBuffer(c)) caPubsList
|
|
976
|
+
if (Array.isArray(caPubs)) caPubs.forEach(function (c) { if (Buffer.isBuffer(c)) _push(caPubsList,c); });
|
|
967
977
|
var added = _asCertList(opts.intermediates);
|
|
968
|
-
if (Buffer.isBuffer(cachedSignerCert)) added
|
|
969
|
-
cachedChain.forEach(function (c) { if (Buffer.isBuffer(c)) added
|
|
978
|
+
if (Buffer.isBuffer(cachedSignerCert)) _push(added,cachedSignerCert);
|
|
979
|
+
cachedChain.forEach(function (c) { if (Buffer.isBuffer(c)) _push(added,c); });
|
|
970
980
|
var pool = _boundedPool(caPubsList, added);
|
|
971
981
|
var res;
|
|
972
982
|
try { res = await _engine.build(leaf, { trustAnchors: _asCertList(opts.trustAnchors), intermediates: pool, validate: true, time: opts.time != null ? opts.time : new Date() }); }
|
|
@@ -1395,7 +1405,7 @@ function session(opts) {
|
|
|
1395
1405
|
if (limit.total !== null && total > limit.total) {
|
|
1396
1406
|
throw _err("cmp/bad-input", "the resumeToken " + name + " exceeds the " + limit.total + "-byte pool a transaction holds");
|
|
1397
1407
|
}
|
|
1398
|
-
out
|
|
1408
|
+
_push(out,der);
|
|
1399
1409
|
}
|
|
1400
1410
|
return out;
|
|
1401
1411
|
}
|
package/lib/cms-decrypt.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
// @primitive pki.cms.decrypt block live in cms-verify.js, which re-exports this function.
|
|
6
6
|
|
|
7
|
-
var nodeCrypto = require("crypto");
|
|
8
7
|
var asn1 = require("./asn1-der");
|
|
9
8
|
var oid = require("./oid");
|
|
10
9
|
var x509 = require("./schema-x509");
|
|
@@ -121,10 +120,10 @@ function _selectCandidates(recips, km, opts) {
|
|
|
121
120
|
for (var k = 0; k < recips.length; k++) {
|
|
122
121
|
var r = recips[k];
|
|
123
122
|
if (want === "asym" && (r.type === "ktri" || r.type === "kari" || r.type === "ori")) {
|
|
124
|
-
if (cert && _ridMatches(r, cert))
|
|
125
|
-
} else if (want === "pwri" && r.type === "pwri") {
|
|
123
|
+
if (cert && _ridMatches(r, cert)) intrinsic.append(out, { ri: r, index: k });
|
|
124
|
+
} else if (want === "pwri" && r.type === "pwri") { intrinsic.append(out, { ri: r, index: k }); }
|
|
126
125
|
else if (want === "kekri" && r.type === "kekri") {
|
|
127
|
-
if (km.kekId == null || _kekIdMatches(r, km.kekId))
|
|
126
|
+
if (km.kekId == null || _kekIdMatches(r, km.kekId)) intrinsic.append(out, { ri: r, index: k });
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
if (!out.length) throw _err("cms/no-matching-recipient", "no recipient matches the supplied key material");
|
|
@@ -235,8 +234,8 @@ async function _ktriCek(ri, km) {
|
|
|
235
234
|
return Buffer.from(await subtle.decrypt({ name: "RSA-OAEP" }, pub, ri.encryptedKey));
|
|
236
235
|
}
|
|
237
236
|
if (kea.oid === O("rsaEncryption")) {
|
|
238
|
-
var keyObj =
|
|
239
|
-
try { return
|
|
237
|
+
var keyObj = guard.crypto.privateKey({ key: k.der, format: "der", type: "pkcs8" });
|
|
238
|
+
try { return guard.crypto.privateDecrypt({ key: keyObj, padding: guard.crypto.padding.RSA_PKCS1 }, ri.encryptedKey); }
|
|
240
239
|
catch (_e) {
|
|
241
240
|
return null;
|
|
242
241
|
}
|
|
@@ -339,7 +338,7 @@ async function _pwriCek(ri, km, opts) {
|
|
|
339
338
|
var iv = asn1.read.octetString(inner.children[1]);
|
|
340
339
|
var pw = pbes2.passwordBytesOwned(km.password, _err, "cms");
|
|
341
340
|
var kek;
|
|
342
|
-
try { kek =
|
|
341
|
+
try { kek = guard.crypto.pbkdf2(pw.bytes, pb.salt, pb.iterations, innerBits / 8, pb.prfNode); }
|
|
343
342
|
finally { if (pw.owned) guard.secret.zeroize(pw.bytes, CmsError, "cms/bad-input", "the password encoding"); }
|
|
344
343
|
try {
|
|
345
344
|
return _pwriUnwrap(kek, ri.encryptedKey, iv, innerBits);
|
|
@@ -421,7 +420,7 @@ async function _openContent(parsed, eci, cek, ct, streamMode) {
|
|
|
421
420
|
}
|
|
422
421
|
if (eci.encryptedContent == null) throw _err("cms/no-encrypted-content", "the message has no encryptedContent (detached; supply it out of band)");
|
|
423
422
|
var substitute = null;
|
|
424
|
-
if (cek == null || cek.length !== keyBits / 8) { substitute =
|
|
423
|
+
if (cek == null || cek.length !== keyBits / 8) { substitute = guard.crypto.randomBytes(keyBits / 8); cek = substitute; }
|
|
425
424
|
try {
|
|
426
425
|
if (ct === "authEnvelopedData") {
|
|
427
426
|
var aad = parsed.authAttrsBytes != null ? _explicitSetOf(parsed.authAttrsBytes) : Buffer.alloc(0);
|
|
@@ -461,9 +460,9 @@ async function _openContent(parsed, eci, cek, ct, streamMode) {
|
|
|
461
460
|
}
|
|
462
461
|
function _gcmOpen(cek, nonce, ct, tag, aad, keyBits, icvLen) {
|
|
463
462
|
if (!tag || tag.length !== icvLen) throw _fail();
|
|
464
|
-
var d =
|
|
465
|
-
|
|
466
|
-
if (aad && aad.length)
|
|
463
|
+
var d = guard.crypto.decipher("aes-" + keyBits + "-gcm", cek, nonce, { authTagLength: icvLen });
|
|
464
|
+
guard.crypto.decipherSetAuthTag(d, tag);
|
|
465
|
+
if (aad && aad.length) guard.crypto.transformSetAAD(d, aad);
|
|
467
466
|
return guard.secret.cipherFinish(d, ct, CmsError, "cms/decrypt-failed", "the recovered content");
|
|
468
467
|
}
|
|
469
468
|
|
|
@@ -543,7 +542,7 @@ async function _decryptPbes2(parsed, eci, km, opts) {
|
|
|
543
542
|
if (!keyBits) throw _err("cms/unsupported-algorithm", "unsupported PBES2 content cipher " + encOid);
|
|
544
543
|
var pwE = pbes2.passwordBytesOwned(km.password, _err, "cms");
|
|
545
544
|
var key;
|
|
546
|
-
try { key =
|
|
545
|
+
try { key = guard.crypto.pbkdf2(pwE.bytes, pb.salt, pb.iterations, keyBits / 8, pb.prfNode); }
|
|
547
546
|
finally { if (pwE.owned) guard.secret.zeroize(pwE.bytes, CmsError, "cms/bad-input", "the password encoding"); }
|
|
548
547
|
try { return _originFields({ content: opts.stream === true ? _cbcStream(key, iv, eci.encryptedContent, keyBits) : pbes2.cbcDecrypt(key, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed"), contentType: eci.contentType, contentTypeName: oid.name(eci.contentType) || eci.contentType, recipientType: "password", recipientIndex: -1, contentEncryptionAlgorithm: oid.name(encOid) || encOid, authenticated: false }, null, null); }
|
|
549
548
|
catch (_e) { throw _fail(); }
|
|
@@ -563,19 +562,23 @@ async function _aesKwUnwrap(kek, wrapped) {
|
|
|
563
562
|
guard.secret.zeroize(new Uint8Array(rawAb), CmsError, "cms/bad-input", "the unwrapped content key");
|
|
564
563
|
}
|
|
565
564
|
}
|
|
565
|
+
/** @internal Both KDF-input structures APPEND by defining the index. A store at an index the list
|
|
566
|
+
* has no own property at consults a setter inherited from the array prototype, so an accessor there
|
|
567
|
+
* would decide the SharedInfo these bytes become and derive a different KEK from the same shared
|
|
568
|
+
* secret. */
|
|
566
569
|
function _eccSharedInfo(wrapName, ukm, kekBytes) {
|
|
567
570
|
var kids = [b.sequence([b.oid(O(wrapName))])];
|
|
568
|
-
if (ukm)
|
|
571
|
+
if (ukm) intrinsic.append(kids, b.explicit(0, b.octetString(ukm)));
|
|
569
572
|
/** @internal The SuppPubInfo key length is written with the capture, for the same reason the
|
|
570
573
|
* encrypt side does it: this is KDF input, and a replaced write derives a different KEK from the
|
|
571
574
|
* same shared secret. */
|
|
572
575
|
var supp = Buffer.alloc(4); intrinsic.writeUInt32BE(supp, kekBytes * 8, 0);
|
|
573
|
-
|
|
576
|
+
intrinsic.append(kids, b.explicit(2, b.octetString(supp)));
|
|
574
577
|
return b.sequence(kids);
|
|
575
578
|
}
|
|
576
579
|
function _kemOtherInfo(wrapName, kekBytes, ukm) {
|
|
577
580
|
var kids = [b.sequence([b.oid(O(wrapName))]), b.integer(BigInt(kekBytes))];
|
|
578
|
-
if (ukm)
|
|
581
|
+
if (ukm) intrinsic.append(kids, b.explicit(0, b.octetString(ukm)));
|
|
579
582
|
return b.sequence(kids);
|
|
580
583
|
}
|
|
581
584
|
function _pwriUnwrap(kek, wrapped, iv, keyBits) {
|
|
@@ -584,13 +587,13 @@ function _pwriUnwrap(kek, wrapped, iv, keyBits) {
|
|
|
584
587
|
var n = wrapped.length;
|
|
585
588
|
var lastDec = null, pass1 = null, body = null;
|
|
586
589
|
try {
|
|
587
|
-
var ecb =
|
|
590
|
+
var ecb = guard.crypto.decipher("aes-" + keyBits + "-ecb", kek, Buffer.alloc(0)); guard.crypto.transformNoPadding(ecb);
|
|
588
591
|
lastDec = guard.secret.cipherFinish(ecb, wrapped.subarray(n - blk), CmsError, "cms/decrypt-failed", "the recovered CBC last block");
|
|
589
592
|
var iv2 = Buffer.alloc(blk);
|
|
590
593
|
for (var i = 0; i < blk; i++) iv2[i] = lastDec[i] ^ wrapped[n - 2 * blk + i];
|
|
591
|
-
var d1 =
|
|
594
|
+
var d1 = guard.crypto.decipher(alg, kek, iv2); guard.crypto.transformNoPadding(d1);
|
|
592
595
|
pass1 = guard.secret.cipherFinish(d1, wrapped, CmsError, "cms/decrypt-failed", "the PWRI first-pass plaintext");
|
|
593
|
-
var d2 =
|
|
596
|
+
var d2 = guard.crypto.decipher(alg, kek, iv); guard.crypto.transformNoPadding(d2);
|
|
594
597
|
body = guard.secret.cipherFinish(d2, pass1, CmsError, "cms/decrypt-failed", "the PWRI plaintext block");
|
|
595
598
|
var count = body[0];
|
|
596
599
|
if (count < 1 || count + 4 > body.length) throw _fail();
|
|
@@ -648,7 +651,7 @@ function _originatorSpki(originator) {
|
|
|
648
651
|
if (!originator || originator.form !== "originatorKey") throw _err("cms/unsupported-algorithm", "kari requires an originatorKey (ephemeral-static ECDH)");
|
|
649
652
|
var v = originator.value;
|
|
650
653
|
var algKids = [b.oid(v.algorithm.oid)];
|
|
651
|
-
if (v.algorithm.parameters != null)
|
|
654
|
+
if (v.algorithm.parameters != null) intrinsic.append(algKids, b.raw(v.algorithm.parameters));
|
|
652
655
|
return b.sequence([b.sequence(algKids), b.bitString(v.publicKey.bytes, v.publicKey.unusedBits)]);
|
|
653
656
|
}
|
|
654
657
|
var MONT = intrinsic.create(null); MONT[O("X25519")] = { name: "X25519", hkdf: "SHA-256" }; MONT[O("X448")] = { name: "X448", hkdf: "SHA-512" };
|
|
@@ -666,8 +669,9 @@ function _ecCurveFromCert(cert) {
|
|
|
666
669
|
function _ecCurveFromPrivateKey(keyDer) {
|
|
667
670
|
var spki;
|
|
668
671
|
try {
|
|
669
|
-
spki =
|
|
670
|
-
.
|
|
672
|
+
spki = guard.crypto.exportKey(
|
|
673
|
+
guard.crypto.publicKey({ key: keyDer, format: "der", type: "pkcs8" }),
|
|
674
|
+
{ format: "der", type: "spki" });
|
|
671
675
|
} catch (_e) { return null; } // allow:swallow-unverified a key this runtime cannot read names no curve
|
|
672
676
|
var alg = asn1.decode(spki).children[0];
|
|
673
677
|
if (asn1.read.oid(alg.children[0]) !== O("ecPublicKey") || alg.children.length < 2) return null;
|
|
@@ -710,7 +714,17 @@ async function _verifyAuthenticatedData(parsed, km, opts, requireProtection) {
|
|
|
710
714
|
guard.der.assertParams(parsed.digestAlgorithm.parameters, "absentOrNull", _err,
|
|
711
715
|
"cms/unsupported-algorithm", parsed.digestAlgorithm.name + " digestAlgorithm (RFC 5754 sec. 2)");
|
|
712
716
|
preimage = _explicitSetOf(parsed.authAttrsBytes);
|
|
713
|
-
|
|
717
|
+
/** @internal The selection returns the first matching attribute or null. `filter(...)[0]` indexes
|
|
718
|
+
* a result that is EMPTY when the attribute is absent, and index 0 of an empty array answers
|
|
719
|
+
* from the array prototype, so a value installed there would supply the digest this comparison
|
|
720
|
+
* is made against. The refusal restates an invariant the parse already holds: an
|
|
721
|
+
* AuthenticatedData with authAttrs is walked through the content-binding check, which requires
|
|
722
|
+
* the message-digest attribute (`cms/missing-message-digest`), so this is not reachable from a
|
|
723
|
+
* message and is here so the read below cannot be made on something that is not an attribute. */
|
|
724
|
+
var mdAttr = guard.list.firstMatch(parsed.authAttrs || [], function (a) { return a.type === O("messageDigest"); });
|
|
725
|
+
if (!mdAttr || mdAttr.values.length !== 1) {
|
|
726
|
+
throw _err("cms/bad-auth-attrs", "authenticated attributes must carry exactly one message-digest attribute (RFC 5652 sec. 9.2)");
|
|
727
|
+
}
|
|
714
728
|
mdCheck = { hash: dHash, declared: asn1.read.octetString(asn1.decode(mdAttr.values[0])) };
|
|
715
729
|
/** @internal RFC 6211 sec. 3.2 on this content type: the two comparisons are what make the
|
|
716
730
|
* outer `digestAlgorithm` and `macAlgorithm` fields mean anything, since the MAC input is the
|
|
@@ -742,7 +756,7 @@ async function _verifyAuthenticatedData(parsed, km, opts, requireProtection) {
|
|
|
742
756
|
_assertSupported(candidates[ci].ri, km);
|
|
743
757
|
var macKey = await _acquireCek(candidates[ci].ri, km, opts);
|
|
744
758
|
var macSubstitute = null;
|
|
745
|
-
if (macKey == null || macKey.length < MAC_KEY_MIN_OCTETS) macSubstitute =
|
|
759
|
+
if (macKey == null || macKey.length < MAC_KEY_MIN_OCTETS) macSubstitute = guard.crypto.randomBytes(MAC_KEY_MIN_OCTETS);
|
|
746
760
|
try {
|
|
747
761
|
var key = await subtle.importKey("raw", macSubstitute || macKey, { name: "HMAC", hash: hash }, false, ["verify"]);
|
|
748
762
|
if (!(await _subtleVerify(subtle, { name: "HMAC" }, key, Buffer.from(parsed.mac), preimage))) throw _fail();
|
package/lib/cms-digest.js
CHANGED
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
* attached or detached, constant-time comparison, fail-closed.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
var nodeCrypto = require("node:crypto");
|
|
30
29
|
var oid = require("./oid");
|
|
31
30
|
var schemaCms = require("./schema-cms");
|
|
32
31
|
var frameworkError = require("./framework-error");
|
|
@@ -101,7 +100,7 @@ async function _digest(content, opts) {
|
|
|
101
100
|
var ctOid = O(ctName);
|
|
102
101
|
if (!ctOid) throw _err("cms/bad-input", "opts.contentType is not a known OID name: " + guard.text.showValue(ctName));
|
|
103
102
|
|
|
104
|
-
var value =
|
|
103
|
+
var value = guard.crypto.digest(NODE_DIGEST[alg], raw);
|
|
105
104
|
var eciChildren = opts.detached === true
|
|
106
105
|
? [b.oid(ctOid)]
|
|
107
106
|
: [b.oid(ctOid), b.explicit(0, b.octetString(raw))];
|
|
@@ -176,7 +175,7 @@ async function _verifyDigest(input, opts) {
|
|
|
176
175
|
var covered = embedded != null
|
|
177
176
|
? guard.bytes.snapshotSource(embedded, CmsError, "cms/bad-input", "the encapsulated content")
|
|
178
177
|
: supplied;
|
|
179
|
-
var recomputed =
|
|
178
|
+
var recomputed = guard.crypto.digest(NODE_DIGEST[alg], covered);
|
|
180
179
|
var matched = guard.crypto.constantTimeEqual(recomputed, parsed.digest);
|
|
181
180
|
return guard.verdict.of({
|
|
182
181
|
valid: matched,
|