@blamejs/pki 0.8.31 → 0.8.49
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 +401 -0
- package/README.md +66 -15
- package/bin/pki.js +716 -30
- package/examples/issue-a-certificate.js +90 -0
- package/examples/post-quantum.js +66 -0
- package/examples/read-a-certificate.js +56 -0
- package/examples/revoke-and-check.js +86 -0
- package/examples/sign-and-verify-cms.js +63 -0
- package/index.js +18 -5
- package/lib/acme.js +223 -74
- package/lib/alt-sig.js +441 -0
- package/lib/asn1-der.js +48 -25
- package/lib/attrcert-sign.js +27 -19
- package/lib/cbor-det.js +24 -14
- package/lib/cmc-build.js +9 -2
- package/lib/cmc-verify.js +14 -3
- package/lib/cmp-build.js +17 -5
- package/lib/cmp-session.js +24 -11
- package/lib/cmp-verify.js +28 -19
- package/lib/cms-compress.js +3 -1
- package/lib/cms-decrypt.js +102 -7
- package/lib/cms-encrypt.js +59 -7
- package/lib/cms-sign.js +31 -9
- package/lib/cms-verify.js +22 -18
- package/lib/composite-kem.js +30 -18
- package/lib/composite-sig.js +29 -14
- package/lib/constants.js +26 -0
- package/lib/crl-sign.js +111 -19
- package/lib/crmf-sign.js +167 -15
- package/lib/csr-sign.js +117 -6
- package/lib/ct.js +1008 -58
- package/lib/est.js +29 -16
- package/lib/framework-error.js +12 -0
- package/lib/guard-async.js +1 -1
- package/lib/guard-bytes.js +103 -20
- package/lib/guard-crypto.js +205 -3
- package/lib/guard-encoding.js +6 -1
- package/lib/guard-identifier.js +55 -8
- package/lib/guard-intrinsic.js +43 -0
- package/lib/guard-json.js +122 -9
- package/lib/guard-limits.js +12 -1
- package/lib/guard-list.js +63 -8
- package/lib/guard-name.js +167 -20
- package/lib/guard-range.js +43 -5
- package/lib/guard-text.js +41 -0
- package/lib/guard-verdict.js +39 -8
- package/lib/hpke.js +688 -42
- package/lib/http-transport.js +156 -4
- package/lib/identity-match.js +4 -4
- package/lib/inspect.js +33 -6
- package/lib/jose.js +93 -25
- package/lib/key.js +18 -13
- package/lib/lint.js +165 -1
- package/lib/merkle.js +233 -27
- package/lib/ocsp.js +27 -14
- package/lib/oid.js +14 -4
- package/lib/path-validate.js +143 -61
- package/lib/pkcs12-build.js +7 -2
- package/lib/pki-build.js +31 -7
- package/lib/possession.js +575 -0
- package/lib/related-cert.js +708 -0
- package/lib/revocation-fetch.js +6 -6
- package/lib/scep.js +15 -5
- package/lib/schema-c509.js +5 -2
- package/lib/schema-cmc.js +1 -1
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +2 -15
- package/lib/schema-csr.js +69 -0
- package/lib/schema-engine.js +10 -3
- package/lib/schema-pkix.js +228 -2
- package/lib/schema-tsp.js +7 -6
- package/lib/sign-scheme.js +22 -9
- package/lib/sigstore.js +684 -168
- package/lib/sleep.js +8 -1
- package/lib/smime.js +22 -1
- package/lib/tlog.js +1235 -0
- package/lib/tls-cert-compress.js +25 -8
- package/lib/tsp-sign.js +120 -35
- package/lib/tuf.js +1395 -0
- package/lib/validator-tpm.js +4 -1
- package/lib/verify-seam.js +7 -1
- package/lib/webauthn-mds.js +41 -4
- package/lib/webauthn.js +43 -25
- package/lib/webcrypto.js +48 -46
- package/lib/x509-sign.js +225 -19
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
package/lib/x509-sign.js
CHANGED
|
@@ -22,7 +22,12 @@ var x509 = require("./schema-x509");
|
|
|
22
22
|
var signScheme = require("./sign-scheme");
|
|
23
23
|
var guard = require("./guard-all");
|
|
24
24
|
var intrinsic = require("./guard-intrinsic");
|
|
25
|
-
var _isArray = intrinsic.isArray, _bufferEquals = intrinsic.bufferEquals;
|
|
25
|
+
var _isArray = intrinsic.isArray, _bufferEquals = intrinsic.bufferEquals, _isBuffer = intrinsic.isBuffer;
|
|
26
|
+
var _arrSlice = intrinsic.arraySlice, _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
27
|
+
/** @internal The RECEIVER for the captured `Promise.resolve`, captured too. `resolve` builds through
|
|
28
|
+
* its receiver, so handing it the live `globalThis.Promise` put the capture back under a constructor a
|
|
29
|
+
* replacement could take over and decide what the awaited value became. */
|
|
30
|
+
var _Promise = intrinsic.Promise;
|
|
26
31
|
var _dnCharCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
27
32
|
var _dnKeys = intrinsic.keys;
|
|
28
33
|
var _dnHasOwn = intrinsic.hasOwn;
|
|
@@ -31,6 +36,7 @@ var schema = require("./schema-engine");
|
|
|
31
36
|
var pkix = require("./schema-pkix");
|
|
32
37
|
var pkiBuild = require("./pki-build");
|
|
33
38
|
var ct = require("./ct");
|
|
39
|
+
var relatedCert = require("./related-cert");
|
|
34
40
|
var lint = require("./lint");
|
|
35
41
|
|
|
36
42
|
var CertificateError = frameworkError.CertificateError;
|
|
@@ -46,8 +52,11 @@ function O(n) { return oid.byName(n); }
|
|
|
46
52
|
var KNOWN_SPEC_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
47
53
|
subject: 1, subjectPublicKey: 1, notBefore: 1, notAfter: 1, serialNumber: 1, extensions: 1,
|
|
48
54
|
});
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
/** @internal `altKey` and `altPublicKey` are the issuer's ALTERNATIVE signing key and its public half,
|
|
56
|
+
* for an ITU-T X.509 (2019) clause 9.8 certificate. They appear in both issuer forms because the
|
|
57
|
+
* alternative key is the issuer's own and is unrelated to how the native issuer was named. */
|
|
58
|
+
var KNOWN_ISSUER_CERT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, cert: 1, altKey: 1, altPublicKey: 1 });
|
|
59
|
+
var KNOWN_ISSUER_EXPLICIT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, name: 1, publicKey: 1, altKey: 1, altPublicKey: 1 });
|
|
51
60
|
var KNOWN_SIGN_OPTS = intrinsic.assign(intrinsic.create(null), { digestAlgorithm: 1, pem: 1, pss: 1, profile: 1 });
|
|
52
61
|
|
|
53
62
|
/** @internal The signature the build-time gate lints against, standing in for one that does not exist
|
|
@@ -66,6 +75,11 @@ var KNOWN_EXT_KEYS = intrinsic.assign(intrinsic.create(null), {
|
|
|
66
75
|
msCertificateTemplate: 1, msEnrollCertType: 1, msCaVersion: 1, msPreviousCertHash: 1,
|
|
67
76
|
msApplicationPolicies: 1,
|
|
68
77
|
subjectInfoAccess: 1, subjectDirectoryAttributes: 1, ocspNoCheck: 1,
|
|
78
|
+
relatedCertificate: 1,
|
|
79
|
+
/** @internal The clause 9.8 alternative set. `altSignatureAlgorithm` and `altSignatureValue` are
|
|
80
|
+
* accepted as names so a caller that supplies either is told they are resolved and computed here,
|
|
81
|
+
* rather than being told the name is unknown. */
|
|
82
|
+
subjectAltPublicKeyInfo: 1, altSignatureAlgorithm: 1, altSignatureValue: 1,
|
|
69
83
|
});
|
|
70
84
|
|
|
71
85
|
var _b = pkiBuild.makeBuilder({ ErrorClass: CertificateError, prefix: "x509", O: O, NS: NS, NAME_SCHEMA: NAME_SCHEMA, SPKI_SCHEMA: SPKI_SCHEMA, EXT_DECODERS: EXT_DECODERS });
|
|
@@ -73,6 +87,7 @@ var _encodeName = _b.encodeName, _isEmptyName = _b.isEmptyName, _reqDer = _b.req
|
|
|
73
87
|
_assertValidSpki = _b.assertValidSpki, _assertValidExtension = _b.assertValidExtension,
|
|
74
88
|
_assertCertCriticality = _b.assertCertCriticality,
|
|
75
89
|
_certLikeFromSpki = _b.certLikeFromSpki, _assertCertVerifies = _b.assertSignatureVerifies,
|
|
90
|
+
_altPublicKeyValueOf = _b.altPublicKeyValueOf,
|
|
76
91
|
_ext = _b.ext, _extBasicConstraints = _b.extBasicConstraints, _validateBcSpec = _b.validateBcSpec,
|
|
77
92
|
_extKeyUsage = _b.extKeyUsage, _extExtKeyUsage = _b.extExtKeyUsage, _extSki = _b.extSki,
|
|
78
93
|
_extNameConstraints = _b.extNameConstraints, _extAia = _b.extAuthorityInfoAccess,
|
|
@@ -135,12 +150,26 @@ function _akiFields(val, ctx) {
|
|
|
135
150
|
return [keyId, issuerName == null ? null : issuerName, serial == null ? null : serial];
|
|
136
151
|
}
|
|
137
152
|
|
|
153
|
+
/** @internal The extensions this verb owns the value of, so a caller never supplies them pre-encoded. */
|
|
154
|
+
var RESERVED_ALT_EXTS = intrinsic.assign(intrinsic.create(null), (function () {
|
|
155
|
+
var o = intrinsic.create(null);
|
|
156
|
+
o[O("altSignatureValue")] = 1;
|
|
157
|
+
o[O("altSignatureAlgorithm")] = 1;
|
|
158
|
+
o[O("subjectAltPublicKeyInfo")] = 1;
|
|
159
|
+
return o;
|
|
160
|
+
})());
|
|
161
|
+
|
|
138
162
|
function _buildExtensions(extSpec, ctx) {
|
|
139
163
|
if (extSpec == null) extSpec = {};
|
|
140
164
|
if (_isArray(extSpec)) {
|
|
141
165
|
var seenExt = intrinsic.create(null), arrCa = false, arrKeyCertSign = false, arrPathLen = false, arrNameConstraints = false;
|
|
142
166
|
var oidBc = O("basicConstraints"), oidKu = O("keyUsage"), oidNc = O("nameConstraints"), oidPm = O("policyMappings");
|
|
143
|
-
|
|
167
|
+
/** @internal The walk goes through `guard.list.copyMap`, never the caller's own `map`. Reached off
|
|
168
|
+
* the array a caller supplied, `map` is that caller's to replace: one returning extension DER the
|
|
169
|
+
* callback below never saw bypassed every per-element rule at once, the duplicate check, the
|
|
170
|
+
* criticality rule, the value decoders and the alternative-signature refusal among them, and the
|
|
171
|
+
* verb then emitted and natively signed a certificate carrying extensions nothing had validated. */
|
|
172
|
+
var arr = guard.list.copyMap(extSpec, function (e, i) {
|
|
144
173
|
var der = _reqDer(e, "extension");
|
|
145
174
|
_assertValidExtension(der, i, EXT_DECODERS);
|
|
146
175
|
var n = asn1.decode(der);
|
|
@@ -164,6 +193,22 @@ function _buildExtensions(extSpec, ctx) {
|
|
|
164
193
|
else if (extnId === oidKu) { arrKeyCertSign = decoded.keyCertSign === true; }
|
|
165
194
|
else if (extnId === oidPm) { _assertNoAnyPolicyMapping(decoded); }
|
|
166
195
|
}
|
|
196
|
+
/** @internal The three alternative-signature extensions are refused HERE as well as in the object
|
|
197
|
+
* form below, and AFTER the decode above so a malformed value is still reported as one. The
|
|
198
|
+
* object form's guards were the only ones, so the pre-encoded array reached none of them: a
|
|
199
|
+
* caller could supply `altSignatureValue` and `altSignatureAlgorithm` outright, and the verb then
|
|
200
|
+
* emitted and natively signed a certificate carrying an alternative signature it never computed
|
|
201
|
+
* or self-checked, which `pki.altSig.verify` refuses for the migrated client the extension exists
|
|
202
|
+
* for. `subjectAltPublicKeyInfo` is refused in this form rather than honored: clause 7.2.2 has
|
|
203
|
+
* all three travel together, the alternative signing side reads the subject key off the OBJECT
|
|
204
|
+
* form only, and a certificate carrying the subject's alternative key with no alternative
|
|
205
|
+
* signature has an alternative half nothing can verify. */
|
|
206
|
+
if (_dnHasOwn(RESERVED_ALT_EXTS, extnId)) {
|
|
207
|
+
throw _err("x509/bad-input", (oid.name(extnId) || extnId) + " is not supplied as a pre-encoded " +
|
|
208
|
+
"extension: this verb computes the alternative signature and resolves its algorithm from " +
|
|
209
|
+
"issuer.altKey, and clause 7.2.2 emits all three together. Pass issuer.altKey with the " +
|
|
210
|
+
"object form of spec.extensions instead (ITU-T X.509 (2019) clause 7.2.2).");
|
|
211
|
+
}
|
|
167
212
|
return b.raw(der);
|
|
168
213
|
});
|
|
169
214
|
if (arrKeyCertSign && !arrCa) throw _err("x509/bad-input", "keyUsage keyCertSign requires basicConstraints cA=TRUE (RFC 5280 sec. 4.2.1.3)");
|
|
@@ -273,9 +318,122 @@ function _buildExtensions(extSpec, ctx) {
|
|
|
273
318
|
if (extSpec.ocspNoCheck !== true) throw _err("x509/bad-input", "ocspNoCheck is a flag, so it is true or omitted");
|
|
274
319
|
guard.list.append(out, _ext(O("ocspNoCheck"), false, asn1.build.nullValue()));
|
|
275
320
|
}
|
|
321
|
+
/** @internal RFC 9763 sec. 4.1 names a second certificate of the same subject by the digest of the
|
|
322
|
+
* whole certificate, and the section says the extension SHOULD NOT be marked critical, so it is
|
|
323
|
+
* emitted non-critical with no option to mark it. */
|
|
324
|
+
if (extSpec.relatedCertificate != null) guard.list.append(out, _ext(O("relatedCertificate"), false, _relatedCertificate(extSpec.relatedCertificate)));
|
|
325
|
+
/** @internal ITU-T X.509 (2019) clause 9.8.2. The subject's alternative public key, and the algorithm
|
|
326
|
+
* identifier of the issuer's alternative signature beside it: clause 7.2.2 requires all three of the
|
|
327
|
+
* alternative extensions together, and the third is filled in by the signing pass below because its
|
|
328
|
+
* value is a signature over these two. The NOTEs in clauses 9.8.2 and 9.8.3 recommend non-critical
|
|
329
|
+
* and the clauses permit either, so these are emitted non-critical and there is no option to mark
|
|
330
|
+
* them: marking them would require every relying party to understand the alternative algorithm. */
|
|
331
|
+
if (ctx.altSigAlgId != null) {
|
|
332
|
+
guard.list.append(out, _ext(O("subjectAltPublicKeyInfo"), false, b.raw(ctx.subjectAltSpki)));
|
|
333
|
+
guard.list.append(out, _ext(O("altSignatureAlgorithm"), false, b.raw(ctx.altSigAlgId)));
|
|
334
|
+
}
|
|
276
335
|
return out;
|
|
277
336
|
}
|
|
278
337
|
|
|
338
|
+
var KNOWN_ALT_ISSUER_KEYS = intrinsic.assign(intrinsic.create(null), { altKey: 1, altPublicKey: 1 });
|
|
339
|
+
/** @internal The alternative signing side of clause 9.8, resolved before any signing happens. All three
|
|
340
|
+
* extensions travel together (clause 7.2.2), so a subject alternative key without an issuer
|
|
341
|
+
* alternative key is refused and so is the reverse: either alone emits a certificate whose alternative
|
|
342
|
+
* half cannot be verified. `altSignatureValue` is never a caller's to supply, because its value is a
|
|
343
|
+
* signature this verb computes and a supplied one would be emitted unchecked. */
|
|
344
|
+
function _resolveAltSigning(issuer, extSpec, issuerCert, selfSigned) {
|
|
345
|
+
var subjectAltRaw = (extSpec != null && !_isArray(extSpec)) ? extSpec.subjectAltPublicKeyInfo : null;
|
|
346
|
+
var hasAltKey = issuer.altKey != null;
|
|
347
|
+
if (extSpec != null && !_isArray(extSpec) && extSpec.altSignatureValue != null) {
|
|
348
|
+
throw _err("x509/bad-input", "altSignatureValue is computed by this verb from the alternative signing key, so it is not supplied (ITU-T X.509 (2019) clause 7.2.2)");
|
|
349
|
+
}
|
|
350
|
+
if (extSpec != null && !_isArray(extSpec) && extSpec.altSignatureAlgorithm != null) {
|
|
351
|
+
throw _err("x509/bad-input", "altSignatureAlgorithm is resolved from the alternative signing key, so it is not supplied (ITU-T X.509 (2019) clause 9.8.3)");
|
|
352
|
+
}
|
|
353
|
+
/** @internal An `altPublicKey` with no `altKey` is refused rather than passed over. Returning null on
|
|
354
|
+
* the pair of absences read `altPublicKey` as absent too, so an issuer that supplied it and omitted
|
|
355
|
+
* the private half got an ordinary certificate and no indication that the alternative signature it
|
|
356
|
+
* asked for was never made. `pki.crl.sign` refuses the same pair, and a configuration assembled a
|
|
357
|
+
* field at a time is exactly where the omission happens. */
|
|
358
|
+
if (subjectAltRaw == null && !hasAltKey) {
|
|
359
|
+
if (issuer.altPublicKey != null) {
|
|
360
|
+
throw _err("x509/bad-input", "issuer.altPublicKey needs issuer.altKey to sign the alternative signature with (ITU-T X.509 (2019) clause 7.2.2)");
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
if (subjectAltRaw == null) {
|
|
365
|
+
throw _err("x509/bad-input", "an issuer altKey needs extensions.subjectAltPublicKeyInfo: clause 7.2.2 requires all three alternative extensions together");
|
|
366
|
+
}
|
|
367
|
+
if (!hasAltKey) {
|
|
368
|
+
throw _err("x509/bad-input", "extensions.subjectAltPublicKeyInfo needs an issuer altKey to sign the alternative signature with (ITU-T X.509 (2019) clause 7.2.2)");
|
|
369
|
+
}
|
|
370
|
+
guard.identifier.assertKnownKeys({ altKey: issuer.altKey, altPublicKey: issuer.altPublicKey },
|
|
371
|
+
KNOWN_ALT_ISSUER_KEYS, _err, "x509/bad-input", "unknown alternative issuer field: ");
|
|
372
|
+
var subjectAltSpki = _reqDer(subjectAltRaw, "extensions.subjectAltPublicKeyInfo (the subject's alternative SPKI DER)");
|
|
373
|
+
_assertValidSpki(subjectAltSpki, "extensions.subjectAltPublicKeyInfo");
|
|
374
|
+
if (issuer.altPublicKey == null) {
|
|
375
|
+
throw _err("x509/bad-input", "an issuer altKey needs issuer.altPublicKey, the SPKI of its public half, to resolve the alternative signature algorithm");
|
|
376
|
+
}
|
|
377
|
+
var issuerAltSpki = _reqDer(issuer.altPublicKey, "issuer.altPublicKey (the issuer's alternative SPKI DER)");
|
|
378
|
+
_assertValidSpki(issuerAltSpki, "issuer.altPublicKey");
|
|
379
|
+
/** @internal Bound to the issuer, the way the native signature already is: its key is read OUT of
|
|
380
|
+
* issuer.cert, while this one arrives beside the certificate, so nothing otherwise stops a CA signing
|
|
381
|
+
* with a second alternative pair. Clause 9.8.4 has a relying party check an alternative signature with
|
|
382
|
+
* the issuer's alternative public key, meaning the one the issuer's own certificate publishes, so a
|
|
383
|
+
* signature made with any other key verifies for nobody and the certificate is unusable on the half it
|
|
384
|
+
* was issued for. A self-signed certificate publishes its own alternative key in the extension it is
|
|
385
|
+
* being given, so that is what it is held to. */
|
|
386
|
+
if (issuerCert !== null && issuerCert !== undefined) {
|
|
387
|
+
var published = _altPublicKeyValueOf(issuerCert);
|
|
388
|
+
if (published === null) {
|
|
389
|
+
throw _err("x509/bad-input",
|
|
390
|
+
"issuer.cert publishes no subjectAltPublicKeyInfo, so it names no alternative key for a relying party to check an alternative signature with (ITU-T X.509 (2019) clause 9.8.4)");
|
|
391
|
+
}
|
|
392
|
+
if (!_bufferEquals(published, issuerAltSpki)) {
|
|
393
|
+
throw _err("x509/bad-input",
|
|
394
|
+
"issuer.altPublicKey is not the alternative key issuer.cert publishes in its subjectAltPublicKeyInfo, so the alternative signature would verify under no key a relying party can find (ITU-T X.509 (2019) clause 9.8.4)");
|
|
395
|
+
}
|
|
396
|
+
} else if (selfSigned === true && !_bufferEquals(subjectAltSpki, issuerAltSpki)) {
|
|
397
|
+
throw _err("x509/bad-input",
|
|
398
|
+
"a self-signed certificate publishes its own alternative key, so issuer.altPublicKey must be extensions.subjectAltPublicKeyInfo");
|
|
399
|
+
}
|
|
400
|
+
var altScheme = signScheme.resolveSignScheme(_certLikeFromSpki(issuerAltSpki), { combinedRsaSig: true }, true, _signE);
|
|
401
|
+
return { subjectAltSpki: subjectAltSpki, issuerAltSpki: issuerAltSpki, scheme: altScheme, altSigAlgId: altScheme.sigAlgId };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
var KNOWN_RELATED_CERTIFICATE_KEYS = intrinsic.assign(intrinsic.create(null), { hashAlgorithm: 1, hashValue: 1, relatedCertificate: 1 });
|
|
405
|
+
/** @internal RelatedCertificate ::= SEQUENCE { hashAlgorithm DigestAlgorithmIdentifier,
|
|
406
|
+
* hashValue OCTET STRING } (RFC 9763 sec. 4.1). `relatedCertificate` hands over the certificate and
|
|
407
|
+
* the digest is computed here, which is the form that cannot name one algorithm while carrying the
|
|
408
|
+
* output of another; `hashAlgorithm` + `hashValue` carries a digest already computed elsewhere. Both
|
|
409
|
+
* at once is refused, since the two can disagree and only one of them would be emitted. */
|
|
410
|
+
function _relatedCertificate(spec) {
|
|
411
|
+
if (spec === null || typeof spec !== "object" || _isBuffer(spec) || _isArray(spec)) {
|
|
412
|
+
throw _err("x509/bad-input", "relatedCertificate must be an object { relatedCertificate } or { hashAlgorithm, hashValue }");
|
|
413
|
+
}
|
|
414
|
+
guard.identifier.assertKnownKeys(spec, KNOWN_RELATED_CERTIFICATE_KEYS, _err, "x509/bad-input", "relatedCertificate has an unknown field: ");
|
|
415
|
+
var hasCert = spec.relatedCertificate != null, hasValue = spec.hashValue != null;
|
|
416
|
+
if (hasCert && hasValue) {
|
|
417
|
+
throw _err("x509/bad-input", "relatedCertificate takes either the related certificate to hash or a hashValue already computed, not both");
|
|
418
|
+
}
|
|
419
|
+
var value;
|
|
420
|
+
if (hasCert) {
|
|
421
|
+
try { value = relatedCert.certificateHash(spec.relatedCertificate, spec.hashAlgorithm); }
|
|
422
|
+
catch (e) { throw _err("x509/bad-input", "relatedCertificate: " + ((e && e.message) || intrinsic.String(e)), e); }
|
|
423
|
+
} else {
|
|
424
|
+
if (!hasValue) throw _err("x509/bad-input", "relatedCertificate needs either the related certificate to hash or a hashAlgorithm with a hashValue");
|
|
425
|
+
if (spec.hashAlgorithm == null) throw _err("x509/bad-input", "relatedCertificate hashValue must be accompanied by the hashAlgorithm that produced it");
|
|
426
|
+
var bytes = _reqDer(spec.hashValue, "relatedCertificate hashValue");
|
|
427
|
+
/** @internal Routed through the same reader that holds a parsed value to its algorithm's length,
|
|
428
|
+
* so a digest this build emits and a digest it reads back are held to one rule. */
|
|
429
|
+
try { value = relatedCert.digestValue(spec.hashAlgorithm, bytes); }
|
|
430
|
+
catch (e) { throw _err("x509/bad-input", "relatedCertificate: " + ((e && e.message) || intrinsic.String(e)), e); }
|
|
431
|
+
}
|
|
432
|
+
var algOid = O(value.hashAlgorithm);
|
|
433
|
+
if (!algOid) throw _err("x509/bad-input", "relatedCertificate hashAlgorithm " + value.hashAlgorithm + " has no registered OID");
|
|
434
|
+
return asn1.build.sequence([asn1.build.sequence([asn1.build.oid(algOid)]), asn1.build.octetString(value.hashValue)]);
|
|
435
|
+
}
|
|
436
|
+
|
|
279
437
|
|
|
280
438
|
/** @internal RFC 6962 sec. 3.1 and sec. 3.2 put these at the two ends of one exchange: the poison
|
|
281
439
|
* names the precertificate submitted to a log, and the SCT list names the certificate issued once
|
|
@@ -589,7 +747,13 @@ function _sign(spec, issuer, opts) {
|
|
|
589
747
|
_err, "x509/bad-input", "issuer/subject DN");
|
|
590
748
|
var selfSignedCert = selfIssued && _b.samePublicKey(issuerSpki, spki);
|
|
591
749
|
var extSpec = spec.extensions;
|
|
592
|
-
|
|
750
|
+
/** @internal `selfSignedCert`, not `selfSigned`: a root is self-signed whether the caller omits the
|
|
751
|
+
* issuer or names one whose name and key are the subject's, and the alternative-key binding applies to
|
|
752
|
+
* both. `selfSigned` is only the omitted form, and it is what the extension builder already passes
|
|
753
|
+
* `selfSignedCert` for the same reason. */
|
|
754
|
+
var alt = _resolveAltSigning(issuer, extSpec, issuerCert, selfSignedCert);
|
|
755
|
+
var exts = _buildExtensions(extSpec, { spki: spki, issuerSpki: issuerSpki, issuerCert: issuerCert, subjectEmpty: subjectEmpty, selfSigned: selfSignedCert,
|
|
756
|
+
altSigAlgId: alt === null ? null : alt.altSigAlgId, subjectAltSpki: alt === null ? null : alt.subjectAltSpki });
|
|
593
757
|
if (subjectEmpty && !_hasCriticalSan(extSpec)) {
|
|
594
758
|
throw _err("x509/bad-input", "an empty subject requires a critical subjectAltName (RFC 5280 sec. 4.1.2.6)");
|
|
595
759
|
}
|
|
@@ -602,22 +766,63 @@ function _sign(spec, issuer, opts) {
|
|
|
602
766
|
}
|
|
603
767
|
var version = exts.length ? 3 : 1;
|
|
604
768
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
769
|
+
/** @internal The tbsCertificate over a given extension list, so the two passes an alternative
|
|
770
|
+
* signature needs build it the same way rather than twice. `omitSignatureAlg` drops the `signature`
|
|
771
|
+
* AlgorithmIdentifier, which turns the TBSCertificate into the PreTBSCertificate the alternative
|
|
772
|
+
* signature covers (draft-truskovsky-lamps-pq-hybrid-x509 sec. 4). */
|
|
773
|
+
function tbsOver(extList, omitSignatureAlg) {
|
|
774
|
+
var tbsChildren = [];
|
|
775
|
+
if (version !== 1) tbsChildren.push(b.explicit(0, b.integer(BigInt(version - 1))));
|
|
776
|
+
tbsChildren.push(serialTlv);
|
|
777
|
+
if (omitSignatureAlg !== true) tbsChildren.push(scheme.sigAlgId);
|
|
778
|
+
tbsChildren.push(issuerDer);
|
|
779
|
+
tbsChildren.push(validityDer);
|
|
780
|
+
tbsChildren.push(subjectDer);
|
|
781
|
+
tbsChildren.push(b.raw(spki));
|
|
782
|
+
if (extList.length) tbsChildren.push(b.explicit(3, b.sequence(extList)));
|
|
783
|
+
return b.sequence(tbsChildren);
|
|
784
|
+
}
|
|
618
785
|
|
|
786
|
+
/** @internal Clause 7.2.2 fixes the order: the alternative signature is generated first, over the
|
|
787
|
+
* PreTBSCertificate, which carries neither it nor the `signature` AlgorithmIdentifier, and the native
|
|
788
|
+
* signature then covers the tbsCertificate WITH both. Doing it the other way round leaves a native
|
|
789
|
+
* signature that does not cover the alternative one, which is the ordering error the two-pass exists
|
|
790
|
+
* to make impossible. draft-truskovsky-lamps-pq-hybrid-x509 sec. 4.1 adds the `signature` field in
|
|
791
|
+
* step (d), after the alternative signature of step (b), which is why it is absent here. */
|
|
792
|
+
/** @internal The alternative signature is self-checked against issuer.altPublicKey before it is
|
|
793
|
+
* embedded, the same gate the native pass applies to its own signature. Without it an altKey and an
|
|
794
|
+
* altPublicKey that are different keys of one algorithm both resolve to the same scheme, so signing
|
|
795
|
+
* succeeds and what ships is a certificate whose alternative signature does not verify under the
|
|
796
|
+
* alternative key its own issuer names. */
|
|
797
|
+
var altFirst;
|
|
798
|
+
if (alt === null) {
|
|
799
|
+
altFirst = _promiseResolve(_Promise, null);
|
|
800
|
+
} else {
|
|
801
|
+
var altPreimage = tbsOver(exts, true);
|
|
802
|
+
altFirst = signScheme.signOverTbs(alt.scheme, issuer.altKey, altPreimage, _signE)
|
|
803
|
+
.then(function (altSigBytes) {
|
|
804
|
+
return _promiseResolve(_Promise,
|
|
805
|
+
_assertCertVerifies(altPreimage, altSigBytes, alt.issuerAltSpki, alt.scheme)
|
|
806
|
+
).then(function () { return altSigBytes; });
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
return altFirst.then(function (altSigBytes) {
|
|
811
|
+
var finalExts = exts;
|
|
812
|
+
if (alt !== null) {
|
|
813
|
+
finalExts = _arrSlice(exts);
|
|
814
|
+
guard.list.append(finalExts, _ext(O("altSignatureValue"), false, b.bitString(altSigBytes, 0)));
|
|
815
|
+
}
|
|
816
|
+
var tbsDer = tbsOver(finalExts);
|
|
817
|
+
lint.assertBuildClean("certificate",
|
|
818
|
+
b.sequence([tbsDer, scheme.sigAlgId, b.bitString(GATE_PLACEHOLDER_SIG, 0)]),
|
|
819
|
+
opts.profile, _err, "x509/profile-violation");
|
|
820
|
+
return _signNative(tbsDer);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
function _signNative(tbsDer) {
|
|
619
824
|
return signScheme.signOverTbs(scheme, issuer.key, tbsDer, _signE).then(function (sig) {
|
|
620
|
-
return
|
|
825
|
+
return _promiseResolve(_Promise, _assertCertVerifies(tbsDer, sig, issuerSpki, scheme)).then(function () {
|
|
621
826
|
var certDer = b.sequence([tbsDer, scheme.sigAlgId, b.bitString(sig, 0)]);
|
|
622
827
|
return opts.pem ? x509.pemEncode(certDer, "CERTIFICATE") : certDer;
|
|
623
828
|
});
|
|
@@ -625,6 +830,7 @@ function _sign(spec, issuer, opts) {
|
|
|
625
830
|
if (e instanceof CertificateError) throw e;
|
|
626
831
|
throw _err("x509/bad-input", "signing the certificate failed -- the signing key does not match the resolved algorithm or is invalid", e);
|
|
627
832
|
});
|
|
833
|
+
}
|
|
628
834
|
}
|
|
629
835
|
|
|
630
836
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/pki",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.49",
|
|
4
4
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "blamejs contributors",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"index.js",
|
|
58
58
|
"bin/",
|
|
59
59
|
"lib/",
|
|
60
|
+
"examples/*.js",
|
|
60
61
|
"CHANGELOG.md",
|
|
61
62
|
"LICENSE",
|
|
62
63
|
"MIGRATING.md",
|
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:850d86e8-9f55-4854-9b3a-89ed4e16dfd5",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-
|
|
8
|
+
"timestamp": "2026-10-05T08:10:11.062Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.8.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.8.49",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.49",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.8.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.8.49",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.8.
|
|
57
|
+
"ref": "@blamejs/pki@0.8.49",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|