@blamejs/pki 0.2.29 → 0.2.31
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 +25 -0
- package/README.md +2 -1
- package/lib/acme.js +3 -0
- package/lib/cms-encrypt.js +1 -1
- package/lib/cms-sign.js +2 -3
- package/lib/ct.js +7 -10
- package/lib/framework-error.js +9 -0
- package/lib/guard-all.js +4 -0
- package/lib/guard-limits.js +26 -1
- package/lib/guard-time.js +68 -0
- package/lib/inspect.js +18 -0
- package/lib/lint.js +11 -1
- package/lib/oid.js +14 -1
- package/lib/path-validate.js +17 -6
- package/lib/schema-all.js +4 -0
- package/lib/schema-c509.js +479 -0
- package/lib/schema-pkix.js +117 -1
- package/lib/sigstore.js +12 -1
- package/lib/smime.js +1 -1
- package/lib/tsp-sign.js +1 -3
- package/lib/validator-all.js +3 -2
- package/lib/validator-sig.js +14 -40
- package/lib/webcrypto.js +17 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/smime.js
CHANGED
|
@@ -168,7 +168,7 @@ async function sign(content, signers, opts) {
|
|
|
168
168
|
// Coverage residual: the throw only fires on a >16 MiB assembled message -- a real cap, exercised by no
|
|
169
169
|
// unit vector (driving it would sign 16 MiB).
|
|
170
170
|
function _capped(msg) {
|
|
171
|
-
|
|
171
|
+
guard.limits.byteCap(msg, C.LIMITS.MIME_MAX_BYTES, _err, "smime/too-large", "the assembled S/MIME message");
|
|
172
172
|
return msg;
|
|
173
173
|
}
|
|
174
174
|
|
package/lib/tsp-sign.js
CHANGED
|
@@ -119,9 +119,7 @@ function sign(messageImprint, tsa, opts) {
|
|
|
119
119
|
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(Buffer.from(mi.hashedMessage))]);
|
|
120
120
|
// genTime defaults to now; a supplied value MUST be a valid Date (never a silently-ignored
|
|
121
121
|
// non-Date or an Invalid Date that would encode a garbage GeneralizedTime).
|
|
122
|
-
if (opts.genTime != null
|
|
123
|
-
throw _err("tsp/bad-input", "genTime must be a valid Date");
|
|
124
|
-
}
|
|
122
|
+
if (opts.genTime != null) guard.time.assertValid(opts.genTime, _err, "tsp/bad-input", "genTime");
|
|
125
123
|
var genTime = opts.genTime instanceof Date ? opts.genTime : new Date();
|
|
126
124
|
var fields = [b.integer(1n), _policy(opts.policy), imprint, b.integer(BigInt(opts.serialNumber)), b.generalizedTime(genTime)];
|
|
127
125
|
if (opts.accuracy) fields.push(_accuracy(opts.accuracy));
|
package/lib/validator-all.js
CHANGED
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
// -- the COMPLETE WebAuthn credential COSE_Key rule
|
|
16
16
|
// set (RFC 9052/9053 + WebAuthn sec. 6.5.1 +
|
|
17
17
|
// CTAP2 canonical + on-curve), one home
|
|
18
|
-
// validator.sig.
|
|
19
|
-
// (RFC 3279 + X.690 strict-DER
|
|
18
|
+
// validator.sig.ecdsaDerToP1363 -- the COMPLETE order-aware DER ECDSA-Sig-Value
|
|
19
|
+
// conformance (RFC 3279 + X.690 strict-DER +
|
|
20
|
+
// FIPS 186-5 [1,n-1], CVE-2022-21449) + raw r||s
|
|
20
21
|
// validator.attcert.packedCert / .aikCert / .aaguidExt / .requireV3 / .assertNotCa
|
|
21
22
|
// -- the WebAuthn attestation-certificate profile
|
|
22
23
|
// (sec. 8.2.1 packed, sec. 8.3.1 TPM AIK), one home
|
package/lib/validator-sig.js
CHANGED
|
@@ -25,42 +25,13 @@
|
|
|
25
25
|
|
|
26
26
|
var asn1 = require("./asn1-der");
|
|
27
27
|
|
|
28
|
-
// ecdsaSigToRaw(der, coordLen, E, code) -> the validated signature as raw r||s, each
|
|
29
|
-
// coordinate left-padded to coordLen bytes, or throws new E(code, ...). The complete DER
|
|
30
|
-
// ECDSA-Sig-Value conformance gate; a verifier MUST route an ECDSA signature through here,
|
|
31
|
-
// never hand-decode the SEQUENCE and read r/s content raw (which skips minimality).
|
|
32
|
-
// @enforced-by behavioral -- a DER ECDSA-Sig-Value decode has no rename-proof code shape
|
|
33
|
-
// distinct from generic 2-child-SEQUENCE content access; the RED conformance vectors
|
|
34
|
-
// (non-minimal / negative / zero / over-size r or s rejected) and the webauthn ECDSA KATs
|
|
35
|
-
// are the guard.
|
|
36
|
-
function ecdsaSigToRaw(der, coordLen, E, code) {
|
|
37
|
-
var node;
|
|
38
|
-
try { node = asn1.decode(der); } catch (e) { throw new E(code, "ECDSA signature is not a DER SEQUENCE", e); }
|
|
39
|
-
if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children || node.children.length !== 2) {
|
|
40
|
-
throw new E(code, "ECDSA signature must be a DER SEQUENCE { r, s }");
|
|
41
|
-
}
|
|
42
|
-
function coord(c, label) {
|
|
43
|
-
// The strict DER integer reader enforces PRIMITIVE + MINIMAL encoding (a constructed
|
|
44
|
-
// child, an empty INTEGER, or a redundant 0x00/0xFF sign octet all throw). The value
|
|
45
|
-
// is then range-checked: r and s MUST be positive (>= 1) and fit the curve field size.
|
|
46
|
-
var v;
|
|
47
|
-
try { v = asn1.read.integer(c); } catch (e) { throw new E(code, "ECDSA signature " + label + " is not a minimally-encoded DER INTEGER", e); }
|
|
48
|
-
if (v <= 0n) throw new E(code, "ECDSA signature " + label + " must be a positive integer");
|
|
49
|
-
var hex = v.toString(16); if (hex.length % 2) hex = "0" + hex;
|
|
50
|
-
var b = Buffer.from(hex, "hex");
|
|
51
|
-
if (b.length > coordLen) throw new E(code, "ECDSA signature " + label + " exceeds the curve field size");
|
|
52
|
-
var out = Buffer.alloc(coordLen); b.copy(out, coordLen - b.length); return out;
|
|
53
|
-
}
|
|
54
|
-
return Buffer.concat([coord(node.children[0], "r"), coord(node.children[1], "s")]);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
28
|
// rawToEcdsaDer(raw, coordLen) -> the canonical DER ECDSA-Sig-Value SEQUENCE { r, s } for a raw
|
|
58
|
-
// r||s signature (IEEE P1363, the WebCrypto sign() output). The inverse of
|
|
29
|
+
// r||s signature (IEEE P1363, the WebCrypto sign() output). The inverse of ecdsaDerToP1363: a
|
|
59
30
|
// signer composes this so the emitted ECDSA signature is canonical DER (minimally-encoded
|
|
60
31
|
// INTEGERs via the build layer), never a hand-built SEQUENCE that could re-introduce a
|
|
61
32
|
// non-minimal encoding. A config-time TypeError guards a mis-sized raw signature at entry.
|
|
62
33
|
// @enforced-by behavioral -- a DER ECDSA-Sig-Value BUILD has no rename-proof code shape distinct
|
|
63
|
-
// from generic sequence([integer,integer]); the round-trip vectors (build ->
|
|
34
|
+
// from generic sequence([integer,integer]); the round-trip vectors (build -> ecdsaDerToP1363
|
|
64
35
|
// identity) and the cms.sign ECDSA KATs are the guard.
|
|
65
36
|
function rawToEcdsaDer(raw, coordLen) {
|
|
66
37
|
if (!Buffer.isBuffer(raw) || typeof coordLen !== "number" || coordLen <= 0 || raw.length !== coordLen * 2) {
|
|
@@ -82,12 +53,15 @@ var CURVE_ORDER = {
|
|
|
82
53
|
// ecdsaDerToP1363(der, curve, E, code) -> the DER ECDSA-Sig-Value converted to raw r||s (P1363),
|
|
83
54
|
// each coordinate left-padded to the curve field width, rejecting r or s outside [1, n-1] against
|
|
84
55
|
// the curve ORDER (CVE-2022-21449 "Psychic Signatures" -- the r/s = 0 case AND the >= n upper
|
|
85
|
-
// bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
56
|
+
// bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the SINGLE ECDSA-Sig-Value
|
|
57
|
+
// conformance gate every curve-aware verifier routes through: it enforces the complete strict-DER
|
|
58
|
+
// rule set (2-INTEGER SEQUENCE, minimal encoding) AND the order bound, so a verifier never
|
|
59
|
+
// hand-decodes the SEQUENCE (skipping minimality) nor bounds r/s only by the field size (missing
|
|
60
|
+
// the order). A non-minimal, negative, zero, over-size, or >= n coordinate fails closed.
|
|
61
|
+
// @enforced-by behavioral -- a DER ECDSA-Sig-Value decode has no rename-proof code shape distinct
|
|
62
|
+
// from generic 2-child-SEQUENCE content access; the RED conformance vectors (r/s = 0, r/s >= n,
|
|
63
|
+
// non-minimal, negative, over-size) and the cms / ct / composite / path / webauthn ECDSA KATs are
|
|
64
|
+
// the guard.
|
|
91
65
|
function ecdsaDerToP1363(der, curve, E, code) {
|
|
92
66
|
var width = CURVE_FIELD_BYTES[curve];
|
|
93
67
|
var order = CURVE_ORDER[curve];
|
|
@@ -99,8 +73,8 @@ function ecdsaDerToP1363(der, curve, E, code) {
|
|
|
99
73
|
throw new E(code, "ECDSA signature must be a SEQUENCE of exactly two INTEGERs");
|
|
100
74
|
}
|
|
101
75
|
// Wrap the strict DER integer reads (they enforce PRIMITIVE + MINIMAL encoding) so a non-minimal /
|
|
102
|
-
// constructed / empty INTEGER surfaces the CALLER's typed code, not a raw asn1/* -- the
|
|
103
|
-
//
|
|
76
|
+
// constructed / empty INTEGER surfaces the CALLER's typed code, not a raw asn1/* -- the complete DER
|
|
77
|
+
// conformance rule set, gated together with the order bound below.
|
|
104
78
|
var r, s;
|
|
105
79
|
try { r = asn1.read.integer(n.children[0]); } catch (e) { throw new E(code, "ECDSA signature r is not a minimally-encoded DER INTEGER", e); }
|
|
106
80
|
try { s = asn1.read.integer(n.children[1]); } catch (e) { throw new E(code, "ECDSA signature s is not a minimally-encoded DER INTEGER", e); }
|
|
@@ -122,4 +96,4 @@ function ecdsaDerToP1363(der, curve, E, code) {
|
|
|
122
96
|
return Buffer.concat([pad(r), pad(s)]);
|
|
123
97
|
}
|
|
124
98
|
|
|
125
|
-
module.exports = {
|
|
99
|
+
module.exports = { rawToEcdsaDer: rawToEcdsaDer, ecdsaDerToP1363: ecdsaDerToP1363 };
|
package/lib/webcrypto.js
CHANGED
|
@@ -1000,10 +1000,27 @@ Crypto.prototype.getRandomValues = function getRandomValues(typedArray) {
|
|
|
1000
1000
|
|
|
1001
1001
|
Crypto.prototype.randomUUID = function randomUUID() { return nodeCrypto.randomUUID(); };
|
|
1002
1002
|
|
|
1003
|
+
// decompressEcPoint(sec1Compressed, nodeCurve) -> the uncompressed SEC1 point 0x04||X||Y for a
|
|
1004
|
+
// compressed 0x02/0x03||X input, computing Y on-curve via node's ECDH.convertKey. The crypto-engine
|
|
1005
|
+
// home for EC point de-compression (Hard rule #8: EC key material is webcrypto's domain). Fail-closed:
|
|
1006
|
+
// convertKey throws on an off-curve X or an unsupported curve, surfaced as the caller's typed error.
|
|
1007
|
+
// `nodeCurve` is the OpenSSL curve name (prime256v1 / secp384r1 / secp521r1). The C509-specific
|
|
1008
|
+
// 0xFE/0xFD marker -> 0x02/0x03 parity translation stays in the C509 layer (this takes a real SEC1 point).
|
|
1009
|
+
function decompressEcPoint(sec1Compressed, nodeCurve, E, code) {
|
|
1010
|
+
var head = sec1Compressed[0];
|
|
1011
|
+
if (head !== 0x02 && head !== 0x03) throw E(code, "EC point de-compression expects a compressed SEC1 point (0x02/0x03)");
|
|
1012
|
+
try {
|
|
1013
|
+
return nodeCrypto.ECDH.convertKey(sec1Compressed, nodeCurve, undefined, undefined, "uncompressed");
|
|
1014
|
+
} catch (e) {
|
|
1015
|
+
throw E(code, "EC point is not on curve " + nodeCurve + " (de-compression failed)", e);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1003
1019
|
module.exports = {
|
|
1004
1020
|
webcrypto: new Crypto(),
|
|
1005
1021
|
Crypto: Crypto,
|
|
1006
1022
|
SubtleCrypto: SubtleCrypto,
|
|
1007
1023
|
CryptoKey: CryptoKey,
|
|
1008
1024
|
WebCryptoError: WebCryptoError,
|
|
1025
|
+
decompressEcPoint: decompressEcPoint,
|
|
1009
1026
|
};
|
package/package.json
CHANGED
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:c89087a8-9dba-4496-9704-cd639f3c4038",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-07-
|
|
8
|
+
"timestamp": "2026-07-16T21:10:43.618Z",
|
|
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.2.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.2.31",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.2.
|
|
25
|
+
"version": "0.2.31",
|
|
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.2.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.2.31",
|
|
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.2.
|
|
57
|
+
"ref": "@blamejs/pki@0.2.31",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|