@blamejs/pki 0.2.7 → 0.2.8

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 CHANGED
@@ -4,6 +4,15 @@ 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.2.8 — 2026-07-13
8
+
9
+ pki.webcrypto rejects an AES key of invalid length at import instead of deferring the failure to first use.
10
+
11
+ ### Fixed
12
+
13
+ - pki.webcrypto.subtle.importKey now rejects a raw or JWK AES key whose length is not 128, 192, or 256 bits as a webcrypto/data DataError at import, rather than returning a CryptoKey that only fails at first use. Covers AES-GCM, AES-CBC, AES-CTR, and AES-KW; HMAC, HKDF, and PBKDF2 keys are unaffected.
14
+ - The README capability table and the documentation site render the certificate-inspection entry correctly: an inline code span containing pipe characters no longer breaks the surrounding table into misaligned columns.
15
+
7
16
  ## v0.2.7 — 2026-07-13
8
17
 
9
18
  pki.webcrypto rejects an imported key whose type disagrees with its algorithm, and reports every cipher fault as a typed error.
package/README.md CHANGED
@@ -227,7 +227,7 @@ is callable today; nothing below is a stub.
227
227
  | `pki.shbs` | Stateful hash-based signature **verification** — HSS/LMS (RFC 8554), carried in X.509 by RFC 9802 and CMS by RFC 9708, profiled by NIST SP 800-208 (CNSA 2.0 firmware signing). `verify` checks an HSS signature (every level must pass) and `verifyLms` a single-tree LMS, over the raw public-key / signature blobs the parsers already surface. Pure public-input SHA-256 / SHAKE256 hashing, a data-driven typecode registry, bounds-before-slice reads; a malformed blob throws a typed `ShbsError`, a well-formed-but-wrong signature returns `false`. **Verify only by design** — stateful signing needs atomic one-time-key state that belongs in an HSM — `verify`, `verifyLms` |
228
228
  | `pki.hpke` | Hybrid Public Key Encryption (RFC 9180) — the encrypt-to-a-public-key primitive behind TLS ECH, MLS, and OHTTP. `setupS`/`setupR` establish a sender/recipient context (KEM encapsulation + HKDF key schedule); the context's `seal`/`open` AEAD-encrypt with a sequence-counter nonce and `export` derives further secrets; `seal`/`open` are single-shot wrappers. DHKEM (P-256, P-521, X25519, X448) × HKDF-SHA256/SHA512 × AES-GCM/ChaCha20Poly1305/export-only × all four modes, proven against the RFC 9180 Appendix A vectors. DHKEM(P-384) and HKDF-SHA384 are RFC-registered but Appendix A ships no vector for them, so they fail closed until an authoritative KAT exists. Pure composition over `node:crypto`; ML-KEM / X-Wing are a registry data-row extension pending stable drafts — `suites`, `setupS`, `setupR`, `seal`, `open` |
229
229
  | `pki.sigstore` | Offline verifier for a Sigstore bundle — the exact artifact `npm publish --provenance` produces and the registry serves. `verifyBundle` composes five fail-closed legs against caller-pinned trust (Fulcio CA roots + Rekor log keys, never trusted from the bundle): the DSSE signature over its PAE preimage under the Fulcio leaf key; the ephemeral Fulcio certificate chain, validated as of the Rekor log time; the RFC 9162 inclusion proof folded to a Rekor-signed tree root; the log entry bound to this exact signature; and the in-toto SLSA subject digest the caller confirms against the published artifact. Zero runtime deps — reuses the X.509 parser, RFC 5280 path validator, and Merkle verifier; the net-new codecs are the DSSE PAE byte-builder and a fail-closed JSON reader. `pae`, `parseBundle`, `verifyBundle` |
230
- | `pki.inspect` | Human-readable certificate inspection — the pure-JS equivalent of `openssl x509 -text`. `certificate(pem | der | parsed)` renders a familiar OpenSSL-style report: version, serial, signature algorithm, issuer/subject distinguished names, validity, public-key details (curve or modulus size + the raw point/modulus), every decoded extension with its critical flag, and the signature. Built over the strict parser and the two-way OID registry, so it names extension and algorithm OIDs an OpenSSL build shows only as raw bytes. No OpenSSL dependency; the format is stable and OpenSSL-familiar rather than pinned to one OpenSSL version; a malformed extension falls back to a hex dump rather than throwing — `certificate` |
230
+ | `pki.inspect` | Human-readable certificate inspection — the pure-JS equivalent of `openssl x509 -text`. `certificate(pem \| der \| parsed)` renders a familiar OpenSSL-style report: version, serial, signature algorithm, issuer/subject distinguished names, validity, public-key details (curve or modulus size + the raw point/modulus), every decoded extension with its critical flag, and the signature. Built over the strict parser and the two-way OID registry, so it names extension and algorithm OIDs an OpenSSL build shows only as raw bytes. No OpenSSL dependency; the format is stable and OpenSSL-familiar rather than pinned to one OpenSSL version; a malformed extension falls back to a hex dump rather than throwing — `certificate` |
231
231
  | `pki.webauthn` | WebAuthn / passkey attestation verification — offline trust evaluation of a W3C WebAuthn (Level 3) attestation. `parseAttestationObject(bytes)` decodes the CBOR attestation object + authenticatorData + COSE credential key over the strict `pki.cbor` codec; `verify(attestationObject, clientDataHash, opts)` checks the attestation-statement signature and each format's structural bindings for **packed / tpm / android-key / apple / fido-u2f / none** — the x5c leaf key, the apple nonce, the tpm `certInfo` Name/`extraData` over the `pubArea`, the android `KeyDescription`, the fido-u2f `verificationData` — binding the credential public key to each attestation (via the signed authenticatorData for packed/fido-u2f, or a cert/`pubArea`-key equality check for android-key/apple/tpm) and enforcing each leaf's certificate requirements. The credential-key check covers the full WebAuthn COSE algorithm set — ES256/384/512, RS256/384/512, PS256, EdDSA (Ed25519), and the RFC 9864 fully-specified identifiers **ESP256/384/512, Ed25519, and Ed448** — validating the public-key point on its curve, rejecting the compressed EC point form, and enforcing a minimally-encoded DER ECDSA signature. A verifier, not a ceremony client; fail-closed with typed `webauthn/*` errors. Chaining the returned trust path to a pinned root (and aaguid→root via FIDO MDS) is the caller's step through `pki.path.validate` — `parseAttestationObject`, `verify` |
232
232
  | `pki.C` / `pki.constants` | Version-stable constants — functional scale helpers (`C.TIME.*`, `C.BYTES.*`), codec `LIMITS`, `version` |
233
233
  | `pki.errors` | The `PkiError` taxonomy — `defineClass` plus `ConstantsError` / `Asn1Error` / `OidError` / `PemError` / `CertificateError` / `CrlError` / `CsrError` / `Pkcs8Error` / `CmsError` / `OcspError` / `TspError` / `AttrCertError` / `CrmfError` / `Pkcs12Error` / `CmpError` / `PathError` / `CtError` / `JoseError` / `AcmeError` / `WebauthnError`, each carrying a stable `code` in `domain/reason` form |
package/lib/webcrypto.js CHANGED
@@ -621,6 +621,17 @@ function _cloneWithUsage(key, usage) {
621
621
  return k;
622
622
  }
623
623
 
624
+ // A raw or JWK-oct AES key MUST be 128/192/256 bits: W3C WebCrypto importKey rejects a
625
+ // non-conforming length as a DataError AT IMPORT, never a CryptoKey that only faults at
626
+ // first use (where node would build an "aes-160-gcm" cipher that fails only then). HMAC /
627
+ // HKDF / PBKDF2 keys carry no length restriction, so the check is scoped to the AES names.
628
+ function _assertAesImportLen(name, byteLen) {
629
+ if ((name === "AES-GCM" || name === "AES-CBC" || name === "AES-CTR" || name === "AES-KW") &&
630
+ byteLen !== 16 && byteLen !== 24 && byteLen !== 32) {
631
+ throw new WebCryptoError("webcrypto/data", name + ": an imported AES key must be 128, 192, or 256 bits");
632
+ }
633
+ }
634
+
624
635
  SubtleCrypto.prototype.importKey = async function importKey(format, keyData, algorithm, extractable, keyUsages) {
625
636
  var alg = _normalizeAlg(algorithm, "importKey");
626
637
  var usages = keyUsages || [];
@@ -630,6 +641,7 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
630
641
  // Symmetric raw material, or a raw public key for EC/OKP.
631
642
  if (name === "AES-GCM" || name === "AES-CBC" || name === "AES-CTR" || name === "AES-KW" || name === "HMAC" || name === "HKDF" || name === "PBKDF2") {
632
643
  var raw = _toBuf(keyData, "importKey raw");
644
+ _assertAesImportLen(name, raw.length);
633
645
  var secret = nodeCrypto.createSecretKey(raw);
634
646
  var symAlg = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash), length: raw.length * 8 } : { name: name, length: raw.length * 8 };
635
647
  return new CryptoKey("secret", extractable, symAlg, usages, secret);
@@ -650,6 +662,7 @@ SubtleCrypto.prototype.importKey = async function importKey(format, keyData, alg
650
662
  var jwk = keyData;
651
663
  if (jwk.kty === "oct") {
652
664
  var kbuf = _b64urlToBuf(jwk.k, "JWK oct key material");
665
+ _assertAesImportLen(name, kbuf.length);
653
666
  var s2 = nodeCrypto.createSecretKey(kbuf);
654
667
  var a2 = (name === "HMAC") ? { name: name, hash: _hashObj(alg.hash), length: kbuf.length * 8 } : { name: name, length: kbuf.length * 8 };
655
668
  return new CryptoKey("secret", extractable, a2, usages, s2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
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",
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:ea60e88f-9354-49c4-aa35-ad6712102de1",
5
+ "serialNumber": "urn:uuid:fb3ea0b4-6f7b-459d-b24d-19dcdee7f0a8",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-07-13T07:29:25.087Z",
8
+ "timestamp": "2026-07-13T08:58:47.895Z",
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.7",
22
+ "bom-ref": "@blamejs/pki@0.2.8",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.2.7",
25
+ "version": "0.2.8",
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.7",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.2.8",
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.7",
57
+ "ref": "@blamejs/pki@0.2.8",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]