@blamejs/pki 0.6.52 → 0.6.53
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 +20 -0
- package/lib/composite-kem.js +166 -2
- package/lib/key.js +37 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.6.53 — 2026-09-07
|
|
8
|
+
|
|
9
|
+
A composite ML-KEM private key states its public half, so a holder can name the recipient it is.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- pki.key.publicFromPrivate derives the public key of a composite ML-KEM private key (draft-ietf-lamps-pq-composite-kem section 4.1). A composite private key is the ML-KEM seed followed by the traditional private key, and the public key is the ML-KEM encapsulation key followed by the traditional public key under the same OID. Each half states its own: the seed generates the encapsulation key, and the traditional half is read in the encoding its algorithm uses, an RSAPrivateKey, a SEC1 ECPrivateKey, or the raw scalar of an OKP curve. All twelve registered families are covered, and each derives byte-for-byte the public key the draft's own Appendix G test vectors publish for that key. opts.pem returns it armored, as it does for every other family.
|
|
14
|
+
- The derived key is the one the rest of the toolkit takes. It is the recipient pki.cms.encrypt accepts for a composite KEM recipient, and pki.kem.encapsulate establishes a shared secret for it that the private key it came from decapsulates to the same value.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- A composite ML-KEM private key reports why it cannot be read, rather than reporting that a public key could not be derived from it. An AlgorithmIdentifier carrying parameters is kem/bad-algorithm, since the draft requires them absent (section 5.3); a key no longer than the ML-KEM seed is kem/bad-key; and an OID no registered family claims is refused rather than guessed at. The private key material and the copy taken of it are wiped before the call returns, on the refusing paths as well as the deriving one.
|
|
19
|
+
- Each component is held to what the composite OID names it must be. The RSA component's modulus must be the size that family states, and the EC component must be on the curve it states: a SEC1 ECPrivateKey carries its own curve, and exporting the point of one curve under an OID naming another would state a public key nothing can use. Decapsulation reaches the same conclusion by failing the agreement, and a derivation performs no operation that would notice, so it is checked.
|
|
20
|
+
- Every copy of private key material the derivation allocates is cleared before it returns, on the refusing paths as well as the deriving one: the caller's snapshot, the key octets, and the ML-KEM seed re-encoded for the key engine to read. The EC component adds two more, the scalar taken out of the ECPrivateKey and the copy a width check rejects, and a decapsulation refused after its Diffie-Hellman has already run clears that agreement's secret before reporting the refusal.
|
|
21
|
+
- The EC component's point is generated from its scalar rather than read from the key, in the derivation and in pki.kem.decapsulate alike. RFC 5915 section 3 gives an ECPrivateKey an optional public point, which may be written compressed, and the composite public key carries the uncompressed point at a fixed width. The combiner hashes that public key, so the two sides read one function for it: a key storing a compressed point now establishes the same shared secret whichever spelling of it a sender encapsulated to. Generating the point also means the answer is the one the scalar produces, so a point stored beside a scalar that does not produce it does not reach the result.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- The X25519 and X448 component of a composite ML-KEM key is imported through an encoding that is now cleared once the import has read it. That encoding holds the component's private scalar and is allocated inside the helper both pki.kem.decapsulate and the new derivation reach the curve through, so a decapsulation left a copy of it behind.
|
|
26
|
+
|
|
7
27
|
## v0.6.52 — 2026-09-07
|
|
8
28
|
|
|
9
29
|
A CMP client can accept a key pair the CA generated for it, and refuses one from an authority that never claimed to be one.
|
package/lib/composite-kem.js
CHANGED
|
@@ -102,9 +102,16 @@ function _okpPubFromRaw(d, raw) {
|
|
|
102
102
|
var spki = _b.sequence([_b.sequence([_b.oid(oid.byName(d.trad.curve))]), _b.bitString(raw, 0)]);
|
|
103
103
|
return nodeCrypto.createPublicKey({ key: spki, format: "der", type: "spki" });
|
|
104
104
|
}
|
|
105
|
+
/** @internal The encoding is this helper's own allocation and holds the traditional private scalar, so
|
|
106
|
+
* it is cleared here. A caller's own cleanup cannot reach a buffer it never sees, and both the
|
|
107
|
+
* derivation and the decapsulation reach the curve through this one function. */
|
|
105
108
|
function _okpPrivFromRaw(d, raw) {
|
|
106
109
|
var p8 = _b.sequence([_b.integer(0n), _b.sequence([_b.oid(oid.byName(d.trad.curve))]), _b.octetString(_b.octetString(raw))]);
|
|
107
|
-
|
|
110
|
+
try {
|
|
111
|
+
return nodeCrypto.createPrivateKey({ key: p8, format: "der", type: "pkcs8" });
|
|
112
|
+
} finally {
|
|
113
|
+
guard.secret.zeroize(p8, KemError, "kem/bad-input", "the " + d.trad.curve + " component key encoding");
|
|
114
|
+
}
|
|
108
115
|
}
|
|
109
116
|
function _rawPubOf(keyObject) {
|
|
110
117
|
var spki = keyObject.export({ format: "der", type: "spki" });
|
|
@@ -170,7 +177,20 @@ function _tradDecaps(d, tradSK, tradCT) {
|
|
|
170
177
|
var dh;
|
|
171
178
|
try { dh = Buffer.from(nodeCrypto.diffieHellman({ privateKey: priv2, publicKey: eph })); }
|
|
172
179
|
catch (e) { return Promise.reject(_err("kem/decapsulation-failed", "the " + trad.curve + " Diffie-Hellman failed", e)); }
|
|
173
|
-
|
|
180
|
+
/** @internal The same canonical point the derivation states, read from the one function that spells
|
|
181
|
+
* it. The combiner hashes these bytes, so a stored spelling here and a generated one there would
|
|
182
|
+
* reach different secrets from the two sides of one key. */
|
|
183
|
+
var tradPK;
|
|
184
|
+
try {
|
|
185
|
+
tradPK = trad.kind === "ec" ? _ecPointOf(d, tradSK) : _rawPubOf(nodeCrypto.createPublicKey(priv2));
|
|
186
|
+
} catch (e) {
|
|
187
|
+
/** @internal The agreement already produced a secret, and this rejection happens after it, so the
|
|
188
|
+
* secret is wiped here: the caller's settle-or-wipe covers a value it was handed, and a rejected
|
|
189
|
+
* promise hands it nothing. */
|
|
190
|
+
guard.secret.zeroize(dh, KemError, "kem/bad-input", "the " + trad.curve + " component secret");
|
|
191
|
+
return Promise.reject(e);
|
|
192
|
+
}
|
|
193
|
+
return Promise.resolve({ ss: dh, tradPK: tradPK });
|
|
174
194
|
}
|
|
175
195
|
|
|
176
196
|
function _combine(d, mlkemSS, tradSS, tradCT, tradPK) {
|
|
@@ -309,8 +329,152 @@ async function _encapsulate(publicKey) {
|
|
|
309
329
|
|
|
310
330
|
function _isCompositeKem(algOid) { return COMPOSITE_KEM[algOid] != null; }
|
|
311
331
|
|
|
332
|
+
/** @internal The EC component's public point, generated from its scalar. RFC 5915 sec. 3 gives an
|
|
333
|
+
* ECPrivateKey an optional public point which may be written compressed, and the key engine keeps
|
|
334
|
+
* whatever encoding it was handed. The composite concatenation carries the uncompressed point at a
|
|
335
|
+
* fixed width (draft sec. 4.1), and the combiner hashes those bytes, so BOTH the derivation and the
|
|
336
|
+
* decapsulation read this one function: a point spelled two ways would agree on the secret nowhere. */
|
|
337
|
+
function _ecPointOf(d, tradSK) {
|
|
338
|
+
/** @internal The scalar is a fresh copy of private key material, so it is wiped here rather than
|
|
339
|
+
* left for the caller's own copies to cover: those wipe the PKCS#8 and the key octets, and this
|
|
340
|
+
* buffer is neither. */
|
|
341
|
+
var scalar = _ecScalarOf(d, tradSK);
|
|
342
|
+
var point;
|
|
343
|
+
try {
|
|
344
|
+
var ecdh = nodeCrypto.createECDH(d.trad.node);
|
|
345
|
+
ecdh.setPrivateKey(scalar);
|
|
346
|
+
point = ecdh.getPublicKey(null, "uncompressed");
|
|
347
|
+
} catch (e) {
|
|
348
|
+
if (e instanceof KemError) throw e;
|
|
349
|
+
throw _err("kem/bad-key", "the EC component scalar does not generate a point on " + d.trad.node, e);
|
|
350
|
+
} finally {
|
|
351
|
+
guard.secret.zeroize(scalar, KemError, "kem/bad-input", "the EC component scalar");
|
|
352
|
+
}
|
|
353
|
+
if (point.length !== d.trad.ctLen) throw _err("kem/bad-key", "the EC component point is not the expected " + d.trad.ctLen + " bytes for " + d.name);
|
|
354
|
+
return point;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** @internal The private scalar of a SEC1 ECPrivateKey (RFC 5915 sec. 3), left-padded to the field
|
|
358
|
+
* width the curve uses. The structure states the scalar as an OCTET STRING in its second field, and
|
|
359
|
+
* the point generation below is positional, so a short one is padded rather than shifted. */
|
|
360
|
+
function _ecScalarOf(d, tradSK) {
|
|
361
|
+
var width = (d.trad.ctLen - 1) / 2;
|
|
362
|
+
var raw;
|
|
363
|
+
try {
|
|
364
|
+
var node = asn1.decode(tradSK);
|
|
365
|
+
raw = Buffer.from(asn1.read.octetString(node.children[1]));
|
|
366
|
+
} catch (e) { throw _err("kem/bad-key", "the EC component private key is not a well-formed ECPrivateKey", e); }
|
|
367
|
+
/** @internal `raw` holds private key material. It is handed on only when it IS the returned buffer,
|
|
368
|
+
* which the caller then wipes; on every other exit, the padded one and the refusal alike, this is
|
|
369
|
+
* the last reference and the copy is cleared here. */
|
|
370
|
+
var handedOn = false;
|
|
371
|
+
try {
|
|
372
|
+
if (raw.length === 0 || raw.length > width) throw _err("kem/bad-key", "the EC component scalar is not a " + d.trad.node + " field element");
|
|
373
|
+
if (raw.length === width) { handedOn = true; return raw; }
|
|
374
|
+
var out = Buffer.alloc(width);
|
|
375
|
+
raw.copy(out, width - raw.length);
|
|
376
|
+
return out;
|
|
377
|
+
} finally {
|
|
378
|
+
if (!handedOn) guard.secret.zeroize(raw, KemError, "kem/bad-input", "the EC component scalar copy");
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** @internal The traditional component's public half, derived from its private half by the same route
|
|
383
|
+
* `_tradDecaps` takes to report `tradPK`. Each kind states its private key in its own encoding: an
|
|
384
|
+
* RSAPrivateKey, a SEC1 ECPrivateKey, or the raw scalar an OKP curve carries. */
|
|
385
|
+
function _tradPubOf(d, tradSK) {
|
|
386
|
+
var trad = d.trad;
|
|
387
|
+
if (trad.kind === "rsa") {
|
|
388
|
+
var rp;
|
|
389
|
+
try {
|
|
390
|
+
asn1.decode(tradSK);
|
|
391
|
+
rp = nodeCrypto.createPrivateKey({ key: tradSK, format: "der", type: "pkcs1" });
|
|
392
|
+
} catch (e) { throw _err("kem/bad-key", "the RSA component private key is not a valid RSAPrivateKey", e); }
|
|
393
|
+
if (rp.asymmetricKeyDetails.modulusLength !== trad.bits) {
|
|
394
|
+
throw _err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)");
|
|
395
|
+
}
|
|
396
|
+
return Buffer.from(nodeCrypto.createPublicKey(rp).export({ format: "der", type: "pkcs1" }));
|
|
397
|
+
}
|
|
398
|
+
var priv;
|
|
399
|
+
try {
|
|
400
|
+
if (trad.kind === "ec") {
|
|
401
|
+
asn1.decode(tradSK);
|
|
402
|
+
priv = nodeCrypto.createPrivateKey({ key: tradSK, format: "der", type: "sec1" });
|
|
403
|
+
} else {
|
|
404
|
+
priv = _okpPrivFromRaw(d, tradSK);
|
|
405
|
+
}
|
|
406
|
+
} catch (e) { throw _err("kem/bad-key", "the " + trad.curve + " key material is invalid", e); }
|
|
407
|
+
/** @internal A SEC1 ECPrivateKey names its own curve, and the composite OID names the curve the
|
|
408
|
+
* component must be on. Exporting the point of one curve under an OID that names another states a
|
|
409
|
+
* public key nothing can use, so the two are held to each other here. Decapsulation reaches the same
|
|
410
|
+
* conclusion by failing the agreement; a derivation performs no operation that would notice. */
|
|
411
|
+
if (trad.kind === "ec") {
|
|
412
|
+
var named = priv.asymmetricKeyDetails && priv.asymmetricKeyDetails.namedCurve;
|
|
413
|
+
if (named !== trad.node) {
|
|
414
|
+
throw _err("kem/bad-key", "the EC component is on " + guard.text.showValue(named) + ", and " + d.name + " names " + trad.node);
|
|
415
|
+
}
|
|
416
|
+
/** @internal Recomputed from the scalar rather than exported. RFC 5915 gives an ECPrivateKey an
|
|
417
|
+
* optional public point, the key engine keeps whatever encoding it was handed, and a compressed
|
|
418
|
+
* point is neither the width nor the form the composite concatenation carries (draft sec. 4.1).
|
|
419
|
+
* Generating the point from the scalar answers with the key this component actually holds, so a
|
|
420
|
+
* point stored beside it cannot stand in for one it does not generate. */
|
|
421
|
+
return _ecPointOf(d, tradSK);
|
|
422
|
+
}
|
|
423
|
+
return _rawPubOf(nodeCrypto.createPublicKey(priv));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** @internal The composite `SubjectPublicKeyInfo` for a composite ML-KEM private key. The runtime key
|
|
427
|
+
* decoder does not know a composite OID, so a caller deriving one cannot go through it; the key is
|
|
428
|
+
* split the way `_decapsulate` splits it (draft sec. 4.1: `seed || tradSK`) and each half states its
|
|
429
|
+
* own public key, concatenated under the same OID as `mlkemEK || tradPK` (sec. 5.1). The ML-KEM half
|
|
430
|
+
* is recovered by importing the seed and reading the public key it generates, so a stored public copy
|
|
431
|
+
* could not answer in its place. The private material is wiped on every path. */
|
|
432
|
+
async function _publicFromPrivate(privateKey) {
|
|
433
|
+
var pkcs8 = _toDer(privateKey, "PRIVATE KEY", "the composite ML-KEM private key");
|
|
434
|
+
/** @internal Every buffer this derivation allocates that holds private key material is registered
|
|
435
|
+
* here and cleared once, on every exit. A copy added later is covered by registering it rather than
|
|
436
|
+
* by a further nested block, which is how the ML-KEM seed encoding came to outlive two of them. */
|
|
437
|
+
var secrets = [];
|
|
438
|
+
guard.list.append(secrets, pkcs8);
|
|
439
|
+
var d;
|
|
440
|
+
try {
|
|
441
|
+
var sk;
|
|
442
|
+
try {
|
|
443
|
+
var info = schemaPkcs8.parse(pkcs8);
|
|
444
|
+
var alg = info.privateKeyAlgorithm;
|
|
445
|
+
d = _descriptorForAlg(alg.oid, alg.parameters !== null);
|
|
446
|
+
sk = Buffer.from(info.privateKey);
|
|
447
|
+
guard.list.append(secrets, sk);
|
|
448
|
+
} catch (e) {
|
|
449
|
+
if (e instanceof KemError) throw e;
|
|
450
|
+
if (e && e.code === "pkcs8/bad-algorithm-parameters") throw _err("kem/bad-algorithm", "a composite ML-KEM AlgorithmIdentifier MUST have absent parameters (draft sec. 5.3)", e);
|
|
451
|
+
throw _err("kem/bad-key", "the composite ML-KEM private key is not a well-formed OneAsymmetricKey", e);
|
|
452
|
+
}
|
|
453
|
+
if (sk.length <= MLKEM_SEED) throw _err("kem/bad-key", "the composite private key is shorter than the ML-KEM seed");
|
|
454
|
+
var seed = sk.subarray(0, MLKEM_SEED), tradSK = sk.subarray(MLKEM_SEED);
|
|
455
|
+
var ek;
|
|
456
|
+
try {
|
|
457
|
+
/** @internal The seed re-encoded as a OneAsymmetricKey the key engine reads, which is a second
|
|
458
|
+
* copy of the seed and is registered as one. */
|
|
459
|
+
var seedPkcs8 = _mlkemSeedPkcs8(d, seed);
|
|
460
|
+
guard.list.append(secrets, seedPkcs8);
|
|
461
|
+
var mlPriv = nodeCrypto.createPrivateKey({ key: seedPkcs8, format: "der", type: "pkcs8" });
|
|
462
|
+
ek = _rawPubOf(nodeCrypto.createPublicKey(mlPriv));
|
|
463
|
+
} catch (e) {
|
|
464
|
+
if (e instanceof KemError) throw e;
|
|
465
|
+
throw _err("kem/bad-key", "the ML-KEM component private key is invalid", e);
|
|
466
|
+
}
|
|
467
|
+
if (ek.length !== d.mlkemEk) throw _err("kem/bad-key", "the ML-KEM component public key is not the expected " + d.mlkemEk + " bytes for " + d.name);
|
|
468
|
+
var composite = Buffer.concat([ek, _tradPubOf(d, tradSK)]);
|
|
469
|
+
return _b.sequence([_b.sequence([_b.oid(oid.byName(d.name))]), _b.bitString(composite, 0)]);
|
|
470
|
+
} finally {
|
|
471
|
+
guard.secret.zeroizeAll(secrets, KemError, "kem/bad-input", "the composite private key material");
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
312
475
|
module.exports = {
|
|
313
476
|
encapsulate: _encapsulate,
|
|
314
477
|
decapsulate: _decapsulate,
|
|
315
478
|
isCompositeKem: _isCompositeKem,
|
|
479
|
+
publicFromPrivate: _publicFromPrivate,
|
|
316
480
|
};
|
package/lib/key.js
CHANGED
|
@@ -26,6 +26,7 @@ var pbes2 = require("./pbes2");
|
|
|
26
26
|
var pkcs8 = require("./schema-pkcs8");
|
|
27
27
|
var pkix = require("./schema-pkix");
|
|
28
28
|
var webcrypto = require("./webcrypto");
|
|
29
|
+
var compositeKem = require("./composite-kem");
|
|
29
30
|
var frameworkError = require("./framework-error");
|
|
30
31
|
var guard = require("./guard-all");
|
|
31
32
|
|
|
@@ -311,7 +312,7 @@ async function generate(algorithm, opts) {
|
|
|
311
312
|
* @signature pki.key.publicFromPrivate(privateKey, opts?) -> Promise<Buffer|string>
|
|
312
313
|
* @since 0.3.10
|
|
313
314
|
* @status stable
|
|
314
|
-
* @spec RFC 5280 sec. 4.1.2.7, RFC 8410 sec. 3
|
|
315
|
+
* @spec RFC 5280 sec. 4.1.2.7, RFC 8410 sec. 3, draft-ietf-lamps-pq-composite-kem sec. 4.1
|
|
315
316
|
* @related pki.key.export, pki.key.import
|
|
316
317
|
*
|
|
317
318
|
* Derive the SubjectPublicKeyInfo (SPKI) public key from a PKCS#8 private key (DER `Buffer`, `PRIVATE KEY`
|
|
@@ -319,6 +320,13 @@ async function generate(algorithm, opts) {
|
|
|
319
320
|
* the algorithm from the key structure, so no `AlgorithmIdentifier` is re-encoded: Ed25519 stays
|
|
320
321
|
* parameters-absent, RSA keeps its NULL, EC keeps its namedCurve.
|
|
321
322
|
*
|
|
323
|
+
* A composite ML-KEM key is derived from its two components instead, since the node key engine does not
|
|
324
|
+
* read a composite algorithm. The private key is `seed || tradSK` and the public key is
|
|
325
|
+
* `mlkemEK || tradPK` under the same OID: the seed states the ML-KEM encapsulation key it generates, the
|
|
326
|
+
* traditional half states its own public key, and the two are concatenated. An AlgorithmIdentifier
|
|
327
|
+
* carrying parameters, a key no longer than the ML-KEM seed, and an unregistered composite OID are each
|
|
328
|
+
* refused with a typed `KemError`.
|
|
329
|
+
*
|
|
322
330
|
* @opts
|
|
323
331
|
* - `pem` (boolean) -- return a `PUBLIC KEY` PEM string instead of DER.
|
|
324
332
|
* @example
|
|
@@ -327,12 +335,40 @@ async function generate(algorithm, opts) {
|
|
|
327
335
|
*/
|
|
328
336
|
var _PUBLIC_FROM_PRIVATE_OPTS = Object.assign(Object.create(null), { pem: 1 });
|
|
329
337
|
|
|
338
|
+
/** @internal The composite ML-KEM OID a private key names, or null when it names anything else. Read
|
|
339
|
+
* from the AlgorithmIdentifier alone: a key that is not well formed enough to state one is left to the
|
|
340
|
+
* decoder below, which reports it. */
|
|
341
|
+
function _compositeKemOidOf(der) {
|
|
342
|
+
var algOid;
|
|
343
|
+
try {
|
|
344
|
+
/** @internal The AlgorithmIdentifier OID only, read without the strict OneAsymmetricKey rules. A
|
|
345
|
+
* composite key that breaks one of those rules is still a composite key, and the module that owns
|
|
346
|
+
* the algorithm is the one that should say so: parsing strictly here would send a composite key
|
|
347
|
+
* carrying parameters to the runtime decoder, which reports that it cannot read the key rather
|
|
348
|
+
* than that the parameters are not allowed (draft sec. 5.3). */
|
|
349
|
+
var node = asn1.decode(der);
|
|
350
|
+
if (!node.children || node.children.length < 3) return null;
|
|
351
|
+
var alg = node.children[1];
|
|
352
|
+
if (!alg.children || alg.children.length < 1) return null;
|
|
353
|
+
algOid = asn1.read.oid(alg.children[0]);
|
|
354
|
+
} catch (_e) { return null; }
|
|
355
|
+
return typeof algOid === "string" && compositeKem.isCompositeKem(algOid) ? algOid : null;
|
|
356
|
+
}
|
|
357
|
+
|
|
330
358
|
async function publicFromPrivate(privateKey, opts) {
|
|
331
359
|
opts = guard.identifier.optionsObject(opts, _err, "key/bad-input", "pki.key.publicFromPrivate options");
|
|
332
360
|
guard.identifier.assertKnownKeys(opts, _PUBLIC_FROM_PRIVATE_OPTS, _err, "key/bad-input",
|
|
333
361
|
"pki.key.publicFromPrivate has an unknown option: ");
|
|
334
362
|
var der = await _toPrivateKeyDer(privateKey);
|
|
335
363
|
var spki;
|
|
364
|
+
/** @internal A composite ML-KEM key is a toolkit-defined algorithm the runtime decoder cannot read,
|
|
365
|
+
* so it is routed before the decoder rather than after it fails. Its own module owns the split and
|
|
366
|
+
* the wipe, and reports its own typed reasons. */
|
|
367
|
+
var compositeOid = _compositeKemOidOf(der);
|
|
368
|
+
if (compositeOid !== null) {
|
|
369
|
+
spki = await compositeKem.publicFromPrivate(der);
|
|
370
|
+
return opts.pem ? pkix.pemEncode(spki, "PUBLIC KEY", PemError) : spki;
|
|
371
|
+
}
|
|
336
372
|
try {
|
|
337
373
|
var priv = nodeCrypto.createPrivateKey({ key: der, format: "der", type: "pkcs8" });
|
|
338
374
|
spki = nodeCrypto.createPublicKey(priv).export({ format: "der", type: "spki" });
|
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:258eb315-4dba-4e0f-8600-313c47a1a144",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-07T16:58:48.291Z",
|
|
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.6.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.6.53",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.6.
|
|
25
|
+
"version": "0.6.53",
|
|
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.6.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.6.53",
|
|
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.6.
|
|
57
|
+
"ref": "@blamejs/pki@0.6.53",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|