@blamejs/pki 0.5.17 → 0.5.19
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 +60 -0
- package/lib/acme.js +6 -4
- package/lib/cmp-build.js +15 -1
- package/lib/cms-encrypt.js +155 -14
- package/lib/cms-sign.js +15 -1
- package/lib/crl-sign.js +61 -2
- package/lib/guard-intrinsic.js +27 -0
- package/lib/jose.js +82 -35
- package/lib/key.js +6 -0
- package/lib/pkcs12-build.js +201 -11
- package/lib/tsp-sign.js +64 -0
- package/lib/webcrypto.js +165 -90
- package/lib/x509-sign.js +37 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/tsp-sign.js
CHANGED
|
@@ -142,9 +142,38 @@ function sign(messageImprint, tsa, opts) {
|
|
|
142
142
|
], _sign);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// The three caller-owned objects pki.tsp.sign reads. A dropped option here is a token that quietly
|
|
146
|
+
// lacks what was asked for -- `odering` for `ordering` emitted a token with the ordering flag unset,
|
|
147
|
+
// which is a claim about the timestamp's orderability that the caller believes they made.
|
|
148
|
+
// The TSA is gated too, and the reason it needs to be is not the one it looks like. Both fields it
|
|
149
|
+
// carries are REQUIRED, so a MISSPELLING of either is already refused for what it leaves missing --
|
|
150
|
+
// but that is only half the class. A name belonging on the OPTIONS, written on the TSA instead, is
|
|
151
|
+
// read by nothing here: `ordering` placed on the TSA emits a token the same size as one that never
|
|
152
|
+
// asked for it, with the flag unset, while the caller believes they set it. Requiring the argument
|
|
153
|
+
// to carry only what it reads is what closes that, so all three caller-owned arguments answer the
|
|
154
|
+
// same question -- is this a name I read? -- rather than two of them answering it.
|
|
155
|
+
var _TSA_KEYS = { cert: 1, key: 1 };
|
|
156
|
+
var _IMPRINT_KEYS = { hashAlgorithm: 1, hashedMessage: 1 };
|
|
157
|
+
var _SIGN_OPTS = {
|
|
158
|
+
policy: 1, serialNumber: 1, genTime: 1, nonce: 1, accuracy: 1, ordering: 1, sid: 1,
|
|
159
|
+
digestAlgorithm: 1, certHashAlgorithm: 1, additionalSignedAttributes: 1, pss: 1, pem: 1,
|
|
160
|
+
};
|
|
161
|
+
|
|
145
162
|
function _sign(messageImprint, tsa, opts) {
|
|
146
163
|
opts = opts || {};
|
|
164
|
+
if (messageImprint && typeof messageImprint === "object" && !Buffer.isBuffer(messageImprint)) {
|
|
165
|
+
guard.identifier.assertKnownKeys(messageImprint, _IMPRINT_KEYS, _err, "tsp/bad-input", function (k) {
|
|
166
|
+
return "unknown messageImprint field " + JSON.stringify(k) + " (the imprint is { hashAlgorithm, hashedMessage })";
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
guard.identifier.assertKnownKeys(opts, _SIGN_OPTS, _err, "tsp/bad-input", "pki.tsp.sign has an unknown option ");
|
|
147
170
|
if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.sign options must be an object");
|
|
171
|
+
if (tsa && typeof tsa === "object" && !Buffer.isBuffer(tsa)) {
|
|
172
|
+
guard.identifier.assertKnownKeys(tsa, _TSA_KEYS, _err, "tsp/bad-input", function (k) {
|
|
173
|
+
return "unknown TSA field " + JSON.stringify(k) +
|
|
174
|
+
" (the TSA is { cert, key }; a signing option belongs in the third argument)";
|
|
175
|
+
});
|
|
176
|
+
}
|
|
148
177
|
var mi = messageImprint || {};
|
|
149
178
|
if (!mi.hashAlgorithm || !NODE_DIGEST[mi.hashAlgorithm]) throw _err("tsp/unsupported-algorithm", "messageImprint.hashAlgorithm must be a supported hash name");
|
|
150
179
|
if (!Buffer.isBuffer(mi.hashedMessage) && !(mi.hashedMessage instanceof Uint8Array)) throw _err("tsp/bad-input", "messageImprint.hashedMessage must be a Buffer");
|
|
@@ -187,7 +216,17 @@ function _sign(messageImprint, tsa, opts) {
|
|
|
187
216
|
// Accuracy ::= SEQUENCE { seconds INTEGER OPTIONAL, millis [0] INTEGER (1..999) OPTIONAL,
|
|
188
217
|
// micros [1] INTEGER (1..999) OPTIONAL } (RFC 3161 sec. 2.4.2). seconds is a non-negative
|
|
189
218
|
// integer; millis and micros MUST each be in 1..999 -- enforced before encoding.
|
|
219
|
+
// Accuracy ::= SEQUENCE { seconds INTEGER OPTIONAL, millis [0] OPTIONAL, micros [1] OPTIONAL }
|
|
220
|
+
// (RFC 3161 sec. 2.4.2). All three are OPTIONAL and encoded only when present, so a misspelling is
|
|
221
|
+
// missed by nothing: `milis` emitted an EMPTY Accuracy that parses back as zero seconds, millis and
|
|
222
|
+
// micros, and the token then understates its own precision -- a claim about the timestamp that the
|
|
223
|
+
// caller believed they had made.
|
|
224
|
+
var _ACCURACY_KEYS = { seconds: 1, millis: 1, micros: 1 };
|
|
225
|
+
|
|
190
226
|
function _accuracy(a) {
|
|
227
|
+
guard.identifier.assertKnownKeys(a, _ACCURACY_KEYS, _err, "tsp/bad-input", function (k) {
|
|
228
|
+
return "unknown accuracy field " + JSON.stringify(k) + "; Accuracy is { seconds?, millis?, micros? }";
|
|
229
|
+
});
|
|
191
230
|
var f = [];
|
|
192
231
|
if (a.seconds != null) {
|
|
193
232
|
var s = Number(a.seconds);
|
|
@@ -264,10 +303,29 @@ function _assertImprint(mi) {
|
|
|
264
303
|
* var sha256Digest = Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello")));
|
|
265
304
|
* var req = pki.tsp.request({ hashAlgorithm: "sha256", hashedMessage: sha256Digest }, { nonce: 0x0102030405060708n, certReq: true });
|
|
266
305
|
*/
|
|
306
|
+
// The options pki.tsp.request encodes into the TimeStampReq. Every one is OPTIONAL in the
|
|
307
|
+
// structure, so a misspelled name is missed by nothing and the request goes out claiming less than
|
|
308
|
+
// was asked for. `nonce` is the one that costs most: misspelled, the request carries no nonce at
|
|
309
|
+
// all, and the caller matches a reply against a replay defense that was never asked for.
|
|
310
|
+
var _REQUEST_OPTS = { reqPolicy: 1, nonce: 1, certReq: 1, extensions: 1, pem: 1 };
|
|
311
|
+
|
|
267
312
|
function request(messageImprint, opts) {
|
|
268
313
|
opts = opts || {};
|
|
269
314
|
if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.request options must be an object");
|
|
315
|
+
guard.identifier.assertKnownKeys(opts, _REQUEST_OPTS, _err, "tsp/bad-input", "pki.tsp.request has an unknown option ");
|
|
270
316
|
var mi = messageImprint || {};
|
|
317
|
+
// The same table pki.tsp.sign holds its imprint to. The request's own options live in the second
|
|
318
|
+
// argument, so `nonce` written on the imprint reaches nothing and the request goes out without
|
|
319
|
+
// the replay defense the caller believes they asked for.
|
|
320
|
+
// The same table pki.tsp.sign holds its imprint to. The request's own options live in the second
|
|
321
|
+
// argument, so `nonce` written on the imprint reaches nothing and the request goes out without
|
|
322
|
+
// the replay defense the caller believes they asked for.
|
|
323
|
+
if (mi && typeof mi === "object" && !Buffer.isBuffer(mi)) {
|
|
324
|
+
guard.identifier.assertKnownKeys(mi, _IMPRINT_KEYS, _err, "tsp/bad-input", function (k) {
|
|
325
|
+
return "unknown messageImprint field " + JSON.stringify(k) +
|
|
326
|
+
" (the imprint is { hashAlgorithm, hashedMessage }; a request option belongs in the second argument)";
|
|
327
|
+
});
|
|
328
|
+
}
|
|
271
329
|
_assertImprint(mi);
|
|
272
330
|
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(Buffer.from(mi.hashedMessage))]);
|
|
273
331
|
// TimeStampReq: version(1), messageImprint, then OPTIONAL reqPolicy / nonce / certReq /
|
|
@@ -349,9 +407,15 @@ function parseRequest(input) { return schemaTsp.parseRequest(input); }
|
|
|
349
407
|
* var resp = pki.tsp.response(token, {}); // granted
|
|
350
408
|
* var rej = pki.tsp.response(null, { status: 2, failInfo: ["badAlg"] }); // rejection
|
|
351
409
|
*/
|
|
410
|
+
// The options pki.tsp.response encodes into the TimeStampResp. `status` carries the verdict and
|
|
411
|
+
// defaults to granted(0), so a misspelled name does not lose a detail -- it INVERTS the answer: a
|
|
412
|
+
// TSA refusing a request emits a granted response instead, over a token it did mean to withhold.
|
|
413
|
+
var _RESPONSE_OPTS = { status: 1, statusString: 1, failInfo: 1, pem: 1 };
|
|
414
|
+
|
|
352
415
|
function response(token, opts) {
|
|
353
416
|
opts = opts || {};
|
|
354
417
|
if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.response options must be an object");
|
|
418
|
+
guard.identifier.assertKnownKeys(opts, _RESPONSE_OPTS, _err, "tsp/bad-input", "pki.tsp.response has an unknown option ");
|
|
355
419
|
var status = opts.status == null ? 0 : Number(opts.status);
|
|
356
420
|
if (!Number.isInteger(status) || status < 0 || status > 5) throw _err("tsp/bad-input", "PKIStatus must be an integer in 0..5 (RFC 3161 sec. 2.4.2)");
|
|
357
421
|
var granted = status === 0 || status === 1;
|