@blamejs/pki 0.8.0 → 0.8.2
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 +50 -0
- package/README.md +9 -9
- package/index.js +3 -1
- package/lib/asn1-der.js +1 -1
- package/lib/cbor-det.js +1 -1
- package/lib/cmc-build.js +1 -2
- package/lib/cms-decrypt.js +3 -1
- package/lib/cms-digest.js +191 -0
- package/lib/cms-verify.js +3 -2
- package/lib/guard-der.js +26 -1
- package/lib/guard-name.js +124 -1
- package/lib/guard-text.js +1 -1
- package/lib/oid.js +18 -6
- package/lib/path-validate.js +9 -4
- package/lib/pki-build.js +63 -6
- package/lib/schema-cms.js +28 -1
- package/lib/schema-pkix.js +5 -4
- package/lib/x509-sign.js +100 -1
- package/package.json +4 -2
- package/sbom.cdx.json +6 -6
package/lib/schema-pkix.js
CHANGED
|
@@ -280,10 +280,10 @@ function pbkdf2Params(ns) {
|
|
|
280
280
|
var keyLength = guard.range.positiveInt31(m.fields.keyLength.value, ctx.E, ctx.prefix + "/bad-mac-data", "PBKDF2 keyLength");
|
|
281
281
|
var prf = m.fields.prf.present ? m.fields.prf.value.result : null;
|
|
282
282
|
var pp = prf ? prf.parameters : null;
|
|
283
|
-
if (prf && prf.oid === hmacSha1 && pp !== null &&
|
|
283
|
+
if (prf && prf.oid === hmacSha1 && pp !== null && guard.der.isNull(pp)) {
|
|
284
284
|
throw ctx.E(ctx.prefix + "/bad-mac-data", "a PBKDF2 prf equal to its DEFAULT algid-hmacWithSHA1 must be omitted (X.690 sec. 11.5, RFC 8018 sec. 5.2)");
|
|
285
285
|
}
|
|
286
|
-
if (prf && pp !== null && !(pp
|
|
286
|
+
if (prf && pp !== null && !guard.der.isNull(pp)) {
|
|
287
287
|
throw ctx.E(ctx.prefix + "/bad-mac-data", "the PBKDF2 prf parameters must be absent or NULL (RFC 8018 App. B.1)");
|
|
288
288
|
}
|
|
289
289
|
return {
|
|
@@ -313,7 +313,7 @@ function pbmac1Params(ns) {
|
|
|
313
313
|
}
|
|
314
314
|
var scheme = m.fields.messageAuthScheme.value.result;
|
|
315
315
|
var sp = scheme.parameters;
|
|
316
|
-
if (sp !== null && !(sp
|
|
316
|
+
if (sp !== null && !guard.der.isNull(sp)) {
|
|
317
317
|
throw ctx.E(ctx.prefix + "/bad-mac-data", "the PBMAC1 messageAuthScheme parameters must be absent or NULL (RFC 8018 App. B.1)");
|
|
318
318
|
}
|
|
319
319
|
return {
|
|
@@ -403,7 +403,8 @@ function name(ns, opts) {
|
|
|
403
403
|
_forEach(rdnItem.value.items, function (atvItem) {
|
|
404
404
|
var a = atvItem.value.result;
|
|
405
405
|
_push(atvs, a);
|
|
406
|
-
var label = (a.name && _hasOwn(DN_SHORT, a.name) && DN_SHORT[a.name]) ||
|
|
406
|
+
var label = (a.name && _hasOwn(DN_SHORT, a.name) && DN_SHORT[a.name]) ||
|
|
407
|
+
(guard.name.isAttributeDescriptor(a.name) ? a.name : a.type);
|
|
407
408
|
_push(atvParts, label + "=" + _dnDisplayValue(a.value));
|
|
408
409
|
});
|
|
409
410
|
_push(rdns, atvs);
|
package/lib/x509-sign.js
CHANGED
|
@@ -23,6 +23,9 @@ var signScheme = require("./sign-scheme");
|
|
|
23
23
|
var guard = require("./guard-all");
|
|
24
24
|
var intrinsic = require("./guard-intrinsic");
|
|
25
25
|
var _isArray = intrinsic.isArray, _bufferEquals = intrinsic.bufferEquals;
|
|
26
|
+
var _dnCharCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
27
|
+
var _dnKeys = intrinsic.keys;
|
|
28
|
+
var _dnHasOwn = intrinsic.hasOwn;
|
|
26
29
|
var frameworkError = require("./framework-error");
|
|
27
30
|
var schema = require("./schema-engine");
|
|
28
31
|
var pkix = require("./schema-pkix");
|
|
@@ -788,4 +791,100 @@ function _extension(name, value, opts) {
|
|
|
788
791
|
return _ext(row.oid, critical, valueDer);
|
|
789
792
|
}
|
|
790
793
|
|
|
791
|
-
|
|
794
|
+
/**
|
|
795
|
+
* @primitive pki.x509.parseDn
|
|
796
|
+
* @signature pki.x509.parseDn(dn) -> { rdns, dn, bytes }
|
|
797
|
+
* @since 0.8.2
|
|
798
|
+
* @status stable
|
|
799
|
+
* @spec RFC 4514
|
|
800
|
+
* @related pki.x509.sign, pki.schema.x509.parse
|
|
801
|
+
*
|
|
802
|
+
* Read a distinguished-name string into the `Name` a builder takes, using the attribute names
|
|
803
|
+
* and value escaping of RFC 4514 sec. 2.4 and sec. 3. It is the inverse of the `dn` string
|
|
804
|
+
* every parsed name carries: `bytes` is the encoded `Name`, ready to hand to `pki.x509.sign`
|
|
805
|
+
* as a `subject` or `issuer`, and `dn` is the string re-emitted from what was read, which for
|
|
806
|
+
* a string this toolkit produced is the string it was given.
|
|
807
|
+
*
|
|
808
|
+
* Components are read in the order they are written, so the first one written is the first
|
|
809
|
+
* element of the encoded `Name`. That is the order every `dn` here is emitted in, and the
|
|
810
|
+
* order `openssl x509 -subject` prints. RFC 4514 sec. 2.1 specifies the opposite order, so a
|
|
811
|
+
* string from a directory tool that follows it names the components from last to first;
|
|
812
|
+
* reverse them before parsing one.
|
|
813
|
+
*
|
|
814
|
+
* The string form does not carry the ASN.1 string type of each value, so a parsed value takes
|
|
815
|
+
* the type this toolkit encodes that attribute with: `PrintableString` for `countryName`,
|
|
816
|
+
* `IA5String` for `emailAddress`, `UTF8String` otherwise. A name written by something that
|
|
817
|
+
* chose different types will re-encode to different bytes, which is why RFC 4514 sec. 2.4
|
|
818
|
+
* defines the `#`-prefixed hex form: a value given that way is the exact DER of the
|
|
819
|
+
* `AttributeValue` and round-trips byte for byte.
|
|
820
|
+
*
|
|
821
|
+
* Malformed input throws `x509/bad-dn`. A non-string throws `x509/bad-input`.
|
|
822
|
+
*
|
|
823
|
+
* `bytes` is raw `Name` DER, the form `spec.subject` and `spec.issuer` take alongside a string
|
|
824
|
+
* or an array of RDNs, so a name read from a string can be signed without rebuilding it.
|
|
825
|
+
*
|
|
826
|
+
* @example
|
|
827
|
+
* var name = pki.x509.parseDn("C=US, O=Example Inc, CN=leaf");
|
|
828
|
+
* name.rdns.length; // -> 3
|
|
829
|
+
* name.rdns[0][0].name; // -> "countryName"
|
|
830
|
+
* name.dn; // -> "C=US, O=Example Inc, CN=leaf"
|
|
831
|
+
* Buffer.isBuffer(name.bytes); // -> true
|
|
832
|
+
*/
|
|
833
|
+
function parseDn(dn) {
|
|
834
|
+
if (typeof dn !== "string") throw _err("x509/bad-input", "pki.x509.parseDn takes a distinguished-name string");
|
|
835
|
+
var components = guard.name.parseDnString(dn, _err, "x509/bad-dn", "the distinguished name");
|
|
836
|
+
var spec = [];
|
|
837
|
+
for (var i = 0; i < components.length; i++) {
|
|
838
|
+
var rdn = components[i], out = intrinsic.create(null);
|
|
839
|
+
for (var j = 0; j < rdn.length; j++) {
|
|
840
|
+
var atv = rdn[j];
|
|
841
|
+
var attrName;
|
|
842
|
+
try { attrName = _dnAttrName(atv.type); }
|
|
843
|
+
catch (e) {
|
|
844
|
+
if (e instanceof CertificateError && e.code === "x509/bad-dn") throw e;
|
|
845
|
+
throw _err("x509/bad-dn", "the distinguished name has an attribute type that does not resolve: " + guard.text.showValue(atv.type), e);
|
|
846
|
+
}
|
|
847
|
+
if (_dnHasOwn(out, attrName)) {
|
|
848
|
+
throw _err("x509/bad-dn", "the distinguished name repeats " + attrName + " inside one relative distinguished name");
|
|
849
|
+
}
|
|
850
|
+
out[attrName] = atv.hex === null ? _dnLiteralValue(atv.value) : "#" + atv.hex;
|
|
851
|
+
}
|
|
852
|
+
guard.list.append(spec, out);
|
|
853
|
+
}
|
|
854
|
+
var bytes, read;
|
|
855
|
+
try {
|
|
856
|
+
bytes = _b.encodeName(spec);
|
|
857
|
+
read = schema.walk(NAME_SCHEMA, asn1.decode(bytes), NS).result;
|
|
858
|
+
} catch (e) {
|
|
859
|
+
throw _err("x509/bad-dn", "the distinguished name does not encode: " + (e && e.message), e);
|
|
860
|
+
}
|
|
861
|
+
return { rdns: read.rdns, dn: read.dn, bytes: bytes };
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function _dnAttrName(type) {
|
|
865
|
+
if (oid.isDottedDecimal(type)) {
|
|
866
|
+
var byOid = oid.name(type);
|
|
867
|
+
if (!byOid) throw _err("x509/bad-dn", "the distinguished name names an unregistered attribute OID " + type);
|
|
868
|
+
return byOid;
|
|
869
|
+
}
|
|
870
|
+
var wanted = guard.name.lowerAscii(type);
|
|
871
|
+
var shortNames = pkix.DN_SHORT;
|
|
872
|
+
var keys = _dnKeys(shortNames);
|
|
873
|
+
for (var i = 0; i < keys.length; i++) {
|
|
874
|
+
if (guard.name.lowerAscii(shortNames[keys[i]]) === wanted || guard.name.lowerAscii(keys[i]) === wanted) return keys[i];
|
|
875
|
+
}
|
|
876
|
+
var table = oid.all();
|
|
877
|
+
var dotted = _dnKeys(table);
|
|
878
|
+
for (var j = 0; j < dotted.length; j++) {
|
|
879
|
+
var registered = table[dotted[j]];
|
|
880
|
+
if (guard.name.lowerAscii(registered) === wanted) return registered;
|
|
881
|
+
}
|
|
882
|
+
throw _err("x509/bad-dn", "the distinguished name names an unknown attribute " + guard.text.showValue(type));
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function _dnLiteralValue(value) {
|
|
886
|
+
var first = _dnCharCodeAt(value, 0);
|
|
887
|
+
return (first === 0x23 || first === 0x5c) ? "\\" + value : value;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
module.exports = { sign: sign, randomSerial: randomSerial, extension: extension, parseDn: parseDn };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/pki",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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",
|
|
@@ -68,7 +68,9 @@
|
|
|
68
68
|
"test": "node test/smoke.js",
|
|
69
69
|
"lint": "eslint --max-warnings 0 .",
|
|
70
70
|
"fuzz": "npm ci --prefix fuzz && npx --prefix fuzz jazzer fuzz/asn1-der.fuzz.js -- -max_total_time=60",
|
|
71
|
-
"gates": "node test/layer-0-primitives/codebase-patterns.test.js && node scripts/validate-source-comment-blocks.js && node scripts/check-api-snapshot.js && node scripts/check-spelling-consistency.js && node scripts/check-engine-floor.js",
|
|
71
|
+
"gates": "node test/layer-0-primitives/codebase-patterns.test.js && node scripts/validate-source-comment-blocks.js && node scripts/check-api-snapshot.js && node scripts/check-spelling-consistency.js && node scripts/check-engine-floor.js && node scripts/check-cve-vectors.js && node scripts/check-rfc-currency.js",
|
|
72
|
+
"check:cve-vectors": "node scripts/check-cve-vectors.js",
|
|
73
|
+
"check:rfc-currency": "node scripts/check-rfc-currency.js",
|
|
72
74
|
"check:engine-floor": "node scripts/check-engine-floor.js",
|
|
73
75
|
"check:spelling": "node scripts/check-spelling-consistency.js",
|
|
74
76
|
"coverage": "c8 --include=lib/** --include=index.js --reporter=text-summary --reporter=lcov node test/smoke.js",
|
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:b0bab233-a055-413d-8f5b-3ed8a3f7a8c4",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-20T02:50:18.934Z",
|
|
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.2",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.2",
|
|
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.2",
|
|
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.2",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|