@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/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;