@blamejs/pki 0.4.2 → 0.4.3
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 +22 -1
- package/README.md +2 -0
- package/index.js +5 -0
- package/lib/cms-compress.js +10 -12
- package/lib/cms-sign.js +28 -20
- package/lib/cms-verify.js +21 -12
- package/lib/composite-sig.js +4 -4
- package/lib/constants.js +13 -0
- package/lib/framework-error.js +10 -0
- package/lib/guard-all.js +5 -0
- package/lib/guard-compress.js +164 -0
- package/lib/tls-cert-compress.js +398 -0
- package/lib/validator-all.js +2 -0
- package/lib/validator-tls.js +168 -0
- package/lib/webcrypto.js +41 -9
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/webcrypto.js
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* **PQC-first without being PQC-only**: the FIPS 204 ML-DSA and FIPS
|
|
21
21
|
* 205 SLH-DSA signature suites sit alongside the full classical set PKI
|
|
22
22
|
* still runs on -- RSASSA-PKCS1-v1_5, RSA-PSS, RSA-OAEP, ECDSA, ECDH,
|
|
23
|
-
* Ed25519 / Ed448, AES-GCM / CBC / KW, HMAC, HKDF, PBKDF2,
|
|
24
|
-
*
|
|
23
|
+
* Ed25519 / Ed448, AES-GCM / CBC / KW, HMAC, HKDF, PBKDF2, the SHA family
|
|
24
|
+
* (including legacy SHA-1 for old certificates and signatures), and the
|
|
25
|
+
* SHAKE128 / SHAKE256 extendable-output functions as message digests.
|
|
25
26
|
* FIPS 203 ML-KEM key generation, encoding, certificate/PKCS#8 import,
|
|
26
27
|
* and encapsulation/decapsulation (encapsulateBits / decapsulateBits over
|
|
27
28
|
* Node's crypto.encapsulate/decapsulate) are all available.
|
|
@@ -100,6 +101,21 @@ var HASH_NODE = {
|
|
|
100
101
|
"SHA3-512": "sha3-512",
|
|
101
102
|
};
|
|
102
103
|
|
|
104
|
+
// FIPS 202 extendable-output functions, as message digests. Deliberately a
|
|
105
|
+
// SEPARATE table from HASH_NODE rather than rows added to it: an XOF's output
|
|
106
|
+
// length is a parameter rather than a property of its name, and node:crypto
|
|
107
|
+
// refuses an XOF at every other HASH_NODE consumer (signature, HMAC, HKDF,
|
|
108
|
+
// PBKDF2, OAEP). Widening the shared table would turn those consumers' typed
|
|
109
|
+
// "unsupported hash" rejection into a raw error from the underlying library.
|
|
110
|
+
// The length is fixed by the name, not chosen by the caller: RFC 8702 sec. 4
|
|
111
|
+
// requires the output length of SHAKE128 or SHAKE256 used as a message digest
|
|
112
|
+
// to be 32 or 64 bytes respectively, and RFC 9814 sec. 4 and
|
|
113
|
+
// draft-ietf-lamps-cms-composite-sigs sec. 3 restate it for their profiles.
|
|
114
|
+
var XOF_NODE = {
|
|
115
|
+
"SHAKE128": { node: "shake128", length: 32 },
|
|
116
|
+
"SHAKE256": { node: "shake256", length: 64 },
|
|
117
|
+
};
|
|
118
|
+
|
|
103
119
|
function _hashNode(h, who) {
|
|
104
120
|
var name = (typeof h === "string") ? h : (h && h.name);
|
|
105
121
|
var node = HASH_NODE[String(name).toUpperCase()];
|
|
@@ -218,8 +234,14 @@ function _requireAlgMatch(alg, key, who) {
|
|
|
218
234
|
function SubtleCrypto() {}
|
|
219
235
|
|
|
220
236
|
SubtleCrypto.prototype.digest = async function digest(algorithm, data) {
|
|
221
|
-
var
|
|
222
|
-
var
|
|
237
|
+
var name = _normalizeAlg(algorithm, "digest").name;
|
|
238
|
+
var xof = XOF_NODE[String(name).toUpperCase()];
|
|
239
|
+
// An XOF is always constructed with its output length stated. Omitting it
|
|
240
|
+
// makes node emit a deprecation warning and fall back to a 32-byte squeeze,
|
|
241
|
+
// which for SHAKE256 would silently produce half the required digest.
|
|
242
|
+
var h = xof
|
|
243
|
+
? nodeCrypto.createHash(xof.node, { outputLength: xof.length })
|
|
244
|
+
: nodeCrypto.createHash(_hashNode(name, "digest"));
|
|
223
245
|
h.update(_toBuf(data, "digest"));
|
|
224
246
|
return _toArrayBuffer(h.digest());
|
|
225
247
|
};
|
|
@@ -256,11 +278,15 @@ SubtleCrypto.prototype.generateKey = async function generateKey(algorithm, extra
|
|
|
256
278
|
function _generateKeyPair(alg) {
|
|
257
279
|
var name = alg.name, kp, algorithm;
|
|
258
280
|
if (name === "RSASSA-PKCS1-V1_5" || name === "RSA-PSS" || name === "RSA-OAEP") {
|
|
281
|
+
// Resolve the hash BEFORE generating. An RSA keygen is the expensive step here, and a hash
|
|
282
|
+
// this engine cannot use yields a key that can never sign, so the refusal has to come first
|
|
283
|
+
// or the caller pays for a keypair only to be told the request was unusable.
|
|
284
|
+
var rsaHash = _hashObj(alg.hash, "generateKey " + name);
|
|
259
285
|
kp = nodeCrypto.generateKeyPairSync("rsa", {
|
|
260
286
|
modulusLength: alg.modulusLength || 2048,
|
|
261
287
|
publicExponent: alg.publicExponent ? _bufToBigIntNum(alg.publicExponent) : 65537,
|
|
262
288
|
});
|
|
263
|
-
algorithm = { name: _stdName(name), modulusLength: alg.modulusLength || 2048, publicExponent: alg.publicExponent, hash:
|
|
289
|
+
algorithm = { name: _stdName(name), modulusLength: alg.modulusLength || 2048, publicExponent: alg.publicExponent, hash: rsaHash };
|
|
264
290
|
} else if (name === "ECDSA" || name === "ECDH") {
|
|
265
291
|
var curve = alg.namedCurve;
|
|
266
292
|
if (!CURVE_NODE[curve]) throw new WebCryptoError("webcrypto/not-supported", name + ": unsupported curve " + JSON.stringify(curve));
|
|
@@ -284,7 +310,13 @@ function _generateKeyPair(alg) {
|
|
|
284
310
|
return { publicKey: kp.publicKey, privateKey: kp.privateKey, algorithm: algorithm };
|
|
285
311
|
}
|
|
286
312
|
|
|
287
|
-
|
|
313
|
+
// The hash recorded on a CryptoKey's algorithm. The name is RESOLVED here, so an
|
|
314
|
+
// entry point refuses a hash this engine cannot use instead of minting a key that
|
|
315
|
+
// only fails at its first sign / verify / wrap -- after the caller has already paid
|
|
316
|
+
// for the key generation. Resolution runs through _hashNode itself rather than a
|
|
317
|
+
// second table, so what an entry point accepts cannot drift from what the
|
|
318
|
+
// operations support.
|
|
319
|
+
function _hashObj(h, who) { if (!h) return undefined; var n = (typeof h === "string" ? h : h.name); _hashNode(n, who || "importKey"); return { name: n }; }
|
|
288
320
|
// The node key argument for an ML-DSA sign/verify: the bare handle, or a
|
|
289
321
|
// { key, context } when the caller supplies an ML-DSA context. FIPS 204 bounds the
|
|
290
322
|
// context to 0..255 bytes; a longer one is a DataError at the API boundary, not a
|
|
@@ -849,7 +881,7 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
|
|
|
849
881
|
var raw = _toBuf(keyData, "importKey raw");
|
|
850
882
|
_assertAesImportLen(name, raw.length);
|
|
851
883
|
var secret = nodeCrypto.createSecretKey(raw);
|
|
852
|
-
var symAlg = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash), length: raw.length * 8 } : { name: name, length: raw.length * 8 };
|
|
884
|
+
var symAlg = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash, "importKey raw HMAC"), length: raw.length * 8 } : { name: name, length: raw.length * 8 };
|
|
853
885
|
return new CryptoKey("secret", extractable, symAlg, usages, secret);
|
|
854
886
|
}
|
|
855
887
|
// Raw public keys are imported via JWK reconstruction below.
|
|
@@ -879,7 +911,7 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
|
|
|
879
911
|
var kbuf = _b64urlToBuf(jwk.k, "JWK oct key material");
|
|
880
912
|
_assertAesImportLen(name, kbuf.length);
|
|
881
913
|
var s2 = nodeCrypto.createSecretKey(kbuf);
|
|
882
|
-
var a2 = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash), length: kbuf.length * 8 } : { name: name, length: kbuf.length * 8 };
|
|
914
|
+
var a2 = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash, "importKey jwk HMAC"), length: kbuf.length * 8 } : { name: name, length: kbuf.length * 8 };
|
|
883
915
|
return new CryptoKey("secret", extractable, a2, usages, s2);
|
|
884
916
|
}
|
|
885
917
|
var isPrivate = Object.prototype.hasOwnProperty.call(jwk, "d");
|
|
@@ -946,7 +978,7 @@ function _algFromImport(name, alg, keyObject) {
|
|
|
946
978
|
}
|
|
947
979
|
return { name: name, namedCurve: actualCurve };
|
|
948
980
|
}
|
|
949
|
-
if (name === "RSASSA-PKCS1-V1_5" || name === "RSA-PSS" || name === "RSA-OAEP") return { name: _stdName(name), hash: _hashObj(alg.hash) };
|
|
981
|
+
if (name === "RSASSA-PKCS1-V1_5" || name === "RSA-PSS" || name === "RSA-OAEP") return { name: _stdName(name), hash: _hashObj(alg.hash, "importKey " + name) };
|
|
950
982
|
if (name === "ED25519" || name === "ED448") return { name: _stdName(name) };
|
|
951
983
|
return { name: alg.name };
|
|
952
984
|
}
|
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:1c291ecb-db3e-4569-b3aa-70bb7803b9f3",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-08T07:58:59.469Z",
|
|
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.4.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.4.3",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.4.
|
|
25
|
+
"version": "0.4.3",
|
|
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.4.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.4.3",
|
|
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.4.
|
|
57
|
+
"ref": "@blamejs/pki@0.4.3",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|