@blamejs/pki 0.5.6 → 0.5.8
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 +419 -378
- package/MIGRATING.md +65 -0
- package/README.md +12 -12
- package/lib/acme.js +31 -31
- package/lib/asn1-der.js +10 -10
- package/lib/attrcert-sign.js +24 -20
- package/lib/byte-reader.js +6 -6
- package/lib/byte-writer.js +5 -5
- package/lib/cbor-det.js +27 -24
- package/lib/cmc-build.js +104 -104
- package/lib/cmc-verify.js +108 -32
- package/lib/cmp-build.js +31 -26
- package/lib/cmp-session.js +74 -72
- package/lib/cmp-verify.js +72 -58
- package/lib/cms-compress.js +8 -9
- package/lib/cms-decrypt.js +92 -76
- package/lib/cms-encrypt.js +33 -34
- package/lib/cms-sign.js +100 -54
- package/lib/cms-verify.js +141 -82
- package/lib/composite-sig.js +13 -13
- package/lib/constants.js +4 -4
- package/lib/crl-sign.js +31 -25
- package/lib/crl-verify.js +7 -6
- package/lib/crmf-sign.js +19 -15
- package/lib/csr-sign.js +13 -9
- package/lib/ct.js +37 -37
- package/lib/edwards-point.js +7 -7
- package/lib/est.js +103 -59
- package/lib/framework-error.js +5 -5
- package/lib/guard-all.js +3 -3
- package/lib/guard-async.js +4 -4
- package/lib/guard-bytes.js +378 -15
- package/lib/guard-compress.js +17 -17
- package/lib/guard-crypto.js +1 -1
- package/lib/guard-encoding.js +15 -15
- package/lib/guard-header.js +3 -3
- package/lib/guard-identifier.js +16 -16
- package/lib/guard-json.js +15 -15
- package/lib/guard-limits.js +7 -7
- package/lib/guard-name.js +81 -16
- package/lib/guard-parsed.js +144 -75
- package/lib/guard-range.js +19 -19
- package/lib/guard-secret.js +11 -10
- package/lib/guard-text.js +6 -6
- package/lib/guard-time.js +10 -10
- package/lib/hpke.js +18 -17
- package/lib/http-digest.js +35 -35
- package/lib/http-retry-after.js +13 -13
- package/lib/http-transport.js +20 -19
- package/lib/inspect.js +53 -53
- package/lib/ip-utils.js +2 -2
- package/lib/jose.js +13 -13
- package/lib/key.js +16 -16
- package/lib/lint.js +51 -51
- package/lib/merkle.js +51 -36
- package/lib/mime.js +18 -18
- package/lib/ocsp-verify.js +10 -10
- package/lib/ocsp.js +32 -20
- package/lib/oid.js +29 -29
- package/lib/path-validate.js +114 -113
- package/lib/pbes2.js +16 -16
- package/lib/pkcs12-build.js +71 -56
- package/lib/pki-build.js +23 -22
- package/lib/rc2.js +1 -1
- package/lib/rfc3339.js +5 -5
- package/lib/schema-all.js +31 -31
- package/lib/schema-attrcert.js +12 -12
- package/lib/schema-c509.js +144 -142
- package/lib/schema-cmc.js +58 -58
- package/lib/schema-cmp.js +43 -43
- package/lib/schema-cms.js +169 -36
- package/lib/schema-crl.js +7 -7
- package/lib/schema-crmf.js +28 -28
- package/lib/schema-csr.js +12 -12
- package/lib/schema-csrattrs.js +16 -16
- package/lib/schema-engine.js +18 -18
- package/lib/schema-ocsp.js +15 -15
- package/lib/schema-pkcs12.js +20 -20
- package/lib/schema-pkcs8.js +2 -2
- package/lib/schema-pkix.js +131 -126
- package/lib/schema-smime.js +19 -19
- package/lib/schema-tsp.js +12 -12
- package/lib/schema-x509.js +3 -3
- package/lib/shbs.js +18 -18
- package/lib/sign-scheme.js +19 -16
- package/lib/sigstore.js +10 -11
- package/lib/sleep.js +1 -1
- package/lib/smime.js +308 -96
- package/lib/tls-cert-compress.js +18 -18
- package/lib/trust.js +27 -27
- package/lib/tsp-sign.js +22 -18
- package/lib/validator-all.js +1 -1
- package/lib/validator-attcert.js +1 -1
- package/lib/validator-cose.js +43 -44
- package/lib/validator-keydesc.js +3 -3
- package/lib/validator-sig.js +13 -13
- package/lib/validator-tls.js +11 -11
- package/lib/validator-tpm.js +21 -20
- package/lib/webauthn-mds.js +67 -67
- package/lib/webauthn.js +34 -34
- package/lib/webcrypto.js +15 -15
- package/lib/x509-sign.js +24 -15
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/cmc-build.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright (c) blamejs contributors
|
|
3
3
|
"use strict";
|
|
4
4
|
//
|
|
5
|
-
// @internal -- the pki.cmc.build implementation. So the pki.cmc namespace has
|
|
5
|
+
// @internal -- the pki.cmc.build implementation. So the pki.cmc namespace has one @module home,
|
|
6
6
|
// the operator-facing @module pki.cmc + the @primitive pki.cmc.build documentation block live in
|
|
7
7
|
// cmc-verify.js, which re-exports this build function.
|
|
8
8
|
//
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* The witness rule is the load-bearing one. RFC 5272 sec. 6.2.1 step 1 says the
|
|
20
20
|
* value to be validated is "The PKIData reqSequence field (encoded exactly as it
|
|
21
21
|
* appears in the Full PKI Request including the sequence type and length)". So
|
|
22
|
-
* the reqSequence TLV is built
|
|
23
|
-
* derived from that single buffer
|
|
22
|
+
* the reqSequence TLV is built once and both the message and the witness are
|
|
23
|
+
* derived from that single buffer; computing the witness over a second
|
|
24
24
|
* serialization would agree with itself while disagreeing with the wire the
|
|
25
25
|
* moment any encoding detail differed.
|
|
26
26
|
*/
|
|
@@ -46,10 +46,10 @@ var OID_IDENTIFICATION = O("id-cmc-identification");
|
|
|
46
46
|
var OID_SHA256 = O("sha256");
|
|
47
47
|
var OID_HMAC_SHA256 = O("hmacWithSHA256");
|
|
48
48
|
|
|
49
|
-
// The identity-bearing controls a renewal must
|
|
50
|
-
//
|
|
49
|
+
// The identity-bearing controls a renewal must not carry: RFC 5272 sec. 3.2 (a),
|
|
50
|
+
// "The Identification and Identity Proof controls are absent."
|
|
51
51
|
//
|
|
52
|
-
// "Identity Proof"
|
|
52
|
+
// "Identity Proof" covers both versions: the original id-cmc-identityProof (sec.
|
|
53
53
|
// 6.2.2) and identityProofV2 (sec. 6.2.1). Listing only V2 would leave a renewal
|
|
54
54
|
// able to carry the v1 control, which is the same nonconformance the rule names.
|
|
55
55
|
var RENEWAL_FORBIDDEN = {};
|
|
@@ -57,14 +57,14 @@ RENEWAL_FORBIDDEN[OID_IDENTIFICATION] = "Identification";
|
|
|
57
57
|
RENEWAL_FORBIDDEN[O("id-cmc-identityProof")] = "Identity Proof";
|
|
58
58
|
RENEWAL_FORBIDDEN[OID_IDENTITY_PROOF_V2] = "Identity Proof V2";
|
|
59
59
|
|
|
60
|
-
// RFC 6402 sec. 2.6: responseBody appears
|
|
61
|
-
//
|
|
60
|
+
// RFC 6402 sec. 2.6: responseBody appears only in a PKIResponse. Enforced at
|
|
61
|
+
// build time as well as at parse, so a caller cannot emit a message its own
|
|
62
62
|
// decoder would refuse.
|
|
63
63
|
var OID_RESPONSE_BODY = O("id-cmc-responseBody");
|
|
64
64
|
|
|
65
65
|
// RFC 5272 sec. 6.2.1 says "Implementations MUST be able to support tokens at
|
|
66
|
-
// least 16 characters long"
|
|
67
|
-
//
|
|
66
|
+
// least 16 characters long", a requirement on what an implementation must be
|
|
67
|
+
// able to accept, not a floor every token has to clear. Reading it as a minimum
|
|
68
68
|
// inverts it, and would refuse a shorter secret a CA legitimately provisioned.
|
|
69
69
|
// The shared secret's strength is the deploying CA's policy; what this layer can
|
|
70
70
|
// say is that an empty string is not a credential at all.
|
|
@@ -72,12 +72,12 @@ var OID_RESPONSE_BODY = O("id-cmc-responseBody");
|
|
|
72
72
|
var POP_LINK_RANDOM_BYTES = 64;
|
|
73
73
|
|
|
74
74
|
// The spec fields pki.cmc.build understands. Anything else is a caller mistake
|
|
75
|
-
// caught at the entry point
|
|
75
|
+
// caught at the entry point, never silently dropped from the message.
|
|
76
76
|
var KNOWN_SPEC_KEYS = {
|
|
77
77
|
requests: 1, controls: 1, cmsSequence: 1, otherMsgSequence: 1,
|
|
78
|
-
//
|
|
78
|
+
// `identification` is deliberately absent: the Identification control is attached through
|
|
79
79
|
// identityProof.identity, which is what emits it. Listing a key nothing reads
|
|
80
|
-
// would recreate the very hole this table closes
|
|
80
|
+
// would recreate the very hole this table closes, accepted at the door and
|
|
81
81
|
// silently absent from the message.
|
|
82
82
|
identityProof: 1, popLink: 1, renewal: 1,
|
|
83
83
|
// The exchange binding (RFC 5272 sec. 6.6 / 6.4). First-class here because
|
|
@@ -91,20 +91,20 @@ var OID_TRANSACTION_ID = O("id-cmc-transactionId");
|
|
|
91
91
|
var OID_SENDER_NONCE = O("id-cmc-senderNonce");
|
|
92
92
|
var OID_DATA_RETURN = O("id-cmc-dataReturn");
|
|
93
93
|
|
|
94
|
-
// AlgorithmIdentifier { OID } with no parameters
|
|
94
|
+
// AlgorithmIdentifier { OID } with no parameters: the form the MAC / hash
|
|
95
95
|
// algorithm identifiers in these two controls take.
|
|
96
96
|
function algId(o) { return b.sequence([b.oid(o)]); }
|
|
97
97
|
|
|
98
98
|
// ---- body part identity ---------------------------------------------
|
|
99
99
|
|
|
100
|
-
// RFC 5272 sec. 3.2.2: identifiers are unique across the
|
|
100
|
+
// RFC 5272 sec. 3.2.2: identifiers are unique across the whole message and 0 is
|
|
101
101
|
// reserved for the reference to the current PKIData. The allocator therefore
|
|
102
|
-
// starts at 1, and a caller-supplied value is validated
|
|
102
|
+
// starts at 1, and a caller-supplied value is validated, never adjusted;
|
|
103
103
|
// silently renumbering would break any control that already references it.
|
|
104
104
|
function makeIdAllocator() {
|
|
105
105
|
var used = Object.create(null);
|
|
106
106
|
// Reserved ahead of allocation and not yet consumed by the element that owns
|
|
107
|
-
// it. Kept distinct from `used` so an element re-claiming its
|
|
107
|
+
// it. Kept distinct from `used` so an element re-claiming its own reservation
|
|
108
108
|
// is a no-op, while a second element asking for the same value is still the
|
|
109
109
|
// duplicate the rule forbids.
|
|
110
110
|
var pending = Object.create(null);
|
|
@@ -151,11 +151,11 @@ function makeIdAllocator() {
|
|
|
151
151
|
|
|
152
152
|
// TaggedRequest ::= CHOICE { tcr [0], crm [1], orm [2] }, IMPLICIT tags. For the
|
|
153
153
|
// crm arm the identity is the CertReqMsg's own certReqId (sec. 3.2.2), so it is
|
|
154
|
-
// read back out of the supplied message
|
|
154
|
+
// read back out of the supplied message and never allocated; allocating one
|
|
155
155
|
// would put a second, contradictory identifier on the wire.
|
|
156
|
-
// The identifier a request
|
|
156
|
+
// The identifier a request fixes, or null when it leaves the choice to us. Read
|
|
157
157
|
// in a pre-pass so every caller-determined value is reserved before a single
|
|
158
|
-
// generated one is handed out
|
|
158
|
+
// generated one is handed out; otherwise acceptance depends on the order the
|
|
159
159
|
// caller happened to write the list in, which is not a property of the message.
|
|
160
160
|
// The fields a request descriptor understands -- the same door KNOWN_SPEC_KEYS
|
|
161
161
|
// closes on the spec, on the objects nested inside it. A misspelled `bodyPartID`
|
|
@@ -165,9 +165,9 @@ function makeIdAllocator() {
|
|
|
165
165
|
var KNOWN_REQUEST_KEYS = { tcr: 1, crm: 1, orm: 1, bodyPartID: 1 };
|
|
166
166
|
// The same door on the other descriptors nested in a spec. A control's identifier
|
|
167
167
|
// is always allocated, so a `bodyPartID` written on one is ignored outright; and a
|
|
168
|
-
// misspelled `identity` does not merely go missing
|
|
168
|
+
// misspelled `identity` does not merely go missing: the Identification control
|
|
169
169
|
// it would have emitted is what tells the server to derive the Identity Proof key
|
|
170
|
-
// from the secret
|
|
170
|
+
// from the secret and the identity, so the message would carry a witness no
|
|
171
171
|
// conforming server can reproduce.
|
|
172
172
|
var KNOWN_CONTROL_KEYS = { type: 1, value: 1 };
|
|
173
173
|
var KNOWN_IDENTITY_PROOF_KEYS = { identity: 1, secret: 1 };
|
|
@@ -185,12 +185,12 @@ function fixedRequestId(req, index) {
|
|
|
185
185
|
"each request names exactly one of tcr / crm / orm, got " + (arms.length ? arms.join(" + ") : "none") +
|
|
186
186
|
" (request " + index + ")");
|
|
187
187
|
}
|
|
188
|
-
// A crm arm's identity is its CertReqMsg's own certReqId, so it is
|
|
189
|
-
//
|
|
190
|
-
//
|
|
188
|
+
// A crm arm's identity is its CertReqMsg's own certReqId, so it is always fixed:
|
|
189
|
+
// there is nothing to allocate and nothing that may displace it. A caller who
|
|
190
|
+
// also writes bodyPartID is stating an identity, and the only reason to state one
|
|
191
191
|
// is that something already references it; taking the certReqId silently would
|
|
192
|
-
// sign a message whose control points at no request in it. Equal is accepted
|
|
193
|
-
// the caller is agreeing with the message, not overriding it.
|
|
192
|
+
// sign a message whose control points at no request in it. Equal is accepted,
|
|
193
|
+
// since the caller is agreeing with the message, not overriding it.
|
|
194
194
|
if (req.crm != null) {
|
|
195
195
|
var crmId = _certReqIdOf(_asCertReqMsg(_der(req.crm, "a crm CertReqMsg")));
|
|
196
196
|
if (req.bodyPartID != null && _asBigInt(req.bodyPartID, "a crm request bodyPartID") !== BigInt(crmId)) {
|
|
@@ -205,7 +205,7 @@ function fixedRequestId(req, index) {
|
|
|
205
205
|
|
|
206
206
|
function encodeRequest(req, ids, index) {
|
|
207
207
|
if (req.tcr != null) {
|
|
208
|
-
// A tcr arm
|
|
208
|
+
// A tcr arm is a PKCS#10 CertificationRequest, so it is parsed and never
|
|
209
209
|
// taken on the tag: the readback of the assembled message checks the CMC
|
|
210
210
|
// structure around it, and would pass an empty SEQUENCE here as happily as a
|
|
211
211
|
// real request. Signing a request body that is not one produces an enrolment
|
|
@@ -231,12 +231,12 @@ function encodeRequest(req, ids, index) {
|
|
|
231
231
|
// CLAIM, not just read. The certReqId was reserved in the pre-pass with every
|
|
232
232
|
// other caller-determined identifier; leaving the reservation outstanding lets
|
|
233
233
|
// a later cmsSequence or otherMsg element asking for the same number be taken
|
|
234
|
-
// for its owner, and the message would then carry the identifier twice
|
|
234
|
+
// for its owner, and the message would then carry the identifier twice: a
|
|
235
235
|
// PKIData this toolkit's own parser refuses. The tcr and orm arms already
|
|
236
236
|
// claim; this arm is the same rule.
|
|
237
237
|
var certReqId = ids.claim(_certReqIdOf(msg), "a crm request");
|
|
238
|
-
// IMPLICIT [1]: the tag
|
|
239
|
-
// is re-headered
|
|
238
|
+
// IMPLICIT [1]: the tag replaces the CertReqMsg SEQUENCE tag, so the content
|
|
239
|
+
// is re-headered, not nested.
|
|
240
240
|
var node = asn1.decode(msg);
|
|
241
241
|
var headerLen = node.header.end - node.header.start;
|
|
242
242
|
return { bodyPartID: certReqId, der: b.contextConstructed(1, node.bytes.subarray(headerLen)) };
|
|
@@ -253,17 +253,17 @@ function encodeRequest(req, ids, index) {
|
|
|
253
253
|
};
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
// A TaggedRequest's crm arm carries
|
|
257
|
-
// a CertReqMessages (SEQUENCE SIZE(1..MAX) OF CertReqMsg)
|
|
256
|
+
// A TaggedRequest's crm arm carries one CertReqMsg, but `pki.crmf.build` returns
|
|
257
|
+
// a CertReqMessages (SEQUENCE SIZE(1..MAX) OF CertReqMsg), which is what a
|
|
258
258
|
// caller naturally has in hand. Both are accepted, told apart structurally:
|
|
259
259
|
//
|
|
260
260
|
// CertReqMsg ::= SEQUENCE { certReq CertRequest, ... } first child is a
|
|
261
|
-
// SEQUENCE whose
|
|
261
|
+
// SEQUENCE whose own first child is an INTEGER (certReqId)
|
|
262
262
|
// CertReqMessages ::= SEQUENCE OF CertReqMsg first child is a
|
|
263
263
|
// CertReqMsg, whose own first child is a SEQUENCE
|
|
264
264
|
//
|
|
265
|
-
// A CertReqMessages carrying more than one message is
|
|
266
|
-
// its first
|
|
265
|
+
// A CertReqMessages carrying more than one message is refused, never reduced to
|
|
266
|
+
// its first: each CertReqMsg is its own TaggedRequest with its own identity,
|
|
267
267
|
// so silently dropping the rest would emit a request the caller did not ask for.
|
|
268
268
|
function _asCertReqMsg(der) {
|
|
269
269
|
var node = asn1.decode(der);
|
|
@@ -294,9 +294,9 @@ function _certReqIdOf(msg) {
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
// A pre-encoded TaggedContentInfo / OtherMsg supplied by the caller. Only its
|
|
297
|
-
// leading bodyPartID is read
|
|
297
|
+
// leading bodyPartID is read (the payload is the caller's and stays untouched),
|
|
298
298
|
// so the identifier joins the same allocation space every other element draws
|
|
299
|
-
// from and a collision is refused here
|
|
299
|
+
// from and a collision is refused here, never discovered later by the parser.
|
|
300
300
|
function _claimRawElement(el, ids, what) {
|
|
301
301
|
var der = _der(el, what);
|
|
302
302
|
var node = asn1.decode(der);
|
|
@@ -309,9 +309,9 @@ function _claimRawElement(el, ids, what) {
|
|
|
309
309
|
return der;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
// The binding controls have a named spec field
|
|
312
|
+
// The binding controls have a named spec field and can also be hand-encoded into
|
|
313
313
|
// spec.controls. Emitting both would put two of the same control in one message,
|
|
314
|
-
// and duplicates of these three are what decide what the response must echo
|
|
314
|
+
// and duplicates of these three are what decide what the response must echo: two
|
|
315
315
|
// values means no value. Also refuses two hand-encoded copies, since the same
|
|
316
316
|
// ambiguity arrives that way.
|
|
317
317
|
var BINDING_BY_OID = {};
|
|
@@ -319,9 +319,9 @@ BINDING_BY_OID[O("id-cmc-transactionId")] = "transactionId";
|
|
|
319
319
|
BINDING_BY_OID[O("id-cmc-senderNonce")] = "senderNonce";
|
|
320
320
|
BINDING_BY_OID[O("id-cmc-dataReturn")] = "dataReturn";
|
|
321
321
|
|
|
322
|
-
// ... and the type each one carries. The CMC parser keeps every control value
|
|
322
|
+
// ... and the type each one carries. The CMC parser keeps every control value raw,
|
|
323
323
|
// so nothing downstream of the builder objects to a Transaction Identifier encoded
|
|
324
|
-
// as an OCTET STRING
|
|
324
|
+
// as an OCTET STRING. But these three are read, not carried: pki.cmc.verify
|
|
325
325
|
// compares them against the response, and pki.est.fullcmc reads them back out of
|
|
326
326
|
// the request before it goes out. A hand-encoded value of the wrong type therefore
|
|
327
327
|
// signs a request that this toolkit's own client will refuse to send. The type is
|
|
@@ -365,8 +365,7 @@ function _assertNoDuplicateBinding(callerControls, spec) {
|
|
|
365
365
|
function _asBigInt(v, what) { return guard.range.authoredInteger(v, E, "cmc/bad-input", what); }
|
|
366
366
|
|
|
367
367
|
function _der(v, what) {
|
|
368
|
-
if (Buffer.isBuffer(v)) return v;
|
|
369
|
-
if (v instanceof Uint8Array) return Buffer.from(v);
|
|
368
|
+
if (Buffer.isBuffer(v) || v instanceof Uint8Array) return guard.bytes.snapshot(v, CmcError, "cmc/bad-input", what);
|
|
370
369
|
throw E("cmc/bad-input", what + " must be DER bytes");
|
|
371
370
|
}
|
|
372
371
|
function _oidOf(v) {
|
|
@@ -378,9 +377,9 @@ function _oidOf(v) {
|
|
|
378
377
|
|
|
379
378
|
// TaggedAttribute ::= SEQUENCE { bodyPartID, attrType, attrValues SET OF ANY }.
|
|
380
379
|
// Controls this builder will not put in a Full PKI Request. A row per control with
|
|
381
|
-
// the reason it does not belong in this direction
|
|
380
|
+
// the reason it does not belong in this direction: id-cmc-responseBody by an
|
|
382
381
|
// explicit RFC 6402 sec. 2.6 MUST NOT, the two status controls because RFC 5272
|
|
383
|
-
// sec. 6.1 makes them what a
|
|
382
|
+
// sec. 6.1 makes them what a server emits and a client processes in a PKI Response.
|
|
384
383
|
// a request has no verdict of its own to report, so one written here is a fabricated
|
|
385
384
|
// answer to a question nobody asked.
|
|
386
385
|
//
|
|
@@ -409,6 +408,11 @@ function encodeControl(attrType, values, ids) {
|
|
|
409
408
|
// ---- the witnesses ---------------------------------------------------
|
|
410
409
|
|
|
411
410
|
function _assertSecret(secret, what) {
|
|
411
|
+
// An entry-tier argument check: this tests the TYPE and whether the string is empty, and
|
|
412
|
+
// compares no secret-dependent content. The length of a caller's own argument is already
|
|
413
|
+
// known to that caller, so there is no secret for a timing-safe compare to protect. The
|
|
414
|
+
// MAC comparisons this rule exists for route through guard.crypto.constantTimeEqual.
|
|
415
|
+
// nosemgrep: pki-non-constant-time-secret-compare
|
|
412
416
|
if (typeof secret !== "string" || secret.length === 0) {
|
|
413
417
|
throw E("cmc/bad-input",
|
|
414
418
|
what + " requires the shared secret as a non-empty string; got " +
|
|
@@ -418,10 +422,10 @@ function _assertSecret(secret, what) {
|
|
|
418
422
|
|
|
419
423
|
// IdentifyProofV2 ::= SEQUENCE { hashAlgID, macAlgID, witness OCTET STRING }.
|
|
420
424
|
// key = hash(shared-secret as a UTF8 string); witness = MAC(reqSequenceBytes, key).
|
|
421
|
-
// `reqSequenceBytes` is the buffer that
|
|
425
|
+
// `reqSequenceBytes` is the buffer that becomes the message's reqSequence, never
|
|
422
426
|
// a re-encode of it (RFC 5272 sec. 6.2.1 step 1).
|
|
423
427
|
// Tracks a buffer this module allocated that carries secret material, so the caller clears it
|
|
424
|
-
// on every exit. The shared secret
|
|
428
|
+
// on every exit. The shared secret arrives as a JS string, which is immutable and cannot be
|
|
425
429
|
// cleared; what this closes is every copy the toolkit itself makes of it.
|
|
426
430
|
function _ownSecret(buf, owned) {
|
|
427
431
|
owned.push(buf);
|
|
@@ -431,17 +435,17 @@ function _ownSecret(buf, owned) {
|
|
|
431
435
|
function identityProofV2(secret, reqSequenceBytes, identity) {
|
|
432
436
|
_assertSecret(secret, "an Identity Proof V2 control");
|
|
433
437
|
// RFC 5272 sec. 6.2.3: the Identification control is OPTIONAL ("servers MAY
|
|
434
|
-
// require" it), but when it
|
|
438
|
+
// require" it), but when it is present the key derivation changes: "the
|
|
435
439
|
// hash of the concatenation of the shared-secret and the UTF8 identity value
|
|
436
440
|
// (without the type and length bytes) are hashed rather than just the
|
|
437
441
|
// shared-secret". Same controls on the wire, different key: a producer that
|
|
438
442
|
// emits the Identification control while hashing the secret alone computes a
|
|
439
443
|
// witness every conforming server rejects.
|
|
440
|
-
//
|
|
444
|
+
// Every allocation that carries the shared secret is cleared, not only the derived key.
|
|
441
445
|
// The derivation input holds the secret in the clear, and with an identity present the
|
|
442
|
-
// concatenation leaves two more copies of it behind
|
|
446
|
+
// concatenation leaves two more copies of it behind: the operand and the joined buffer.
|
|
443
447
|
// Wiping the key while those survive clears the cheapest copy and keeps the rest. The
|
|
444
|
-
// derivation runs
|
|
448
|
+
// derivation runs inside the try so a throw partway still reaches the clear.
|
|
445
449
|
var owned = [];
|
|
446
450
|
var key = null;
|
|
447
451
|
try {
|
|
@@ -481,17 +485,13 @@ function popLinkWitnessV2(secret, R) {
|
|
|
481
485
|
// The operator-facing @primitive block for this function lives beside its
|
|
482
486
|
// re-export in cmc-verify.js, the pki.cmc @module home.
|
|
483
487
|
function build(spec, signer, opts) {
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return _build(spec, signer, opts);
|
|
492
|
-
} catch (e) {
|
|
493
|
-
return Promise.reject(e); // the surface stays promise-rejecting, never throwing
|
|
494
|
-
}
|
|
488
|
+
// Every caller-owned argument copied at entry and released when the call settles; see the note
|
|
489
|
+
// on the same call in x509-sign. It matters most here: the Identity Proof and POP Link witnesses
|
|
490
|
+
// are computed over the bytes this builder is about to emit, so a spec that changed in between
|
|
491
|
+
// would witness a different request than the one that goes out.
|
|
492
|
+
return guard.bytes.fixedCall(CmcError, "cmc/bad-input", [
|
|
493
|
+
[spec, "the CMC request spec"], [signer, "the signer"], [opts, "pki.cmc.build options"],
|
|
494
|
+
], _build);
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
function _build(spec, signer, opts) {
|
|
@@ -500,9 +500,9 @@ function _build(spec, signer, opts) {
|
|
|
500
500
|
throw E("cmc/bad-input", "the CMC request spec must be an object");
|
|
501
501
|
}
|
|
502
502
|
if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw E("cmc/bad-input", "pki.cmc.build options must be an object");
|
|
503
|
-
// A misspelled or unsupported spec field fails
|
|
503
|
+
// A misspelled or unsupported spec field fails open in the quietest way there is:
|
|
504
504
|
// the message builds, is signed, and simply does not carry what was asked for.
|
|
505
|
-
// That is worst for the exchange-binding fields below
|
|
505
|
+
// That is worst for the exchange-binding fields below: a request built without
|
|
506
506
|
// them has no replay defence, and pki.cmc.verify cannot enforce a binding the
|
|
507
507
|
// client never sent, so the omission is invisible from both ends.
|
|
508
508
|
guard.identifier.assertKnownKeys(spec, KNOWN_SPEC_KEYS, E, "cmc/bad-input", "unknown spec field ");
|
|
@@ -513,16 +513,16 @@ function _build(spec, signer, opts) {
|
|
|
513
513
|
|
|
514
514
|
var ids = makeIdAllocator();
|
|
515
515
|
|
|
516
|
-
//
|
|
516
|
+
// Every identifier the caller determines is reserved before a single generated
|
|
517
517
|
// one is handed out. There are four sources, and reserving only some of them
|
|
518
|
-
// makes acceptance depend on the order the caller wrote things in
|
|
519
|
-
//
|
|
518
|
+
// makes acceptance depend on the order the caller wrote things in, not on
|
|
519
|
+
// the message:
|
|
520
520
|
// 1. a request's explicit bodyPartID
|
|
521
521
|
// 2. a crm arm's certReqId, which is fixed by the CertReqMsg itself
|
|
522
522
|
// 3. a cmsSequence element's bodyPartID
|
|
523
523
|
// 4. an otherMsgSequence element's bodyPartID
|
|
524
524
|
// Auto-allocation walks upward from 1, so anything minted before these were
|
|
525
|
-
// claimed could take a value the caller already spent
|
|
525
|
+
// claimed could take a value the caller already spent, and the caller's own
|
|
526
526
|
// element would then be rejected as a duplicate of whatever displaced it.
|
|
527
527
|
requests.forEach(function (r, i) {
|
|
528
528
|
var fixed = fixedRequestId(r, i);
|
|
@@ -559,7 +559,7 @@ function _build(spec, signer, opts) {
|
|
|
559
559
|
// A renewal is authenticated by the certificate being renewed: sec. 6.3.3 says
|
|
560
560
|
// "the outermost signature layer is created using the current signing
|
|
561
561
|
// certificate, which allows the original identity to be associated with the
|
|
562
|
-
// certification request". That identity is the whole mechanism
|
|
562
|
+
// certification request". That identity is the whole mechanism: it is what
|
|
563
563
|
// replaces the Identity Proof this mode just refused. A key-only signer has no
|
|
564
564
|
// certificate and so carries no prior identity, leaving a message with nothing
|
|
565
565
|
// for the CA to authenticate the renewal against.
|
|
@@ -588,13 +588,13 @@ function _build(spec, signer, opts) {
|
|
|
588
588
|
|
|
589
589
|
// The exchange binding (RFC 5272 sec. 6.6 Transaction Identifier / Sender Nonce,
|
|
590
590
|
// sec. 6.4 Data Return). These are what pki.cmc.verify checks the response
|
|
591
|
-
// against, and a request that omits them has no replay defence
|
|
592
|
-
// named fields here
|
|
591
|
+
// against, and a request that omits them has no replay defence, so they are
|
|
592
|
+
// named fields here and not something the caller hand-encodes into
|
|
593
593
|
// spec.controls and can silently get wrong.
|
|
594
594
|
// A named field and a hand-encoded control of the same type would emit the
|
|
595
|
-
// control
|
|
596
|
-
// response can be bound to
|
|
597
|
-
// Refuse it at the source
|
|
595
|
+
// control twice, and a message carrying two of them has no single value the
|
|
596
|
+
// response can be bound to; this toolkit's own /fullcmc refuses exactly that.
|
|
597
|
+
// Refuse it at the source, before signing something no one can bind to.
|
|
598
598
|
_assertNoDuplicateBinding(callerControls, spec);
|
|
599
599
|
if (spec.transactionId != null) {
|
|
600
600
|
controls.push(encodeControl(OID_TRANSACTION_ID,
|
|
@@ -617,7 +617,7 @@ function _build(spec, signer, opts) {
|
|
|
617
617
|
throw E("cmc/bad-input", "identityProof.identity is the UTF8String the Identification control carries");
|
|
618
618
|
}
|
|
619
619
|
// The pair is emitted together when an identity is given, because the two are
|
|
620
|
-
// coupled: the Identification control tells the server
|
|
620
|
+
// coupled: the Identification control tells the server which shared secret to
|
|
621
621
|
// look up, and its presence is what changes how the key is derived. Letting a
|
|
622
622
|
// caller supply one without the other would put that coupling out of reach.
|
|
623
623
|
if (identity != null) controls.push(encodeControl(OID_IDENTIFICATION, [b.utf8(identity)], ids));
|
|
@@ -642,8 +642,8 @@ function _build(spec, signer, opts) {
|
|
|
642
642
|
}
|
|
643
643
|
|
|
644
644
|
// PKIData ::= SEQUENCE { controlSequence, reqSequence, cmsSequence,
|
|
645
|
-
// otherMsgSequence }
|
|
646
|
-
// reqSequenceBytes is spliced in as the
|
|
645
|
+
// otherMsgSequence }: all four emitted, the unused ones empty (sec. 3.2.1).
|
|
646
|
+
// reqSequenceBytes is spliced in as the same buffer the witness was computed
|
|
647
647
|
// over, which is the whole of IP1.
|
|
648
648
|
var pkiData = b.sequence([
|
|
649
649
|
b.sequence(controls),
|
|
@@ -653,13 +653,13 @@ function _build(spec, signer, opts) {
|
|
|
653
653
|
]);
|
|
654
654
|
|
|
655
655
|
// Read the assembled message back through the shipped parser before signing it.
|
|
656
|
-
// Every arm here splices
|
|
657
|
-
// cmsSequence TaggedContentInfo, an otherMsg's value
|
|
656
|
+
// Every arm here splices caller-supplied DER (a tcr's CertificationRequest, a
|
|
657
|
+
// cmsSequence TaggedContentInfo, an otherMsg's value), and checking each shape
|
|
658
658
|
// by hand would restate the parser's rules in a second place, where they would
|
|
659
659
|
// drift and where a newly added arm would silently miss them. One round-trip
|
|
660
660
|
// covers them all, including arms not yet written: whatever the parser refuses,
|
|
661
|
-
// this refuses at build time
|
|
662
|
-
//
|
|
661
|
+
// this refuses at build time, so no message goes out whose recipient (this
|
|
662
|
+
// toolkit's own decoder included) cannot read it.
|
|
663
663
|
try {
|
|
664
664
|
cmcFmt.parsePkiData(pkiData);
|
|
665
665
|
} catch (e) {
|
|
@@ -673,13 +673,13 @@ function _build(spec, signer, opts) {
|
|
|
673
673
|
// The signer is copied for the same reason the message was assembled at the
|
|
674
674
|
// call: cms.sign reads `key` inside its own promise chain, so a caller who
|
|
675
675
|
// swaps signer.key on the next line would have the request signed by the
|
|
676
|
-
// replacement while the original certificate stays embedded
|
|
676
|
+
// replacement while the original certificate stays embedded: a message whose
|
|
677
677
|
// signature does not belong to the certificate beside it.
|
|
678
678
|
//
|
|
679
679
|
// cms.sign resolves eContentType through oid.byName, so it takes the registry
|
|
680
|
-
//
|
|
681
|
-
// Copying a private key makes a
|
|
682
|
-
// settled
|
|
680
|
+
// name; handing it the dotted value resolves to undefined.
|
|
681
|
+
// Copying a private key makes a second copy of a secret, so it is cleared once signing has
|
|
682
|
+
// settled and never left for the collector, the same discipline the MAC keys above follow.
|
|
683
683
|
// The clear covers the rejecting path too: a wrong or malformed key is the case a caller can
|
|
684
684
|
// reach, and a success-only wipe would keep the secret exactly when it matters.
|
|
685
685
|
var ownedKeyBytes = [];
|
|
@@ -699,12 +699,12 @@ function _build(spec, signer, opts) {
|
|
|
699
699
|
// Each signer descriptor, and every byte value in it. cms.sign reads these inside
|
|
700
700
|
// its own promise chain, so both levels matter: re-pointing signer.key is one way
|
|
701
701
|
// to change who signs, and rewriting the PKCS#8 buffer it already points at is
|
|
702
|
-
// the other. A CryptoKey handle is passed through as-is
|
|
702
|
+
// the other. A CryptoKey handle is passed through as-is: it is an opaque
|
|
703
703
|
// reference the caller is meant to share, and there is nothing to copy.
|
|
704
|
-
// `owned` collects the buffers
|
|
704
|
+
// `owned` collects the buffers this function allocated that hold private key material, so the
|
|
705
705
|
// caller can clear them once signing has settled. Only the key is listed: the other copied
|
|
706
706
|
// values are certificates and identifiers, which are public and outlive the call by design.
|
|
707
|
-
// The caller's own key is never written to
|
|
707
|
+
// The caller's own key is never written to; only our copy of it is.
|
|
708
708
|
function _copySigners(signer, owned) {
|
|
709
709
|
function one(s) {
|
|
710
710
|
if (!s || typeof s !== "object") return s;
|
|
@@ -715,9 +715,9 @@ function _copySigners(signer, owned) {
|
|
|
715
715
|
}
|
|
716
716
|
return out;
|
|
717
717
|
}
|
|
718
|
-
// One level further for a
|
|
719
|
-
// ({ mldsa, trad })
|
|
720
|
-
// those components
|
|
718
|
+
// One level further for a composite key, which is an object of component keys
|
|
719
|
+
// ({ mldsa, trad }) and not a buffer. Copying only the top level would leave
|
|
720
|
+
// those components, the actual PKCS#8 bytes that sign, the caller's to
|
|
721
721
|
// replace or zeroize while cms.sign reads them in a later turn. A CryptoKey is
|
|
722
722
|
// an opaque handle and is passed through as-is.
|
|
723
723
|
function copyValue(v, isSecret) {
|
|
@@ -742,7 +742,7 @@ function _copySigners(signer, owned) {
|
|
|
742
742
|
|
|
743
743
|
/**
|
|
744
744
|
* RFC 5272 sec. 3.2, the three rules that apply when the signature is made with
|
|
745
|
-
* the private key of a certification request the message carries
|
|
745
|
+
* the private key of a certification request the message carries, and not with
|
|
746
746
|
* an already-certified key:
|
|
747
747
|
*
|
|
748
748
|
* a. that request MUST include a Subject Key Identifier extension;
|
|
@@ -753,15 +753,15 @@ function _copySigners(signer, owned) {
|
|
|
753
753
|
* signer. (a) and (c) are about agreement between the signer and the requests
|
|
754
754
|
* beside it, which is checked here: an identifier the requests never declare
|
|
755
755
|
* leaves the CA unable to tie the signature to the key being enrolled, so the
|
|
756
|
-
* request is signed but unusable
|
|
756
|
+
* request is signed but unusable, and it is the producer's job to catch that,
|
|
757
757
|
* not the CA's to guess.
|
|
758
758
|
*
|
|
759
|
-
* This lives
|
|
759
|
+
* This lives here, not in pki.cms.sign, because it is a CMC rule: only this
|
|
760
760
|
* layer knows the content is a PKIData and which requests are in it. Teaching the
|
|
761
761
|
* generic CMS signer to parse CMC content would put the protocol's rules in the
|
|
762
762
|
* wrong module.
|
|
763
763
|
*
|
|
764
|
-
* A signer
|
|
764
|
+
* A signer that carries a certificate is untouched: it identifies itself by that
|
|
765
765
|
* certificate, and the clause does not reach it.
|
|
766
766
|
*/
|
|
767
767
|
function _assertKeyOnlySigner(signer, requests) {
|
|
@@ -780,7 +780,7 @@ function _assertKeyOnlySigner(signer, requests) {
|
|
|
780
780
|
keyOnly.forEach(function (so) { _assertKeyOnlySignerBinding(so, requests); });
|
|
781
781
|
}
|
|
782
782
|
|
|
783
|
-
// The sec. 3.2a/3.2c binding for
|
|
783
|
+
// The sec. 3.2a/3.2c binding for a single key-only signer.
|
|
784
784
|
function _assertKeyOnlySignerBinding(so, requests) {
|
|
785
785
|
var id = so.keyIdentifier;
|
|
786
786
|
var idBytes = (Buffer.isBuffer(id) || id instanceof Uint8Array) ? Buffer.from(id) : null;
|
|
@@ -795,7 +795,7 @@ function _assertKeyOnlySignerBinding(so, requests) {
|
|
|
795
795
|
var declaredSki = false, sawRequest = false;
|
|
796
796
|
for (var i = 0; i < requests.length; i++) {
|
|
797
797
|
var req = requests[i];
|
|
798
|
-
//
|
|
798
|
+
// Both key-bearing arms: sec. 3.2 says the signing key may belong to a request
|
|
799
799
|
// "included in the TaggedRequest tcr or crm fields", so reading only PKCS#10
|
|
800
800
|
// would reject every conforming CRMF enrolment of a brand-new key.
|
|
801
801
|
var declaredBy;
|
|
@@ -806,11 +806,11 @@ function _assertKeyOnlySignerBinding(so, requests) {
|
|
|
806
806
|
if (req && (req.tcr != null || req.crm != null)) sawRequest = true;
|
|
807
807
|
if (!declaredBy || !declaredBy.ski) continue;
|
|
808
808
|
declaredSki = true;
|
|
809
|
-
// The identifier
|
|
810
|
-
// signer holding key A while the request it points at asks to certify key B
|
|
809
|
+
// The identifier and the key together. Matching the identifier alone would accept a
|
|
810
|
+
// signer holding key A while the request it points at asks to certify key B:
|
|
811
811
|
// the SKI is caller-chosen, so the two can be made to agree while the keys do
|
|
812
812
|
// not, and a CA resolving the SID to the requested key could then not verify
|
|
813
|
-
// the carrier at all. The identifier says
|
|
813
|
+
// the carrier at all. The identifier says which request; the key is what makes
|
|
814
814
|
// the claim true. The comparison runs through the toolkit's shared byte
|
|
815
815
|
// equality; neither value here is secret, but there is one way to compare.
|
|
816
816
|
if (guard.crypto.constantTimeEqual(declaredBy.ski, idBytes) &&
|
|
@@ -833,14 +833,14 @@ function _assertKeyOnlySignerBinding(so, requests) {
|
|
|
833
833
|
"to be the one asking for this very public key (RFC 5272 sec. 3.2)");
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
-
// { ski, spki } for a CRMF certification request
|
|
836
|
+
// { ski, spki } for a CRMF certification request: the identifier it declares and
|
|
837
837
|
// the key it asks to have certified. The CertTemplate carries extensions directly,
|
|
838
838
|
// so there is no attribute wrapper to unwrap; the extnValue is the same DER
|
|
839
839
|
// SubjectKeyIdentifier OCTET STRING.
|
|
840
840
|
function _crmKeyIdentity(crmDer) {
|
|
841
841
|
var msg = _asCertReqMsg(_der(crmDer, "a crm CertReqMsg"));
|
|
842
842
|
// pki.schema.crmf reads CertReqMessages (SEQUENCE OF CertReqMsg); one message is
|
|
843
|
-
// wrapped
|
|
843
|
+
// wrapped here and decoded there, so the CRMF rules stay in the CRMF parser.
|
|
844
844
|
var msgs = crmf.parse(b.sequence([msg])).messages;
|
|
845
845
|
var tmpl = msgs && msgs[0] && msgs[0].certReq && msgs[0].certReq.certTemplate;
|
|
846
846
|
var found = null;
|
|
@@ -849,7 +849,7 @@ function _crmKeyIdentity(crmDer) {
|
|
|
849
849
|
found = asn1.read.octetString(asn1.decode(e.value));
|
|
850
850
|
});
|
|
851
851
|
// RFC 4211 sec. 4.1: the requested key may live in the signature POP's
|
|
852
|
-
// POPOSigningKeyInput
|
|
852
|
+
// POPOSigningKeyInput instead of the CertTemplate, and the CRMF parser surfaces
|
|
853
853
|
// it there. Reading only the template would refuse a key-only signer whose
|
|
854
854
|
// request is perfectly conforming.
|
|
855
855
|
var msg0 = msgs && msgs[0];
|