@blamejs/pki 0.8.25 → 0.8.27
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 +54 -0
- package/README.md +2 -2
- package/lib/attrcert-sign.js +26 -5
- package/lib/cmp-verify.js +16 -2
- package/lib/cms-decrypt.js +8 -2
- package/lib/cms-sign.js +41 -4
- package/lib/composite-sig.js +5 -0
- package/lib/constants.js +1 -0
- package/lib/guard-name.js +147 -7
- package/lib/http-retry-after.js +28 -7
- package/lib/inspect.js +8 -7
- package/lib/lint.js +1526 -35
- package/lib/oid.js +10 -0
- package/lib/path-validate.js +8 -1
- package/lib/rfc3339.js +12 -5
- package/lib/schema-attrcert.js +39 -9
- package/lib/schema-cms.js +23 -0
- package/lib/schema-tsp.js +82 -0
- package/package.json +2 -2
- package/sbom.cdx.json +6 -6
package/lib/oid.js
CHANGED
|
@@ -608,6 +608,16 @@ var _PARAMS_ABSENT = new Set();
|
|
|
608
608
|
"Ed25519", "Ed448", "X25519", "X448",
|
|
609
609
|
"id-ml-kem-512", "id-ml-kem-768", "id-ml-kem-1024",
|
|
610
610
|
"hkdfWithSha256", "hkdfWithSha384", "hkdfWithSha512",
|
|
611
|
+
/** @internal RFC 3279 sec. 2.2.3: "When the ecdsa-with-SHA1 algorithm identifier appears as the
|
|
612
|
+
* algorithm field in an AlgorithmIdentifier, the encoding MUST omit the parameters field", and
|
|
613
|
+
* sec. 2.2.2 says SHALL for id-dsa-with-sha1. RFC 5758 sec. 3.2 repeats it for the SHA-2 set: "the
|
|
614
|
+
* encoding MUST omit the parameters field". Unlike the `*WithRSAEncryption` identifiers, which
|
|
615
|
+
* RFC 4055 requires to CARRY a NULL, these omit the field entirely. */
|
|
616
|
+
"ecdsaWithSHA1", "ecdsaWithSHA256", "ecdsaWithSHA384", "ecdsaWithSHA512", "dsaWithSha1",
|
|
617
|
+
/** @internal RFC 8702 sec. 2: "when using the id-shake128 or id-shake256 algorithm identifiers, the
|
|
618
|
+
* parameters MUST be absent", sec. 3.1 for the CMS digest fields, and sec. 3.2 for the two
|
|
619
|
+
* RSASSA-PSS-SHAKE signature identifiers. */
|
|
620
|
+
"shake128", "shake256", "id-RSASSA-PSS-SHAKE128", "id-RSASSA-PSS-SHAKE256",
|
|
611
621
|
"id-alg-hss-lms-hashsig", "id-alg-xmss-hashsig", "id-alg-xmssmt-hashsig",
|
|
612
622
|
"id-MLDSA44-RSA2048-PSS-SHA256", "id-MLDSA44-RSA2048-PKCS15-SHA256",
|
|
613
623
|
"id-MLDSA44-Ed25519-SHA512", "id-MLDSA44-ECDSA-P256-SHA256",
|
package/lib/path-validate.js
CHANGED
|
@@ -458,7 +458,14 @@ function emailMatch(constraint, mailbox) {
|
|
|
458
458
|
if (constraint.indexOf("@") !== -1) {
|
|
459
459
|
var cb = splitMailbox(constraint);
|
|
460
460
|
if (cb === "ambiguous" || cb === null || mb === null) return "unsupported";
|
|
461
|
-
|
|
461
|
+
/** @internal A constraint naming a whole mailbox asks the RFC 5280 sec. 7.5 question, so it is
|
|
462
|
+
* asked of the one place that holds that rule. A trailing dot on either domain is removed first,
|
|
463
|
+
* since sec. 4.2.1.10 compares a host and `a.example` and `a.example.` are one host. */
|
|
464
|
+
var verdict = guard.name.emailEqual(mb[0] + "@" + stripTrailingDot(mb[1]),
|
|
465
|
+
cb[0] + "@" + stripTrailingDot(cb[1]));
|
|
466
|
+
if (verdict === "match") return true;
|
|
467
|
+
if (verdict === "no-match") return false;
|
|
468
|
+
return "unsupported";
|
|
462
469
|
}
|
|
463
470
|
if (mb === null) return "unsupported";
|
|
464
471
|
var host = stripTrailingDot(mb[1].toLowerCase());
|
package/lib/rfc3339.js
CHANGED
|
@@ -6,11 +6,18 @@
|
|
|
6
6
|
* lib/rfc3339.js: RFC 3339 date-time parsing + validation, a fail-closed engine primitive shared by
|
|
7
7
|
* pki.acme (a boolean field-validator over server-supplied JSON) and pki.ct (parse a CT log-list
|
|
8
8
|
* timestamp / temporal-interval bound to a comparable Date). Beyond the grammar it enforces CALENDAR
|
|
9
|
-
* validity: a syntactically well-formed but impossible instant
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* validity: a syntactically well-formed but impossible instant is rejected, so a downstream expiry or
|
|
10
|
+
* window comparison never runs on a value JS `Date` reads as a different instant.
|
|
11
|
+
* `2026-02-30T00:00:00Z` is the case that earns the check its keep: `new Date` reads it as
|
|
12
|
+
* `2026-03-02T00:00:00.000Z`, two days forward, and returns that instant without complaint. The
|
|
13
|
+
* runtime refuses month 13, hour 25 and a `+25:00` offset on its own, so the check agrees with it
|
|
14
|
+
* there and adds nothing.
|
|
15
|
+
* A `:60` second is well-formed: RFC 3339 sec. 5.7 permits `time-second` to be 60 at the end of a
|
|
16
|
+
* month in which a leap second occurs. Refusing it here is a decision, taken because JS cannot
|
|
17
|
+
* represent a leap second, so a caller that accepted one would hold either an invalid Date or an
|
|
18
|
+
* instant one second off; a typed error says so instead. Like the byte-reader / guard family, `parse`
|
|
19
|
+
* takes the caller's `(code, message) -> error` factory so every consumer keeps its own domain fault
|
|
20
|
+
* code.
|
|
14
21
|
*/
|
|
15
22
|
|
|
16
23
|
function _digitsAt(v, start, count) {
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -119,9 +119,30 @@ var HOLDER = schema.seq([
|
|
|
119
119
|
], { minTag: 0, maxTag: 2, unexpectedCode: "attrcert/bad-holder", orderCode: "attrcert/bad-holder" }),
|
|
120
120
|
], {
|
|
121
121
|
assert: "sequence", code: "attrcert/bad-holder", what: "Holder",
|
|
122
|
-
|
|
122
|
+
/** @internal Sec. 4.2.2 states three requirements for the holder's baseCertificateID issuer that
|
|
123
|
+
* sec. 4.2.3 states in the same words for the AC issuer: "The PKC issuer MUST have a non-empty
|
|
124
|
+
* distinguished name that is to be present as the single value of the
|
|
125
|
+
* holder.baseCertificateID.issuer construct in the directoryName field." V2_FORM's build enforces
|
|
126
|
+
* the AC issuer's copy, so the holder's is enforced here. It is checked in THIS build rather than
|
|
127
|
+
* in ISSUER_SERIAL, because that schema also reads a targetInformation targetCertificate, which
|
|
128
|
+
* sec. 4.2.2 says nothing about. */
|
|
129
|
+
build: function (m, ctx) {
|
|
130
|
+
var base = m.fields.baseCertificateID.present ? m.fields.baseCertificateID.value.result : null;
|
|
131
|
+
if (base !== null) {
|
|
132
|
+
var names = base.issuer.names;
|
|
133
|
+
if (names.length !== 1) {
|
|
134
|
+
throw ctx.E("attrcert/bad-holder", "the baseCertificateID issuer must contain one and only one GeneralName (RFC 5755 sec. 4.2.2)");
|
|
135
|
+
}
|
|
136
|
+
if (names[0].tagNumber !== 4) {
|
|
137
|
+
throw ctx.E("attrcert/bad-holder", "the baseCertificateID issuer GeneralName must be a directoryName [4] (RFC 5755 sec. 4.2.2)");
|
|
138
|
+
}
|
|
139
|
+
var dirName = pkix.decodeNested(names[0].bytes, ctx, "the baseCertificateID issuer directoryName").children[0];
|
|
140
|
+
if (!dirName.children || dirName.children.length < 1) {
|
|
141
|
+
throw ctx.E("attrcert/bad-holder", "the baseCertificateID issuer directoryName must be a non-empty distinguished name (RFC 5755 sec. 4.2.2)");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
123
144
|
return {
|
|
124
|
-
baseCertificateID:
|
|
145
|
+
baseCertificateID: base,
|
|
125
146
|
entityName: m.fields.entityName.present ? m.fields.entityName.value.result : null,
|
|
126
147
|
objectDigestInfo: m.fields.objectDigestInfo.present ? m.fields.objectDigestInfo.value.result : null,
|
|
127
148
|
};
|
|
@@ -605,18 +626,27 @@ function validateExtensionValue(extnOid, extnValueDer) {
|
|
|
605
626
|
}
|
|
606
627
|
|
|
607
628
|
/** @internal The criticality RFC 5755 fixes for each extension it profiles for an attribute
|
|
608
|
-
* certificate.
|
|
609
|
-
*
|
|
629
|
+
* certificate: sec. 4.3's six, and the ProxyInfo whose criticality sec. 7.2 fixes the same way. The
|
|
630
|
+
* AC signer refuses to write an extension against this table, the extension door reports one that
|
|
631
|
+
* arrived against it, and the RFC 5755 lint profile takes its own view of it, from this one row set.
|
|
632
|
+
* Each row carries the clause number and the citation is rendered from it, so a reader cannot be
|
|
633
|
+
* given a clause that names a different section than the rule it states.
|
|
634
|
+
* An extension whose clause says MAY has no row, because a MAY fixes no criticality. AAControls is
|
|
635
|
+
* that case: sec. 7.4 states "criticality MAY be TRUE" and places it in "CA and AC issuer PKCs",
|
|
636
|
+
* so for an attribute certificate it is one of the extensions sec. 4.2.9 governs, where a
|
|
637
|
+
* non-critical one conforms and a critical one does not. That is profile membership, which the lint
|
|
638
|
+
* profile answers from the keys of this table, and not a criticality to enforce here. */
|
|
610
639
|
var AC_FIXED_CRITICALITY_ROWS = [
|
|
611
|
-
["acAuditIdentity", true, "
|
|
612
|
-
["authorityKeyIdentifier", false, "
|
|
613
|
-
["cRLDistributionPoints", false, "
|
|
614
|
-
["acProxying", true, "
|
|
640
|
+
["acAuditIdentity", true, "4.3.1"], ["targetInformation", true, "4.3.2"],
|
|
641
|
+
["authorityKeyIdentifier", false, "4.3.3"], ["authorityInfoAccess", false, "4.3.4"],
|
|
642
|
+
["cRLDistributionPoints", false, "4.3.5"], ["noRevAvail", false, "4.3.6"],
|
|
643
|
+
["acProxying", true, "7.2"],
|
|
615
644
|
];
|
|
616
645
|
function extensionCriticality(oidRegistry) {
|
|
617
646
|
var table = intrinsic.create(null);
|
|
618
647
|
intrinsic.forEach(AC_FIXED_CRITICALITY_ROWS, function (r) {
|
|
619
|
-
table[oidRegistry.byName(r[0])] =
|
|
648
|
+
table[oidRegistry.byName(r[0])] =
|
|
649
|
+
{ name: r[0], critical: r[1], clause: r[2], citation: "RFC 5755 sec. " + r[2] };
|
|
620
650
|
});
|
|
621
651
|
return table;
|
|
622
652
|
}
|
package/lib/schema-cms.js
CHANGED
|
@@ -1092,8 +1092,31 @@ function parseCertsOnly(der, E, prefix, maxCerts, requireCert) {
|
|
|
1092
1092
|
};
|
|
1093
1093
|
}
|
|
1094
1094
|
|
|
1095
|
+
/** @internal The DER encoding of ASN.1 NULL, which RFC 5754 sec. 2 makes interchangeable with absent
|
|
1096
|
+
* parameters on a digest AlgorithmIdentifier: "Implementations MUST accept SHA2 AlgorithmIdentifiers
|
|
1097
|
+
* with absent parameters. Implementations MUST accept SHA2 AlgorithmIdentifiers with NULL
|
|
1098
|
+
* parameters." */
|
|
1099
|
+
var DIGEST_DER_NULL = Buffer.from([0x05, 0x00]);
|
|
1100
|
+
function digestParamsAbsentOrNull(p) {
|
|
1101
|
+
if (p === null || p === undefined) return true;
|
|
1102
|
+
return Buffer.isBuffer(p) && p.equals(DIGEST_DER_NULL);
|
|
1103
|
+
}
|
|
1104
|
+
/** @internal Whether two digest AlgorithmIdentifiers name one algorithm, under the rule above. Two
|
|
1105
|
+
* identifiers carrying the same OID and different parameters are different digests, so the
|
|
1106
|
+
* parameters are read rather than ignored; absent and an explicit NULL are the one exception the
|
|
1107
|
+
* clause grants. Every place that compares or LISTS a digest answers from here, so a set cannot be
|
|
1108
|
+
* written under one rule and read under another. */
|
|
1109
|
+
function sameDigestAlgorithm(a, c) {
|
|
1110
|
+
if (!a || !c || a.oid !== c.oid) return false;
|
|
1111
|
+
if (digestParamsAbsentOrNull(a.parameters)) return digestParamsAbsentOrNull(c.parameters);
|
|
1112
|
+
if (digestParamsAbsentOrNull(c.parameters)) return false;
|
|
1113
|
+
return Buffer.isBuffer(a.parameters) && Buffer.isBuffer(c.parameters) &&
|
|
1114
|
+
a.parameters.equals(c.parameters);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1095
1117
|
module.exports = {
|
|
1096
1118
|
parse: parse,
|
|
1119
|
+
sameDigestAlgorithm: sameDigestAlgorithm, // @internal
|
|
1097
1120
|
pemDecode: pemDecode,
|
|
1098
1121
|
pemEncode: pemEncode,
|
|
1099
1122
|
matches: matches,
|
package/lib/schema-tsp.js
CHANGED
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
* inputs, single-signer token composition over CMS, fail-closed.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
+
var nodeCrypto = require("crypto");
|
|
37
38
|
var asn1 = require("./asn1-der");
|
|
38
39
|
var schema = require("./schema-engine");
|
|
39
40
|
var pkix = require("./schema-pkix");
|
|
40
41
|
var oid = require("./oid");
|
|
42
|
+
var smime = require("./schema-smime");
|
|
41
43
|
var cms = require("./schema-cms");
|
|
42
44
|
var frameworkError = require("./framework-error");
|
|
43
45
|
|
|
@@ -500,6 +502,85 @@ function matches(root) {
|
|
|
500
502
|
return true;
|
|
501
503
|
}
|
|
502
504
|
|
|
505
|
+
/** @internal The output length RFC 8702 sec. 2 fixes for each XOF where it names a message digest.
|
|
506
|
+
* A certHash of any other length is not that digest, and the length comparison below refuses it. */
|
|
507
|
+
var ESS_XOF_BYTES = Object.assign(Object.create(null), { shake128: 32, shake256: 64 });
|
|
508
|
+
|
|
509
|
+
/** @internal Which digest an ESS certHash names. RFC 5035 sec. 4's ESSCertIDv2 carries a hashAlgorithm
|
|
510
|
+
* AlgorithmIdentifier and fixes no set of algorithms for it, and RFC 2634 sec. 5.4's ESSCertID carries
|
|
511
|
+
* no such field at all, where SHA-1 is implied. Both attributes satisfy RFC 3161 sec. 2.4.2, so both
|
|
512
|
+
* are read here, and each name comes from the OID registry rather than the token, so the registry is
|
|
513
|
+
* the set of names this is asked about. A hand-kept table of four names left the signer of a token
|
|
514
|
+
* naming any other registered digest unresolved and every row that reads the signer certificate
|
|
515
|
+
* silent, so the question asked is whether this build computes the digest.
|
|
516
|
+
* The digest answers WHICH of the certificates a token already carries the token says signed it,
|
|
517
|
+
* which is a different question from whether that binding is strong enough to trust: a sender who
|
|
518
|
+
* could find a collision here also chose the certificates it would collide, and `pki.tsp.verify`
|
|
519
|
+
* keeps its own narrower table for the binding it accepts. */
|
|
520
|
+
var ESS_NODE_DIGESTS = Object.create(null);
|
|
521
|
+
nodeCrypto.getHashes().forEach(function (n) { ESS_NODE_DIGESTS[n] = true; });
|
|
522
|
+
function essDigest(name) {
|
|
523
|
+
if (typeof name !== "string" || ESS_NODE_DIGESTS[name] !== true) return null;
|
|
524
|
+
var xof = ESS_XOF_BYTES[name];
|
|
525
|
+
/** @internal One read of the name answers both whether the digest can be computed and how to
|
|
526
|
+
* compute it, so the hash every candidate is put through is the one that read settled. */
|
|
527
|
+
return function () {
|
|
528
|
+
return xof ? nodeCrypto.createHash(name, { outputLength: xof }) : nodeCrypto.createHash(name);
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** @internal Which of the certificates a token embeds is the one that signed it. RFC 3161 sec. 2.4.2
|
|
533
|
+
* requires the ESS SigningCertificate or SigningCertificateV2 signed attribute and `parseToken`
|
|
534
|
+
* refuses a token carrying neither, so the certHash is present, and it names exactly one
|
|
535
|
+
* certificate. The SignerInfo sid names no certificate uniquely: an issuer with a serial, and a
|
|
536
|
+
* subject key identifier, are both values an issuer wrote, two certificates can carry either, and
|
|
537
|
+
* the `certificates` SET is outside the signature, so a reader that takes the first sid match
|
|
538
|
+
* answers from an order the sender chose. Returns the signer's DER, or null when no embedded
|
|
539
|
+
* certificate hashes to the ESS value, which is the answer a token supplying its certificate out of
|
|
540
|
+
* band gets, and when two attributes name two different embedded certificates. The hash is a public
|
|
541
|
+
* value and is compared by content. */
|
|
542
|
+
function signerCertificateDer(token) {
|
|
543
|
+
var attrs = (token.signerInfo && token.signerInfo.signedAttrs) || [];
|
|
544
|
+
if (!Array.isArray(attrs)) return null;
|
|
545
|
+
var found = null;
|
|
546
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
547
|
+
if (attrs[i].type !== OID_SIGNING_CERT_V2 && attrs[i].type !== OID_SIGNING_CERT) continue;
|
|
548
|
+
var named = _essNamedCertificate(token, attrs[i]);
|
|
549
|
+
if (named === null) continue;
|
|
550
|
+
/** @internal RFC 5035 sec. 7: the two attributes "are generated and evaluated independently" and
|
|
551
|
+
* "the contents could conceivably be in conflict", where a signer holds several certificates over
|
|
552
|
+
* one key. Two that name two different certificates leave which of them signed the token
|
|
553
|
+
* unsettled, so this answers with nothing and the caller says so, rather than taking one of
|
|
554
|
+
* them. */
|
|
555
|
+
if (found !== null) return Buffer.compare(found, named) === 0 ? found : null;
|
|
556
|
+
found = named;
|
|
557
|
+
}
|
|
558
|
+
return found;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** @internal The certificate one ESS attribute names, or null when the token embeds none that hashes
|
|
562
|
+
* to its value. Each attribute is read on its own, so a token carrying both, which RFC 5035 sec. 7
|
|
563
|
+
* permits so that a legacy reader keeps its protection against certificate substitution, resolves
|
|
564
|
+
* from whichever of them names a certificate it carries. */
|
|
565
|
+
function _essNamedCertificate(token, attr) {
|
|
566
|
+
var decoded;
|
|
567
|
+
// allow:swallow-unverified no token reaches here with an unreadable ESS attribute, since parseToken refuses one as tsp/bad-token; this answers for a caller that resolves against a token it parsed elsewhere
|
|
568
|
+
try { decoded = smime.decodeAttribute(attr); } catch (_e) { return null; }
|
|
569
|
+
var essCert = decoded && decoded.certs && decoded.certs[0];
|
|
570
|
+
if (!essCert || !essCert.hashAlgorithm || !essCert.certHash) return null;
|
|
571
|
+
var digest = essDigest(essCert.hashAlgorithm.name);
|
|
572
|
+
if (digest === null) return null;
|
|
573
|
+
var want = Buffer.from(essCert.certHash);
|
|
574
|
+
var certs = Array.isArray(token.certificates) ? token.certificates : [];
|
|
575
|
+
for (var j = 0; j < certs.length; j++) {
|
|
576
|
+
var element = certs[j];
|
|
577
|
+
if (!element || element.tagClass !== "universal" || !Buffer.isBuffer(element.bytes)) continue;
|
|
578
|
+
var actual = digest().update(element.bytes).digest();
|
|
579
|
+
if (actual.length === want.length && Buffer.compare(actual, want) === 0) return element.bytes;
|
|
580
|
+
}
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
|
|
503
584
|
module.exports = {
|
|
504
585
|
parse: parse,
|
|
505
586
|
parseResponse: parse,
|
|
@@ -507,6 +588,7 @@ module.exports = {
|
|
|
507
588
|
decodeGeneralName: decodeGeneralName, // @internal
|
|
508
589
|
parseTstInfo: parseTstInfo,
|
|
509
590
|
parseToken: parseToken,
|
|
591
|
+
signerCertificateDer: signerCertificateDer, // @internal
|
|
510
592
|
pemDecode: pemDecode,
|
|
511
593
|
pemEncode: pemEncode,
|
|
512
594
|
matches: matches,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/pki",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.27",
|
|
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",
|
|
@@ -86,6 +86,6 @@
|
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"c8": "12.0.0",
|
|
88
88
|
"esbuild": "0.28.2",
|
|
89
|
-
"eslint": "10.
|
|
89
|
+
"eslint": "10.11.0"
|
|
90
90
|
}
|
|
91
91
|
}
|
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:0dc52d70-7641-471f-98fe-c06068d7838f",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-28T19:32:41.570Z",
|
|
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.27",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.27",
|
|
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.27",
|
|
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.27",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|