@blamejs/pki 0.8.30 → 0.8.48
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 +411 -0
- package/README.md +67 -16
- 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 +28 -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 +939 -48
- 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 +448 -2
- 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 +267 -4
- 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/schema-pkix.js
CHANGED
|
@@ -479,11 +479,44 @@ function coerceToDer(input, opts) {
|
|
|
479
479
|
* would otherwise be read end to end on the way to being refused for its size. */
|
|
480
480
|
guard.limits.byteCap(buf, _COERCE_MAX_BYTES, function (c, m) { return new opts.ErrorClass(c, m); },
|
|
481
481
|
opts.prefix + "/too-large", "parse input");
|
|
482
|
-
|
|
482
|
+
if (!_isPemArmor(buf)) return buf;
|
|
483
|
+
/** @internal A DER header short enough for the short form can be entirely text bytes, so the
|
|
484
|
+
* probe alone cannot separate armor from a DER value carrying an armor line in its content: an
|
|
485
|
+
* APPLICATION 3 primitive is identifier 0x43 and any content length from 32 to 126 is a
|
|
486
|
+
* printable length octet. Bytes that do not decode as one complete DER value are armor, which is
|
|
487
|
+
* the resolution `pki convert` takes and keeps every PEM file a caller reads into a Buffer on the
|
|
488
|
+
* PEM path. The decode runs only where the probe already said armor, so DER input reaches its
|
|
489
|
+
* parser without it. */
|
|
490
|
+
if (!_isCompleteDer(buf)) return pemDecode(buf, opts.pemLabel, opts.PemError);
|
|
491
|
+
/** @internal A primitive tag makes any content valid, so bytes can satisfy both readings at once
|
|
492
|
+
* and no rule chooses between them: a caller handed the wrong one would be handed the other
|
|
493
|
+
* reading's bytes, silently. The argument is what is ambiguous, not the file, so the message
|
|
494
|
+
* names the two unambiguous ways to say which was meant. */
|
|
495
|
+
if (_isPemBlock(buf, opts)) {
|
|
496
|
+
throw new opts.ErrorClass(opts.prefix + "/bad-input",
|
|
497
|
+
"parse input is both one complete DER value and a well-formed PEM block, so which was meant " +
|
|
498
|
+
"cannot be read from the bytes: pass PEM as a string, or pass the DER value on its own");
|
|
499
|
+
}
|
|
500
|
+
return buf;
|
|
483
501
|
}
|
|
484
502
|
throw new opts.ErrorClass(opts.prefix + "/bad-input", "parse expects a DER BufferSource (Buffer, typed array, DataView, or ArrayBuffer) or a PEM string");
|
|
485
503
|
}
|
|
486
504
|
|
|
505
|
+
/** @internal Whether a buffer also holds a well-formed PEM block, asked only to detect that both
|
|
506
|
+
* readings hold. The decoded bytes are discarded: the caller refuses rather than choosing one. */
|
|
507
|
+
function _isPemBlock(buf, opts) {
|
|
508
|
+
try { pemDecode(buf, opts.pemLabel, opts.PemError); return true; }
|
|
509
|
+
catch (_e) { return false; }
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/** @internal Whether a buffer is exactly one complete DER value. Trailing bytes disqualify it, so a
|
|
513
|
+
* PEM file whose leading text happened to decode cannot be taken for DER on that prefix alone. */
|
|
514
|
+
function _isCompleteDer(buf) {
|
|
515
|
+
// allow:bare-decode classifies bytes AT the door, before a parse context exists to carry caps; the buffer is byte-capped above and the decode holds the codec's own caps.
|
|
516
|
+
try { asn1.decode(buf); return true; }
|
|
517
|
+
catch (_e) { return false; }
|
|
518
|
+
}
|
|
519
|
+
|
|
487
520
|
/** @internal Whether a buffer is armored text rather than DER. Read from a fixed window at the
|
|
488
521
|
* front, the answer is wrong for a file whose explanatory text is longer than the window, and the
|
|
489
522
|
* caller then hands PEM to the DER decoder and reports the DER as malformed. So the whole buffer is
|
|
@@ -493,7 +526,11 @@ function _isPemArmor(buf) {
|
|
|
493
526
|
var n = buf.length;
|
|
494
527
|
for (var i = 0; i < n; i++) {
|
|
495
528
|
var c = buf[i];
|
|
496
|
-
|
|
529
|
+
/** @internal ASCII whitespace has six members and a text file carries all six, so the walk
|
|
530
|
+
* passes tab, line feed, vertical tab, form feed, carriage return and space, plus the printable
|
|
531
|
+
* range and the byte-order mark. Every other byte ends the walk: a boundary behind one means the
|
|
532
|
+
* buffer is not text, and DER is binary. A PEM file that does carry one is passed as a string. */
|
|
533
|
+
var textByte = (c >= 0x20 && c < 0x7f) || (c >= 0x09 && c <= 0x0d) ||
|
|
497
534
|
c === 0xef || c === 0xbb || c === 0xbf;
|
|
498
535
|
if (!textByte) return false;
|
|
499
536
|
if (c === 0x2d && _bufToString(_subarray(buf, i, i + 10), "latin1") === "-----BEGIN") return true;
|
|
@@ -704,6 +741,68 @@ function algorithmIdentifier(ns, opts) {
|
|
|
704
741
|
});
|
|
705
742
|
}
|
|
706
743
|
|
|
744
|
+
/** @internal IssuerAndSerialNumber ::= SEQUENCE { issuer Name, serialNumber CertificateSerialNumber }
|
|
745
|
+
* (RFC 5652 sec. 10.2.4). CMS names the type and RFC 9763 sec. 3.1 reuses it inside a CSR attribute,
|
|
746
|
+
* so the shape is defined once and each caller supplies its own code and name for the refusal. */
|
|
747
|
+
function issuerAndSerialNumber(ns, opts) {
|
|
748
|
+
opts = opts || {};
|
|
749
|
+
return schema.seq([
|
|
750
|
+
schema.field("issuer", name(ns)),
|
|
751
|
+
schema.field("serialNumber", schema.integerLeaf()),
|
|
752
|
+
], {
|
|
753
|
+
assert: "sequence", arity: { exact: 2 },
|
|
754
|
+
code: opts.code || (ns.prefix + "/bad-issuer-and-serial"), what: opts.what || "IssuerAndSerialNumber",
|
|
755
|
+
build: function (m) {
|
|
756
|
+
return {
|
|
757
|
+
issuer: m.fields.issuer.value.result,
|
|
758
|
+
serialNumber: m.fields.serialNumber.value,
|
|
759
|
+
serialNumberHex: _bufToString(m.fields.serialNumber.node.content, "hex"),
|
|
760
|
+
};
|
|
761
|
+
},
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/** @internal The digest a signature algorithm OID names, where it names one. RFC 9763 sec. 4.1 reads
|
|
766
|
+
* this relation directly ("a hash algorithm explicitly indicated by the related certificate's
|
|
767
|
+
* signature OID"), and an algorithm whose OID fixes no digest, such as Ed25519 or ML-DSA, is absent
|
|
768
|
+
* rather than mapped to a default. The value is the registry name, so a caller that needs its own
|
|
769
|
+
* spelling maps from one table instead of carrying a second copy of the relation. */
|
|
770
|
+
var SIG_OID_DIGEST_ROWS = [
|
|
771
|
+
["sha1WithRSAEncryption", "sha1"], ["sha256WithRSAEncryption", "sha256"],
|
|
772
|
+
["sha384WithRSAEncryption", "sha384"], ["sha512WithRSAEncryption", "sha512"],
|
|
773
|
+
["ecdsaWithSHA1", "sha1"], ["ecdsaWithSHA256", "sha256"],
|
|
774
|
+
["ecdsaWithSHA384", "sha384"], ["ecdsaWithSHA512", "sha512"],
|
|
775
|
+
["sha1", "sha1"], ["sha256", "sha256"], ["sha384", "sha384"], ["sha512", "sha512"],
|
|
776
|
+
["sha3-256", "sha3-256"], ["sha3-512", "sha3-512"],
|
|
777
|
+
["id-RSASSA-PSS-SHAKE128", "shake128"], ["id-RSASSA-PSS-SHAKE256", "shake256"],
|
|
778
|
+
];
|
|
779
|
+
/** @internal The digest output length in octets, for the readers that hold a hash value to the length
|
|
780
|
+
* of the algorithm named beside it. A SHAKE is absent: its output length is a parameter of the use,
|
|
781
|
+
* not of the algorithm, so there is no single length to hold a value to. */
|
|
782
|
+
var DIGEST_LENGTH_ROWS = [
|
|
783
|
+
["sha1", 20], ["sha256", 32], ["sha384", 48], ["sha512", 64],
|
|
784
|
+
["sha3-256", 32], ["sha3-512", 64],
|
|
785
|
+
];
|
|
786
|
+
function digestLengths() {
|
|
787
|
+
var out = _create(null);
|
|
788
|
+
_forEach(DIGEST_LENGTH_ROWS, function (r) { out[r[0]] = r[1]; });
|
|
789
|
+
return out;
|
|
790
|
+
}
|
|
791
|
+
var DIGEST_LENGTHS = digestLengths();
|
|
792
|
+
/** @internal Keyed by dotted OID through the registry the caller passes, this module taking its
|
|
793
|
+
* registry as a parameter rather than requiring it. A row whose name the registry does not carry
|
|
794
|
+
* throws rather than being skipped: a skipped row is a digest the readers silently stop deriving,
|
|
795
|
+
* which reads as "this certificate names no hash" on a certificate that names one. */
|
|
796
|
+
function signatureDigestNames(oidRegistry) {
|
|
797
|
+
var out = _create(null);
|
|
798
|
+
_forEach(SIG_OID_DIGEST_ROWS, function (r) {
|
|
799
|
+
var dotted = oidRegistry.byName(r[0]);
|
|
800
|
+
if (!dotted) throw new Error("schema-pkix: the signature-digest table names " + r[0] + ", which the OID registry does not carry");
|
|
801
|
+
out[dotted] = r[1];
|
|
802
|
+
});
|
|
803
|
+
return out;
|
|
804
|
+
}
|
|
805
|
+
|
|
707
806
|
function pbkdf2Params(ns) {
|
|
708
807
|
return schema.seq([
|
|
709
808
|
schema.field("salt", schema.octetString()),
|
|
@@ -1349,7 +1448,12 @@ function crlExtensionDecoders(ns) {
|
|
|
1349
1448
|
var doorScope = _create(null);
|
|
1350
1449
|
doorScope.crl = _assign(_create(null), byScope.crl);
|
|
1351
1450
|
doorScope.entry = _assign(_create(null), byScope.entry);
|
|
1352
|
-
|
|
1451
|
+
/** @internal ITU-T X.509 (2019) clause 9.8 states altSignatureAlgorithm and altSignatureValue as CRL
|
|
1452
|
+
* extensions as well as certificate ones, and clause 7.10.3 gives the CRL the same procedure, so the
|
|
1453
|
+
* two share one decoder with the certificate scope. subjectAltPublicKeyInfo is absent: the NOTE in
|
|
1454
|
+
* clause 7.2.2 says it "is only used as a public-key certificate extension". */
|
|
1455
|
+
_forEach(["authorityKeyIdentifier", "issuerAltName", "authorityInfoAccess", "freshestCRL",
|
|
1456
|
+
"altSignatureAlgorithm", "altSignatureValue"], function (n) {
|
|
1353
1457
|
doorScope.crl[O(n)] = scopedCertDecoder(ns, n);
|
|
1354
1458
|
});
|
|
1355
1459
|
doorScope.entry[O("certificateIssuer")] = _generalNamesDecoder(ns);
|
|
@@ -1670,10 +1774,93 @@ function _acceptExtensionRecord(ext, E, code, key) {
|
|
|
1670
1774
|
record.oid = checked.oid;
|
|
1671
1775
|
record.name = typeof checked.name === "string" ? checked.name : null;
|
|
1672
1776
|
record.critical = checked.critical === true;
|
|
1673
|
-
|
|
1777
|
+
/** @internal Normalized to a byte view before it is copied. The check above admits what
|
|
1778
|
+
* `guard.bytes.isByteSource` admits, which includes a DataView and an ArrayBuffer, and
|
|
1779
|
+
* `guard.bytes.snapshot` takes neither: a value this verb had just called acceptable was then refused
|
|
1780
|
+
* for being the wrong kind of container, so the two messages contradicted each other about one value.
|
|
1781
|
+
* `snapshotSource` views and copies in one call, and the view shares the caller's store rather than
|
|
1782
|
+
* copying it, so the copy is still the only allocation. */
|
|
1783
|
+
record.value = guard.bytes.snapshotSource(checked.value, E, code, "the extension value");
|
|
1674
1784
|
return record;
|
|
1675
1785
|
}
|
|
1676
1786
|
|
|
1787
|
+
/** @internal Whether a parsed certificate's keyUsage permits a named set of purposes, as one definition
|
|
1788
|
+
* three verifiers share. RFC 5280 sec. 4.2.1.3 confines a key to the purposes a present keyUsage asserts,
|
|
1789
|
+
* so a verifier checking a signature under a certificate's key owes this check: a certificate confined to
|
|
1790
|
+
* `keyAgreement` does not authorize a signature. Absent keyUsage is unconfined and permits; a keyUsage that
|
|
1791
|
+
* does not DECODE denies rather than permits, since an extension nothing could read has not been satisfied.
|
|
1792
|
+
* `allowed` is the set each caller's own specification names, which is the only thing that differs between
|
|
1793
|
+
* them: RFC 9483 sec. 3.2 requires `digitalSignature` alone, while a signature over content is also
|
|
1794
|
+
* authorized by `contentCommitment` or `nonRepudiation`. */
|
|
1795
|
+
function keyUsagePermits(ns, allowed) {
|
|
1796
|
+
var kuOid = ns.oid.byName("keyUsage");
|
|
1797
|
+
var decoders = certExtensionDecoders(ns).byOid;
|
|
1798
|
+
return function (parsedCert) {
|
|
1799
|
+
var exts = (parsedCert && parsedCert.extensions) || [];
|
|
1800
|
+
for (var i = 0; i < exts.length; i++) {
|
|
1801
|
+
if (exts[i].oid !== kuOid) continue;
|
|
1802
|
+
var ku;
|
|
1803
|
+
try { ku = decoders[kuOid](exts[i].value); }
|
|
1804
|
+
catch (e) { void e; return false; }
|
|
1805
|
+
for (var a = 0; a < allowed.length; a++) if (ku[allowed[a]] === true) return true;
|
|
1806
|
+
return false;
|
|
1807
|
+
}
|
|
1808
|
+
return true;
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
/** @internal The subject-key algorithms that can only ever SIGN, as one table the linter and the verifier
|
|
1813
|
+
* share. RFC 9883 sec. 6 states "The privateKeyPossessionStatement attribute MUST NOT be used to obtain a
|
|
1814
|
+
* signature certificate", and sec. 4 states the same requirement as a property of the request: "the
|
|
1815
|
+
* subjectPKInfo MUST contain the public key for the key establishment algorithm". The mechanism exists
|
|
1816
|
+
* because a key-establishment key cannot sign and so cannot prove possession the PKCS#10 way. A key that
|
|
1817
|
+
* can only sign has no such problem, so a request to certify one through that attribute asks for the
|
|
1818
|
+
* certificate those sentences forbid, whether or not the request names a key usage. Read off the requested
|
|
1819
|
+
* usages alone, the rule missed every request that omitted the keyUsage extension; read off this table
|
|
1820
|
+
* too, the two doors that apply it reach the same verdict on the same request.
|
|
1821
|
+
*
|
|
1822
|
+
* Only the signature-ONLY families are here. RSA and EC can do either, so an RSA or EC subject key says
|
|
1823
|
+
* nothing on its own and is left to the usages a request asks for. `rsassaPss` IS here and
|
|
1824
|
+
* `rsaEncryption` is NOT, which is the distinction the rule turns on: a bare RSA key can do key
|
|
1825
|
+
* transport, while an id-RSASSA-PSS SPKI is restricted to signing and can no more establish a key than
|
|
1826
|
+
* an EdDSA one. `dsa` and the stateful hash-based algorithms sign by construction. */
|
|
1827
|
+
var SIGNATURE_ONLY_KEY_NAMES = [
|
|
1828
|
+
"Ed25519", "Ed448", "rsassaPss", "dsa",
|
|
1829
|
+
"id-alg-hss-lms-hashsig", "id-alg-xmss-hashsig", "id-alg-xmssmt-hashsig",
|
|
1830
|
+
];
|
|
1831
|
+
/** @internal The families, matched by the prefix the registry files them under rather than listed one at a
|
|
1832
|
+
* time: three ML-DSA sets, twelve SLH-DSA sets and eighteen composite ML-DSA arms, all of which sign and
|
|
1833
|
+
* none of which establishes a key. Taking them by family means a set the registry gains later belongs to
|
|
1834
|
+
* this rule without a second edit, which is what kept RSASSA-PSS out of an earlier hand-written list. */
|
|
1835
|
+
var SIGNATURE_ONLY_KEY_PREFIXES = ["id-ml-dsa-", "id-slh-dsa-", "id-MLDSA"];
|
|
1836
|
+
|
|
1837
|
+
var _signatureOnlyByNs = new _WeakMapI();
|
|
1838
|
+
|
|
1839
|
+
/** @internal Whether a dotted subject-key algorithm OID names a signature-only family. Built once per
|
|
1840
|
+
* OID registry and memoized on it, since the registry is enumerated to pick the families up by prefix. */
|
|
1841
|
+
function signatureOnlyKeyOid(ns) {
|
|
1842
|
+
var table = _weakGet(_signatureOnlyByNs, ns.oid);
|
|
1843
|
+
if (table === undefined) {
|
|
1844
|
+
table = _assign(_create(null), {});
|
|
1845
|
+
_forEach(SIGNATURE_ONLY_KEY_NAMES, function (n) {
|
|
1846
|
+
var dotted = ns.oid.byName(n);
|
|
1847
|
+
if (dotted !== undefined && dotted !== null) table[dotted] = n;
|
|
1848
|
+
});
|
|
1849
|
+
var registry = ns.oid.all();
|
|
1850
|
+
_forEach(intrinsic.keys(registry), function (dotted) {
|
|
1851
|
+
var n = registry[dotted];
|
|
1852
|
+
if (typeof n !== "string") return;
|
|
1853
|
+
_forEach(SIGNATURE_ONLY_KEY_PREFIXES, function (p) {
|
|
1854
|
+
if (_strIndexOf(n, p) === 0) table[dotted] = n;
|
|
1855
|
+
});
|
|
1856
|
+
});
|
|
1857
|
+
_weakSet(_signatureOnlyByNs, ns.oid, table);
|
|
1858
|
+
}
|
|
1859
|
+
return function (dotted) {
|
|
1860
|
+
return typeof dotted === "string" && _hasOwn(table, dotted);
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1677
1864
|
function certExtensionDecoders(ns) {
|
|
1678
1865
|
var GN_SUBTREE = generalName(ns, { decodeValue: true, subtreeBase: true, code: ns.prefix + "/bad-name-constraints" });
|
|
1679
1866
|
var _h = _decodeHelpers(ns), decodeTop = _h.decodeTop, seqChildren = _h.seqChildren, readInt = _h.readInt, O = _h.O;
|
|
@@ -1876,6 +2063,67 @@ function certExtensionDecoders(ns) {
|
|
|
1876
2063
|
return null;
|
|
1877
2064
|
}
|
|
1878
2065
|
|
|
2066
|
+
/** @internal RelatedCertificate ::= SEQUENCE { hashAlgorithm DigestAlgorithmIdentifier,
|
|
2067
|
+
* hashValue OCTET STRING } (RFC 9763 sec. 4.1), the digest of "the entire related certificate".
|
|
2068
|
+
* The value is held to the length of the algorithm it names, since a hash of the wrong length for
|
|
2069
|
+
* its own algorithm identifies no certificate and would otherwise be carried forward as an
|
|
2070
|
+
* identity to compare against. */
|
|
2071
|
+
function relatedCertificate(buf) {
|
|
2072
|
+
var C = ns.prefix + "/bad-extension-value";
|
|
2073
|
+
var kids = seqChildren(buf, C, "RelatedCertificate");
|
|
2074
|
+
if (kids.length !== 2) throw ns.E(C, "RelatedCertificate must be a SEQUENCE { hashAlgorithm, hashValue } (RFC 9763 sec. 4.1)");
|
|
2075
|
+
var alg;
|
|
2076
|
+
try { alg = schema.walk(algorithmIdentifier(ns), kids[0], ns).result; }
|
|
2077
|
+
catch (e) { throw ns.E(C, "RelatedCertificate hashAlgorithm must be a DigestAlgorithmIdentifier (RFC 9763 sec. 4.1)", e); }
|
|
2078
|
+
var value;
|
|
2079
|
+
try { value = _bufferFrom(asn1.read.octetString(kids[1])); }
|
|
2080
|
+
catch (e) { throw ns.E(C, "RelatedCertificate hashValue must be an OCTET STRING (RFC 9763 sec. 4.1)", e); }
|
|
2081
|
+
if (value.length === 0) throw ns.E(C, "RelatedCertificate hashValue must not be empty (RFC 9763 sec. 4.1)");
|
|
2082
|
+
var want = DIGEST_LENGTHS[alg.name];
|
|
2083
|
+
if (want !== undefined && value.length !== want) {
|
|
2084
|
+
throw ns.E(C, "RelatedCertificate hashValue is " + value.length + " octets, and " + alg.name + " produces " + want);
|
|
2085
|
+
}
|
|
2086
|
+
return { hashAlgorithm: alg.name, hashAlgorithmOid: alg.oid, hashValue: value };
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
/** @internal SubjectAltPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier,
|
|
2090
|
+
* subjectAltPublicKey BIT STRING } (ITU-T X.509 (2019) clause 9.8.2). Those are the two components
|
|
2091
|
+
* SubjectPublicKeyInfo has, in that order, and a field name is not encoded, so the two types share
|
|
2092
|
+
* one encoding and this reads through the shared SPKI sub-schema. The whole value is surfaced as
|
|
2093
|
+
* `bytes` because it IS an SPKI on the wire: an importer takes it unchanged. */
|
|
2094
|
+
function subjectAltPublicKeyInfo(buf) {
|
|
2095
|
+
var C = ns.prefix + "/bad-extension-value";
|
|
2096
|
+
var n = decodeTop(buf, C, "SubjectAltPublicKeyInfo");
|
|
2097
|
+
var v;
|
|
2098
|
+
try { v = schema.walk(spki(ns), n, ns).result; }
|
|
2099
|
+
catch (e) { throw ns.E(C, "SubjectAltPublicKeyInfo must be a SEQUENCE { algorithm, subjectAltPublicKey BIT STRING } (ITU-T X.509 (2019) clause 9.8.2)", e); }
|
|
2100
|
+
if (v.publicKey.unusedBits !== 0) throw ns.E(C, "the subjectAltPublicKey BIT STRING must hold whole octets");
|
|
2101
|
+
if (v.publicKey.bytes.length === 0) throw ns.E(C, "the subjectAltPublicKey must not be empty");
|
|
2102
|
+
return { algorithm: v.algorithm, subjectAltPublicKey: v.publicKey.bytes, bytes: v.bytes };
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/** @internal AltSignatureAlgorithm ::= AlgorithmIdentifier (clause 9.8.3). The extension value is the
|
|
2106
|
+
* AlgorithmIdentifier itself, not a SEQUENCE wrapping one. */
|
|
2107
|
+
function altSignatureAlgorithm(buf) {
|
|
2108
|
+
var C = ns.prefix + "/bad-extension-value";
|
|
2109
|
+
var n = decodeTop(buf, C, "AltSignatureAlgorithm");
|
|
2110
|
+
try { return schema.walk(algorithmIdentifier(ns), n, ns).result; }
|
|
2111
|
+
catch (e) { throw ns.E(C, "AltSignatureAlgorithm must be an AlgorithmIdentifier (ITU-T X.509 (2019) clause 9.8.3)", e); }
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
/** @internal AltSignatureValue ::= BIT STRING (clause 9.8.4). Held to whole octets, a signature being
|
|
2115
|
+
* an octet string of bits and every algorithm here producing one. */
|
|
2116
|
+
function altSignatureValue(buf) {
|
|
2117
|
+
var C = ns.prefix + "/bad-extension-value";
|
|
2118
|
+
var n = decodeTop(buf, C, "AltSignatureValue");
|
|
2119
|
+
var bs;
|
|
2120
|
+
try { bs = asn1.read.bitString(n); }
|
|
2121
|
+
catch (e) { throw ns.E(C, "AltSignatureValue must be a BIT STRING (ITU-T X.509 (2019) clause 9.8.4)", e); }
|
|
2122
|
+
if (bs.unusedBits !== 0) throw ns.E(C, "the AltSignatureValue BIT STRING must hold whole octets");
|
|
2123
|
+
if (bs.bytes.length === 0) throw ns.E(C, "the AltSignatureValue must not be empty");
|
|
2124
|
+
return { unusedBits: bs.unusedBits, bytes: _bufferFrom(bs.bytes) };
|
|
2125
|
+
}
|
|
2126
|
+
|
|
1879
2127
|
function extKeyUsage(buf) {
|
|
1880
2128
|
var C = ns.prefix + "/bad-extension-value";
|
|
1881
2129
|
var kids = seqChildren(buf, C, "ExtKeyUsage");
|
|
@@ -2233,6 +2481,10 @@ function certExtensionDecoders(ns) {
|
|
|
2233
2481
|
byOid[O("subjectInfoAccess")] = subjectInfoAccess;
|
|
2234
2482
|
byOid[O("subjectDirectoryAttributes")] = subjectDirectoryAttributes;
|
|
2235
2483
|
byOid[O("ocspNoCheck")] = ocspNoCheck;
|
|
2484
|
+
byOid[O("relatedCertificate")] = relatedCertificate;
|
|
2485
|
+
byOid[O("subjectAltPublicKeyInfo")] = subjectAltPublicKeyInfo;
|
|
2486
|
+
byOid[O("altSignatureAlgorithm")] = altSignatureAlgorithm;
|
|
2487
|
+
byOid[O("altSignatureValue")] = altSignatureValue;
|
|
2236
2488
|
byOid[O("msCertificateTemplate")] = msCertificateTemplate;
|
|
2237
2489
|
byOid[O("msEnrollCertType")] = msEnrollCertType;
|
|
2238
2490
|
byOid[O("msCaVersion")] = msCaVersion;
|
|
@@ -2340,6 +2592,12 @@ function signedEnvelope(ns, tbsSchema, opts) {
|
|
|
2340
2592
|
schema.field("signatureAlgorithm", algorithmIdentifier(ns)),
|
|
2341
2593
|
schema.field("signatureValue", schema.bitString()),
|
|
2342
2594
|
], {
|
|
2595
|
+
/** @internal Exactly three, with no trailing element. X.509 (2019) extended the SIGNED
|
|
2596
|
+
* parameterized type with optional `altAlgorithmIdentifier` and `altSignature` components after
|
|
2597
|
+
* the signature, and then forbade them for every structure it profiles: "only one digital
|
|
2598
|
+
* signature shall be generated, i.e., the altAlgorithmIdentifier and the altSignature components
|
|
2599
|
+
* shall be absent" (clauses 7.2.1, 7.10.2, 11.3 and 14.2). Declaring either as an optional field
|
|
2600
|
+
* here would admit a second signature the text excludes, in every format that composes this. */
|
|
2343
2601
|
assert: "sequence", arity: { exact: 3 }, code: opts.code, what: opts.what,
|
|
2344
2602
|
build: function (m, ctx) {
|
|
2345
2603
|
var tbsMatch = m.fields.toBeSigned.value;
|
|
@@ -2415,6 +2673,9 @@ module.exports = {
|
|
|
2415
2673
|
DN_SHORT: DN_SHORT,
|
|
2416
2674
|
time: time,
|
|
2417
2675
|
algorithmIdentifier: algorithmIdentifier,
|
|
2676
|
+
issuerAndSerialNumber: issuerAndSerialNumber,
|
|
2677
|
+
digestLengths: digestLengths,
|
|
2678
|
+
signatureDigestNames: signatureDigestNames,
|
|
2418
2679
|
pbkdf2Params: pbkdf2Params,
|
|
2419
2680
|
pbmac1Params: pbmac1Params,
|
|
2420
2681
|
spki: spki,
|
|
@@ -2447,6 +2708,8 @@ module.exports = {
|
|
|
2447
2708
|
crlFixedCriticality: crlFixedCriticality,
|
|
2448
2709
|
crlEntryFixedCriticality: crlEntryFixedCriticality,
|
|
2449
2710
|
certExtensionDecoders: certExtensionDecoders,
|
|
2711
|
+
keyUsagePermits: keyUsagePermits,
|
|
2712
|
+
signatureOnlyKeyOid: signatureOnlyKeyOid,
|
|
2450
2713
|
scopedCertDecoder: scopedCertDecoder,
|
|
2451
2714
|
crlExtensionDecoders: crlExtensionDecoders,
|
|
2452
2715
|
decodeExtensionList: decodeExtensionList,
|
package/lib/schema-tsp.js
CHANGED
|
@@ -42,6 +42,7 @@ var oid = require("./oid");
|
|
|
42
42
|
var smime = require("./schema-smime");
|
|
43
43
|
var cms = require("./schema-cms");
|
|
44
44
|
var frameworkError = require("./framework-error");
|
|
45
|
+
var guard = require("./guard-all");
|
|
45
46
|
|
|
46
47
|
var TspError = frameworkError.TspError;
|
|
47
48
|
var PemError = frameworkError.PemError;
|
|
@@ -177,11 +178,11 @@ var PKI_STATUS_INFO = schema.seq([
|
|
|
177
178
|
_assertMinimalNamedBits(bs.unusedBits, bs.bytes);
|
|
178
179
|
failInfo = { unusedBits: bs.unusedBits, bytes: bs.bytes, bits: _namedBits(bs.bytes) };
|
|
179
180
|
}
|
|
180
|
-
return {
|
|
181
|
+
return guard.verdict.of({
|
|
181
182
|
status: Number(status),
|
|
182
183
|
statusString: m.fields.statusString.present ? m.fields.statusString.value.items.map(function (it) { return it.value; }) : null,
|
|
183
184
|
failInfo: failInfo,
|
|
184
|
-
};
|
|
185
|
+
});
|
|
185
186
|
},
|
|
186
187
|
});
|
|
187
188
|
|
|
@@ -213,14 +214,14 @@ var TIME_STAMP_RESP = schema.seq([
|
|
|
213
214
|
if (granted && !present) throw NS.E("tsp/missing-token", "a granted TimeStampResp must carry a timeStampToken (RFC 3161 sec. 2.4.2)");
|
|
214
215
|
if (!granted && present) throw NS.E("tsp/unexpected-token", "a non-granted TimeStampResp must not carry a timeStampToken (RFC 3161 sec. 2.4.2)");
|
|
215
216
|
if (status.status !== 2 && status.failInfo) throw NS.E("tsp/unexpected-failinfo", "failInfo is present only when the status is rejection(2) (RFC 3161 sec. 2.4.2)");
|
|
216
|
-
return {
|
|
217
|
+
return guard.verdict.of({
|
|
217
218
|
status: status.status,
|
|
218
219
|
statusString: status.statusString,
|
|
219
220
|
failInfo: status.failInfo,
|
|
220
221
|
/** @internal The token is a second parse of bytes carried inside this one, so it takes the
|
|
221
222
|
* caps this door was given rather than the built-in ceilings. */
|
|
222
223
|
timeStampToken: present ? parseToken(m.fields.timeStampToken.value.bytes, ctx ? ctx.caps : undefined) : null,
|
|
223
|
-
};
|
|
224
|
+
});
|
|
224
225
|
},
|
|
225
226
|
});
|
|
226
227
|
|
|
@@ -552,7 +553,7 @@ function signerCertificateDer(token) {
|
|
|
552
553
|
* one key. Two that name two different certificates leave which of them signed the token
|
|
553
554
|
* unsettled, so this answers with nothing and the caller says so, rather than taking one of
|
|
554
555
|
* them. */
|
|
555
|
-
if (found !== null) return
|
|
556
|
+
if (found !== null) return guard.bytes.compare(found, named) === 0 ? found : null;
|
|
556
557
|
found = named;
|
|
557
558
|
}
|
|
558
559
|
return found;
|
|
@@ -576,7 +577,7 @@ function _essNamedCertificate(token, attr) {
|
|
|
576
577
|
var element = certs[j];
|
|
577
578
|
if (!element || element.tagClass !== "universal" || !Buffer.isBuffer(element.bytes)) continue;
|
|
578
579
|
var actual = digest().update(element.bytes).digest();
|
|
579
|
-
if (actual.length === want.length &&
|
|
580
|
+
if (actual.length === want.length && guard.bytes.compare(actual, want) === 0) return element.bytes;
|
|
580
581
|
}
|
|
581
582
|
return null;
|
|
582
583
|
}
|
package/lib/sign-scheme.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
var asn1 = require("./asn1-der");
|
|
7
7
|
var oid = require("./oid");
|
|
8
|
-
var nodeCrypto = require("crypto");
|
|
9
8
|
var pkcs8 = require("./schema-pkcs8");
|
|
10
9
|
var pkix = require("./schema-pkix");
|
|
11
10
|
var frameworkError = require("./framework-error");
|
|
@@ -14,6 +13,13 @@ var subtle = webcrypto.webcrypto.subtle;
|
|
|
14
13
|
var validator = require("./validator-all");
|
|
15
14
|
var guard = require("./guard-all");
|
|
16
15
|
var intrinsic = require("./guard-intrinsic");
|
|
16
|
+
/** @internal The signing and key-import operations, captured at load. The objects were held and their
|
|
17
|
+
* METHODS fetched per call, which leaves them replaceable: a `sign` returning bytes of its own choosing
|
|
18
|
+
* decides the signature that goes out on a certificate, a CRL or a request, and an `importKey` or an
|
|
19
|
+
* `adoptKey` returning a different private key makes the captured signer sign with that key instead. */
|
|
20
|
+
var _subtleSign = intrinsic.uncurry(subtle.sign);
|
|
21
|
+
var _subtleImportKey = intrinsic.uncurry(subtle.importKey);
|
|
22
|
+
var _adoptKey = webcrypto.adoptKey;
|
|
17
23
|
var compositeSig = require("./composite-sig");
|
|
18
24
|
var b = asn1.build;
|
|
19
25
|
function O(name) { return oid.byName(name); }
|
|
@@ -28,6 +34,10 @@ var _isBuffer = intrinsic.isBuffer;
|
|
|
28
34
|
var _bufferFrom = intrinsic.bufferFrom;
|
|
29
35
|
var _isU8 = intrinsic.types.isUint8Array;
|
|
30
36
|
var _resolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
37
|
+
/** @internal The promise constructor, captured at load, for the reason `guard-intrinsic` gives: the
|
|
38
|
+
* resolve builds its promise from the receiver, and the bytes a signature is made of travel through
|
|
39
|
+
* these promises. */
|
|
40
|
+
var _Promise = intrinsic.Promise;
|
|
31
41
|
|
|
32
42
|
var HASH = intrinsic.assign(intrinsic.create(null), { sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" });
|
|
33
43
|
var NODE_DIGEST = intrinsic.assign(intrinsic.create(null), { sha256: "sha256", sha384: "sha384", sha512: "sha512", shake128: "shake128", shake256: "shake256" });
|
|
@@ -211,7 +221,7 @@ function _importKey(key, imp, E) {
|
|
|
211
221
|
throw E("bad-input", "the signing key is a WebCrypto CryptoKey of type " + _stringify(key.type) + "; a signing key must be a private key (type \"private\")");
|
|
212
222
|
}
|
|
213
223
|
_assertKeyMatchesScheme(key, imp, E);
|
|
214
|
-
return
|
|
224
|
+
return _adoptKey(key, imp, ["sign"], E, "bad-input");
|
|
215
225
|
}
|
|
216
226
|
var der, owned = false;
|
|
217
227
|
if (_isBuffer(key)) der = guard.bytes.view(key, E, "bad-input", "the signer private key");
|
|
@@ -224,7 +234,7 @@ function _importKey(key, imp, E) {
|
|
|
224
234
|
throw E("bad-input", "a signer key must be a WebCrypto CryptoKey, a node:crypto KeyObject, a PKCS#8 DER Buffer, " +
|
|
225
235
|
"a PKCS#8 PEM string, or a signer { algorithm, publicKey, sign }");
|
|
226
236
|
}
|
|
227
|
-
var imported = subtle
|
|
237
|
+
var imported = _subtleImportKey(subtle, "pkcs8", der, imp, false, ["sign"]);
|
|
228
238
|
if (!owned) return imported;
|
|
229
239
|
return imported.then(function (k) {
|
|
230
240
|
guard.secret.zeroize(der, E, "bad-input", "the signer private-key copy");
|
|
@@ -315,11 +325,14 @@ function _signExternally(signer, scheme, signedBytes, E) {
|
|
|
315
325
|
* the rewritten artifact. `Buffer.from` is not enough: a small copy comes out of Node's shared
|
|
316
326
|
* pool, whose backing ArrayBuffer the callback could reach the original through. */
|
|
317
327
|
var handed = new intrinsic.Uint8Array(signedBytes.length);
|
|
318
|
-
|
|
328
|
+
/** @internal The copy goes through the captured `set`. Read off the instance it dispatches through
|
|
329
|
+
* `Uint8Array.prototype`, and a replacement there writes different bytes into the allocation the
|
|
330
|
+
* callback is handed, so the signer signs a message this never assembled. */
|
|
331
|
+
intrinsic.typedArraySet(handed, signedBytes);
|
|
319
332
|
var produced;
|
|
320
333
|
try { produced = intrinsic.apply(signer.sign, signer.receiver, [handed]); }
|
|
321
334
|
catch (e) { throw E("bad-input", "the signer sign() callback failed", e); }
|
|
322
|
-
return _resolve(
|
|
335
|
+
return _resolve(_Promise, produced).then(function (raw) {
|
|
323
336
|
var sig = guard.bytes.snapshotSource(raw, E, "bad-input", "the signature the signer returned");
|
|
324
337
|
return _assertSignerSignature(sig, scheme, E);
|
|
325
338
|
}, function (e) {
|
|
@@ -369,7 +382,7 @@ function _nodeSignOptions(key, scheme) {
|
|
|
369
382
|
var name = scheme.sign.name;
|
|
370
383
|
if (name === "ECDSA") return { key: key, dsaEncoding: "ieee-p1363" };
|
|
371
384
|
if (name === "RSA-PSS") {
|
|
372
|
-
return { key: key, padding:
|
|
385
|
+
return { key: key, padding: guard.crypto.padding.RSA_PKCS1_PSS, saltLength: scheme.sign.saltLength };
|
|
373
386
|
}
|
|
374
387
|
return key;
|
|
375
388
|
}
|
|
@@ -393,7 +406,7 @@ function _signWithKeyObject(key, scheme, signedBytes, E) {
|
|
|
393
406
|
}
|
|
394
407
|
_assertKeyObjectMatchesScheme(key, scheme, E);
|
|
395
408
|
try {
|
|
396
|
-
return _bufferFrom(
|
|
409
|
+
return _bufferFrom(guard.crypto.sign(_nodeDigest(scheme), signedBytes, _nodeSignOptions(key, scheme)));
|
|
397
410
|
} catch (e) {
|
|
398
411
|
throw E("bad-input", "the signer KeyObject could not produce a " + scheme.imp.name + " signature", e);
|
|
399
412
|
}
|
|
@@ -402,7 +415,7 @@ function _signWithKeyObject(key, scheme, signedBytes, E) {
|
|
|
402
415
|
function signOverTbs(scheme, key, signedBytes, E) {
|
|
403
416
|
if (intrinsic.types.isKeyObject(key)) {
|
|
404
417
|
var koSig = _signWithKeyObject(key, scheme, signedBytes, E);
|
|
405
|
-
return _resolve(
|
|
418
|
+
return _resolve(_Promise, scheme.ecdsaDer ? validator.sig.rawToEcdsaDer(koSig, scheme.coordLen) : koSig);
|
|
406
419
|
}
|
|
407
420
|
var signer = _snapshotSigner(key);
|
|
408
421
|
if (signer) {
|
|
@@ -414,7 +427,7 @@ function signOverTbs(scheme, key, signedBytes, E) {
|
|
|
414
427
|
return compositeSig.compositeSign(scheme.composite, _normCompositeKeys(key, scheme.composite, E), signedBytes).then(function (sig) { return _bufferFrom(sig); });
|
|
415
428
|
}
|
|
416
429
|
return _importKey(key, scheme.imp, E).then(function (priv) {
|
|
417
|
-
return subtle
|
|
430
|
+
return _subtleSign(subtle, scheme.sign, priv, signedBytes).then(function (sigRaw) {
|
|
418
431
|
var sig = _bufferFrom(sigRaw);
|
|
419
432
|
if (scheme.ecdsaDer) sig = validator.sig.rawToEcdsaDer(sig, scheme.coordLen);
|
|
420
433
|
return sig;
|