@blamejs/pki 0.6.50 → 0.6.51
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 +19 -0
- package/lib/cmp-session.js +7 -1
- package/lib/cmp-verify.js +120 -6
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ 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.51 — 2026-09-07
|
|
8
|
+
|
|
9
|
+
A CMP message that names one key and is protected by another no longer verifies.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- pki.cmp.verify checks the senderKID of a MAC-protected message against the commonName of its sender field, which is how such a message names the shared secret it was protected with (RFC 9483 section 4.1.5). A senderKID naming another entity, or a sender carrying no commonName to match, is refused with cmp/bad-sender-kid. An anonymous sender is the exception the base protocol names: where the sender is the NULL-DN a requester uses before it knows its own name, the senderKID is a reference number rather than a name (RFC 9810 section 5.1.1), so it is carried and not compared. A KEM-protected message's senderKID is likewise carried and not compared, since no specification says what it names there.
|
|
14
|
+
- pki.cmp.verify refuses an opts.time that is not a Date with cmp/bad-input, at the door, rather than letting whichever check reads it first throw a bare TypeError.
|
|
15
|
+
- pki.cmp.verify takes opts.messageTimeTolerance, the accepted messageTime skew in seconds. RFC 9483 section 3.5 requires a present messageTime to be close to the receiver's current time but leaves the threshold to the use case, so stating a window turns the check on and a message outside it in either direction is a cmp/bad-message-time verdict. A clock runs fast as readily as slow, so a message dated ahead of the receiver is judged the same as one dated behind it. Without the option the messageTime is not judged, and a message carrying none is unaffected.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- A caller that passes opts.signerCert for a message whose senderKID does not match that certificate now receives a cmp/bad-sender-kid verdict where the message previously verified. A sender that sets senderKID to its certificate's subjectKeyIdentifier, which is what the field is for, is unaffected.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- pki.cmp.verify holds a declared senderKID to the key that actually verified the protection, on every path (RFC 9483 section 3.5). A certificate passed as opts.signerCert is now checked against the header's senderKID exactly as one selected from extraCerts already was, so a message naming one key identifier and signed under another is refused with cmp/bad-sender-kid instead of verifying. A signer certificate carrying no subjectKeyIdentifier offers nothing for a declared identifier to name and cannot satisfy one; a message declaring no senderKID is unaffected, since the rule is conditioned on the field being present.
|
|
24
|
+
- A message whose extraCerts carry no certificate matching the declared senderKID reports cmp/bad-sender-kid rather than cmp/signer-cert-not-found. The two failures are different: nothing was offered, versus nothing offered carries the identifier the message declares. Reporting them apart keeps one defect from carrying two codes depending on how the certificate would have arrived. pki.cmp.session treats the new code the same way it treated the old one, so a transaction whose response declares a signer the response did not carry still falls back to the certificate the session already holds, and that certificate is put through the same identifier check.
|
|
25
|
+
|
|
7
26
|
## v0.6.50 — 2026-09-07
|
|
8
27
|
|
|
9
28
|
A client whose key can only establish secrets can now authenticate its CMP messages with that key.
|
package/lib/cmp-session.js
CHANGED
|
@@ -201,8 +201,14 @@ var CAPUBS_MAX = 2 * MAX_EXTRA_CERTS;
|
|
|
201
201
|
var SESSION_MAX_INTERMEDIATES = constants.LIMITS.PATH_BUILD_MAX_CANDIDATES - CAPUBS_MAX - MAX_EXTRA_CERTS;
|
|
202
202
|
var SESSION_MAX_INTERMEDIATES_MAC = constants.LIMITS.PATH_BUILD_MAX_CANDIDATES - CAPUBS_MAX;
|
|
203
203
|
function _isRecoverableVerify(code) {
|
|
204
|
+
/** @internal `cmp/bad-sender-kid` belongs here for the same reason `cmp/signer-cert-not-found` does:
|
|
205
|
+
* a response whose extraCerts carry no certificate matching the declared key identifier is one this
|
|
206
|
+
* session may already hold the signer for. Retrying is safe because the fallback certificate is put
|
|
207
|
+
* through the same identifier check, so the retry can only succeed on a certificate the message
|
|
208
|
+
* actually names. */
|
|
204
209
|
return code === "cmp/signer-cert-not-found" || code === "cmp/protection-failed" ||
|
|
205
|
-
code === "cmp/sender-mismatch" || code === "cmp/untrusted-signer"
|
|
210
|
+
code === "cmp/sender-mismatch" || code === "cmp/untrusted-signer" ||
|
|
211
|
+
code === "cmp/bad-sender-kid";
|
|
206
212
|
}
|
|
207
213
|
function _responseExtraCerts(responseBytes) {
|
|
208
214
|
var extra = schemaCmp.parse(responseBytes).extraCerts;
|
package/lib/cmp-verify.js
CHANGED
|
@@ -52,6 +52,7 @@ var _certExtDecoders = pkix.certExtensionDecoders(NS).byOid;
|
|
|
52
52
|
var KNOWN_VERIFY_OPTS = intrinsic.assign(intrinsic.create(null), {
|
|
53
53
|
sharedSecret: 1, signerCert: 1, trustAnchors: 1, intermediates: 1, time: 1,
|
|
54
54
|
transactionID: 1, expectRecipNonce: 1, revocationChecker: 1, maxIterations: 1, kem: 1,
|
|
55
|
+
messageTimeTolerance: 1,
|
|
55
56
|
});
|
|
56
57
|
var KNOWN_KEM_VERIFY_KEYS = intrinsic.assign(intrinsic.create(null),
|
|
57
58
|
{ sharedSecret: 1, transactionID: 1 });
|
|
@@ -146,6 +147,30 @@ function _sanGeneralNames(parsed) {
|
|
|
146
147
|
return [];
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
/** @internal An anonymous sender: the NULL-DN a requester uses when it knows nothing about its own
|
|
151
|
+
* name (RFC 9810 sec. 5.1.1), encoded as a directoryName with no relative names. */
|
|
152
|
+
function _isNullDnSender(sender) {
|
|
153
|
+
return !!sender && sender.tagClass === "context" && sender.tagNumber === 4 &&
|
|
154
|
+
!!sender.value && _isArray(sender.value.rdns) && sender.value.rdns.length === 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @internal The commonName of a sender given as a directoryName, which is the name a MAC-protected
|
|
158
|
+
* message identifies its shared secret by (RFC 9483 sec. 4.1.5). A sender in any other GeneralName
|
|
159
|
+
* form, or one carrying no commonName, names nothing a senderKID could equal. */
|
|
160
|
+
function _senderCommonName(sender) {
|
|
161
|
+
if (!sender || sender.tagClass !== "context" || sender.tagNumber !== 4) return null;
|
|
162
|
+
var rdns = sender.value && sender.value.rdns;
|
|
163
|
+
if (!_isArray(rdns)) return null;
|
|
164
|
+
var cnOid = oid.byName("commonName");
|
|
165
|
+
for (var i = 0; i < rdns.length; i++) {
|
|
166
|
+
var rdn = rdns[i];
|
|
167
|
+
for (var j = 0; j < rdn.length; j++) {
|
|
168
|
+
if (rdn[j].type === cnOid && typeof rdn[j].value === "string") return rdn[j].value;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
149
174
|
function _senderBoundToCert(sender, parsed) {
|
|
150
175
|
if (!sender || !sender.bytes) return false;
|
|
151
176
|
var subjectRdns = parsed.subject.rdns;
|
|
@@ -311,6 +336,15 @@ function _uriEqual(a, b) {
|
|
|
311
336
|
return na === nb;
|
|
312
337
|
}
|
|
313
338
|
|
|
339
|
+
/** @internal Whether a declared key identifier names this certificate. Absent means the rule does not
|
|
340
|
+
* apply; present with no subjectKeyIdentifier to compare means it cannot be satisfied, so the message
|
|
341
|
+
* is refused rather than admitted for want of a value. */
|
|
342
|
+
function _senderKidMatchesCert(senderKID, parsed) {
|
|
343
|
+
if (senderKID == null) return true;
|
|
344
|
+
var ski = _certSki(parsed);
|
|
345
|
+
return ski != null && guard.crypto.constantTimeEqual(ski, senderKID);
|
|
346
|
+
}
|
|
347
|
+
|
|
314
348
|
function _resolveSignerCert(m, opts, extra) {
|
|
315
349
|
var senderKID = m.header.senderKID;
|
|
316
350
|
function _signerObj(der, p) { return { der: der, spki: p.subjectPublicKeyInfo.bytes, subject: _subjectDn(p), parsed: p }; }
|
|
@@ -318,10 +352,7 @@ function _resolveSignerCert(m, opts, extra) {
|
|
|
318
352
|
var p;
|
|
319
353
|
try { p = x509.parse(der); }
|
|
320
354
|
catch (_e) { return null; }
|
|
321
|
-
if (senderKID
|
|
322
|
-
var ski = _certSki(p);
|
|
323
|
-
if (ski == null || !guard.crypto.constantTimeEqual(ski, senderKID)) return null;
|
|
324
|
-
}
|
|
355
|
+
if (!_senderKidMatchesCert(senderKID, p)) return null;
|
|
325
356
|
return _signerObj(der, p);
|
|
326
357
|
}
|
|
327
358
|
if (opts.signerCert != null) {
|
|
@@ -361,6 +392,22 @@ function _headerChecks(m, opts) {
|
|
|
361
392
|
if (opts.expectRecipNonce != null && !_bufEq(m.header.recipNonce, opts.expectRecipNonce)) {
|
|
362
393
|
return { code: "cmp/bad-recip-nonce", reason: "header.recipNonce does not echo the expected sender nonce" };
|
|
363
394
|
}
|
|
395
|
+
/** @internal A stated window is checked in both directions: a clock runs fast as readily as slow, and
|
|
396
|
+
* a message dated ahead of the receiver is as much a replay signal as one dated behind it. */
|
|
397
|
+
if (opts.messageTimeTolerance != null && m.header.messageTime != null) {
|
|
398
|
+
var at = guard.time.instantOf(opts.time != null ? opts.time : new _Date());
|
|
399
|
+
var sent = guard.time.instantOf(m.header.messageTime);
|
|
400
|
+
if (!intrinsic.isFinite(at) || !intrinsic.isFinite(sent)) {
|
|
401
|
+
return { code: "cmp/bad-message-time", reason: "the header messageTime could not be read as an instant to compare against the verification time (RFC 9483 sec. 3.5)" };
|
|
402
|
+
}
|
|
403
|
+
// allow:nan-date-comparison-unguarded -- both instants are finite by the check above
|
|
404
|
+
var skew = (at > sent ? at - sent : sent - at) / 1000;
|
|
405
|
+
if (skew > opts.messageTimeTolerance) {
|
|
406
|
+
return { code: "cmp/bad-message-time", reason: "the header messageTime is " + intrinsic.floor(skew) +
|
|
407
|
+
" seconds from the verification time, outside the " + opts.messageTimeTolerance +
|
|
408
|
+
"-second window this caller accepts (RFC 9483 sec. 3.5)" };
|
|
409
|
+
}
|
|
410
|
+
}
|
|
364
411
|
return null;
|
|
365
412
|
}
|
|
366
413
|
|
|
@@ -460,6 +507,23 @@ async function _verifyMac(m, protectedPart, protectionAlg, protection, opts) {
|
|
|
460
507
|
if (!guard.crypto.constantTimeEqual(computed, protection.bytes)) {
|
|
461
508
|
return _fail(m, "mac", protectionAlg, "cmp/protection-failed", "the PBMAC1 MAC does not verify (a wrong shared secret or a tampered ProtectedPart)");
|
|
462
509
|
}
|
|
510
|
+
/** @internal Under MAC protection the sender names the shared secret, and sec. 4.1.5 requires the
|
|
511
|
+
* senderKID to carry that same name: the identifier and the name the message is attributed to are
|
|
512
|
+
* one value, so a message pointing them at different entities is refused. */
|
|
513
|
+
/** @internal The name rule governs a sender that HAS a name. An anonymous NULL-DN sender carries a
|
|
514
|
+
* reference number instead (RFC 9810 sec. 5.1.1), which names no commonName to equal, so it is
|
|
515
|
+
* required to be present above and left uncompared here rather than refused for failing a match the
|
|
516
|
+
* base protocol does not ask of it. */
|
|
517
|
+
if (m.header.senderKID != null && !_isNullDnSender(m.header.sender)) {
|
|
518
|
+
var cn = _senderCommonName(m.header.sender);
|
|
519
|
+
if (cn === null || !guard.crypto.constantTimeEqual(_bufferFrom(cn, "utf8"), m.header.senderKID)) {
|
|
520
|
+
return _fail(m, "mac", protectionAlg, "cmp/bad-sender-kid",
|
|
521
|
+
"the header senderKID must carry the same name as the commonName of the sender field, which " +
|
|
522
|
+
"is how a MAC-protected message identifies the shared secret it was protected with " +
|
|
523
|
+
"(RFC 9483 sec. 4.1.5)");
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
463
527
|
var hc = _headerChecks(m, opts);
|
|
464
528
|
if (hc) return _fail(m, "mac", protectionAlg, hc.code, hc.reason);
|
|
465
529
|
return _ok(m, "mac", protectionAlg, true, null);
|
|
@@ -485,7 +549,17 @@ function _capWork(iterationCount, salt, keyLength, prfHash, opts) {
|
|
|
485
549
|
async function _verifySignature(m, protectedPart, protectionAlg, protection, opts) {
|
|
486
550
|
var extra = _boundExtraCerts(m.extraCerts);
|
|
487
551
|
var signer = _resolveSignerCert(m, opts, extra);
|
|
488
|
-
if (!signer)
|
|
552
|
+
if (!signer) {
|
|
553
|
+
/** @internal Selecting from `extraCerts` narrows by the declared key identifier, so "nothing
|
|
554
|
+
* matched" and "nothing was offered" are different failures. Reporting them apart keeps one
|
|
555
|
+
* defect from carrying two codes depending on how the certificate would have arrived. */
|
|
556
|
+
if (m.header.senderKID != null && extra.length) {
|
|
557
|
+
return _fail(m, "signature", protectionAlg, "cmp/bad-sender-kid",
|
|
558
|
+
"no certificate in extraCerts carries the subjectKeyIdentifier the header senderKID declares, " +
|
|
559
|
+
"so nothing offered identifies the key that would verify the protection (RFC 9483 sec. 3.5)");
|
|
560
|
+
}
|
|
561
|
+
return _fail(m, "signature", protectionAlg, "cmp/signer-cert-not-found", "no signer certificate resolved (opts.signerCert, senderKID, or extraCerts)");
|
|
562
|
+
}
|
|
489
563
|
|
|
490
564
|
var ok = await _engine.verifyWithSpki(protectionAlg, protection.bytes, signer.spki, protectedPart);
|
|
491
565
|
if (ok !== true) return _fail(m, "signature", protectionAlg, "cmp/protection-failed", "the protection signature does not verify over the ProtectedPart under the declared protectionAlg", signer);
|
|
@@ -493,6 +567,16 @@ async function _verifySignature(m, protectedPart, protectionAlg, protection, opt
|
|
|
493
567
|
if (!_senderBoundToCert(m.header.sender, signer.parsed)) {
|
|
494
568
|
return _fail(m, "signature", protectionAlg, "cmp/sender-mismatch", "the header sender field does not match the signer certificate subject, or (for an empty subject) a subjectAltName entry (RFC 9483 sec. 3.1, RFC 5280 sec. 7.1)", signer);
|
|
495
569
|
}
|
|
570
|
+
/** @internal The declared key identifier is checked against the certificate whose key ACTUALLY
|
|
571
|
+
* verified, on every route that certificate arrives by. Selecting from `extraCerts` already matches
|
|
572
|
+
* it while choosing, and a caller-supplied certificate reaches the same rule here, so a message
|
|
573
|
+
* naming one key and signed by another is refused however the verifier came by the certificate
|
|
574
|
+
* (RFC 9483 sec. 3.5). */
|
|
575
|
+
if (!_senderKidMatchesCert(m.header.senderKID, signer.parsed)) {
|
|
576
|
+
return _fail(m, "signature", protectionAlg, "cmp/bad-sender-kid",
|
|
577
|
+
"the header senderKID does not identify the certificate whose key verified the protection: it " +
|
|
578
|
+
"must equal that certificate's subjectKeyIdentifier (RFC 9483 sec. 3.5)", signer);
|
|
579
|
+
}
|
|
496
580
|
|
|
497
581
|
var hc = _headerChecks(m, opts);
|
|
498
582
|
if (hc) return _fail(m, "signature", protectionAlg, hc.code, hc.reason, signer);
|
|
@@ -640,6 +724,18 @@ function _fixSecret(v, made, label) {
|
|
|
640
724
|
return v;
|
|
641
725
|
}
|
|
642
726
|
|
|
727
|
+
/** @internal The freshness window, in seconds. RFC 9483 sec. 3.5 leaves the threshold to the use case
|
|
728
|
+
* and lists the check under local policy, so it is stated by the caller rather than defaulted: a
|
|
729
|
+
* window this library invented would refuse conforming messages from a peer whose clock is merely
|
|
730
|
+
* further off than that number. */
|
|
731
|
+
function _fixTolerance(v) {
|
|
732
|
+
if (v == null) return null;
|
|
733
|
+
if (typeof v !== "number" || !intrinsic.isFinite(v) || v < 0) {
|
|
734
|
+
throw _err("cmp/bad-input", "opts.messageTimeTolerance is the accepted messageTime skew in seconds, a non-negative number");
|
|
735
|
+
}
|
|
736
|
+
return v;
|
|
737
|
+
}
|
|
738
|
+
|
|
643
739
|
/** @internal The KEM verification material, taken once at the door like every other caller value: the
|
|
644
740
|
* shared secret is copied and registered for wiping, and the transaction identifier is snapshot, so
|
|
645
741
|
* neither can answer differently when the derivation reads it. */
|
|
@@ -675,8 +771,13 @@ function _fixCertList(v, label) {
|
|
|
675
771
|
for (var i = 0; i < n; i++) _append(out, _fixByteish(v[i], label + "[" + i + "]"));
|
|
676
772
|
return out;
|
|
677
773
|
}
|
|
774
|
+
/** @internal The verification instant, copied so a caller cannot move it after the fact. A value that
|
|
775
|
+
* is not a Date is refused here rather than carried into a comparison that would throw from whichever
|
|
776
|
+
* check happened to read it first. */
|
|
678
777
|
function _fixTime(v) {
|
|
679
|
-
|
|
778
|
+
if (v == null) return v;
|
|
779
|
+
if (!guard.time.isDate(v)) throw _err("cmp/bad-input", "opts.time must be a Date");
|
|
780
|
+
return new _Date(guard.time.instantOf(v));
|
|
680
781
|
}
|
|
681
782
|
function _fixVerifyOptions(opts, made) {
|
|
682
783
|
if (opts == null) opts = {};
|
|
@@ -693,6 +794,7 @@ function _fixVerifyOptions(opts, made) {
|
|
|
693
794
|
revocationChecker: opts.revocationChecker,
|
|
694
795
|
maxIterations: opts.maxIterations,
|
|
695
796
|
kem: _fixKem(opts.kem, made),
|
|
797
|
+
messageTimeTolerance: _fixTolerance(opts.messageTimeTolerance),
|
|
696
798
|
};
|
|
697
799
|
|
|
698
800
|
_assertEchoBytes(f.transactionID, "transactionID");
|
|
@@ -763,6 +865,14 @@ async function _verify(message, opts) {
|
|
|
763
865
|
* carry at least 128 bits; a violation is a `{ valid: false }` verdict carrying `cmp/unsupported-version`,
|
|
764
866
|
* `cmp/bad-transaction-id`, or `cmp/bad-sender-nonce`.
|
|
765
867
|
*
|
|
868
|
+
* A present `senderKID` must identify the key material that verified the protection (RFC 9483
|
|
869
|
+
* sec. 3.5), whichever way that key was resolved. Under signature protection it must equal the
|
|
870
|
+
* `subjectKeyIdentifier` of the certificate whose key verified, so a certificate supplied as
|
|
871
|
+
* `opts.signerCert` is held to the declared identifier exactly as one selected out of `extraCerts` is,
|
|
872
|
+
* and a certificate carrying no `subjectKeyIdentifier` cannot satisfy one. Under MAC protection it must
|
|
873
|
+
* carry the same name as the `commonName` of the sender field, which is how such a message names the
|
|
874
|
+
* shared secret it was protected with (sec. 4.1.5). A mismatch is a `cmp/bad-sender-kid` verdict.
|
|
875
|
+
*
|
|
766
876
|
* Returns a verdict (never a bare boolean): `{ valid, trusted, protectionType, protectionAlg, signer,
|
|
767
877
|
* transactionID, senderNonce, recipNonce, header, body, code?, reason? }`. `valid` is whether the
|
|
768
878
|
* protection is cryptographically intact under the declared algorithm; `trusted` is whether a MAC secret
|
|
@@ -775,6 +885,10 @@ async function _verify(message, opts) {
|
|
|
775
885
|
*
|
|
776
886
|
* @opts
|
|
777
887
|
* - `sharedSecret` (string|Buffer) -- the PBMAC1 secret; REQUIRED for a MAC-protected message (UTF-8).
|
|
888
|
+
* - `messageTimeTolerance` (number) -- the accepted `messageTime` skew in seconds. RFC 9483
|
|
889
|
+
* sec. 3.5 requires a present `messageTime` to be close to the receiver's current time but leaves
|
|
890
|
+
* the threshold to the use case, so stating it turns the check on; a message outside the window in
|
|
891
|
+
* either direction is a `cmp/bad-message-time` verdict. Absent, the `messageTime` is not judged.
|
|
778
892
|
* - `kem` ({ sharedSecret, transactionID? }) -- REQUIRED for a KEM-protected message (RFC 9810
|
|
779
893
|
* sec. 5.1.3.4). `sharedSecret` is what this side encapsulated to the sender's KEM public key, and
|
|
780
894
|
* `transactionID` names the message that carried the ciphertext when it was not this one. The
|
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:d9015ac6-f8a1-409f-9e1f-5ae1fc7491a1",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-07T06:27:34.397Z",
|
|
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.51",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.6.
|
|
25
|
+
"version": "0.6.51",
|
|
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.51",
|
|
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.51",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|