kxco-post-quantum 1.7.0 → 1.7.2

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/AGILITY.md ADDED
@@ -0,0 +1,115 @@
1
+ # Cryptographic agility
2
+
3
+ What has to change, and what does not, when the algorithm changes.
4
+
5
+ "Crypto-agility" is used for three different things: a plan to replace an
6
+ algorithm, a mechanism that performs the replacement, and a transition that
7
+ peers can follow without breaking. They are not the same claim and this
8
+ document keeps them apart. Where the answer is "we do not have that", it says
9
+ so.
10
+
11
+ ## 1. A replacement plan
12
+
13
+ [MIGRATION.md](MIGRATION.md) carries the plan, in four stages: verify both,
14
+ sign both, require both, drop the classical signature. The rule it exists to
15
+ enforce is *do not swap, add then remove*, because a one-release swap strands
16
+ everything signed before it and every peer you do not control.
17
+
18
+ The same shape covers a move between post-quantum parameter sets, with one
19
+ difference: there is no classical side to retire, so stage 4 is reached as soon
20
+ as every consumer verifies the new set.
21
+
22
+ For key establishment the plan is not optional in the same way. Combine the
23
+ ML-KEM shared secret with a classical secret through a KDF, so the session
24
+ holds if either does. `deriveSeed` is what that HKDF step is for.
25
+
26
+ ## 2. An implemented mechanism
27
+
28
+ Three mechanisms exist in this package today.
29
+
30
+ **Parameter set selection.** Five sets are published helpers with their own
31
+ entry points: ML-DSA-65, ML-DSA-87, ML-KEM-768, ML-KEM-1024 and
32
+ SLH-DSA-SHA2-192s. Category 5 is not a future item; `mlDsa87` and `mlKem1024`
33
+ ship today. Changing set is an import change at the call site rather than a
34
+ redesign, because every set is reached through the same function shapes.
35
+
36
+ Be precise about the difference between those five and what the backend can
37
+ express. `backend().parameterSets` reports nine on OpenSSL 3.5, including
38
+ ML-DSA-44, ML-KEM-512 and two further SLH-DSA sets, and
39
+ [BENCHMARKS.md](BENCHMARKS.md) measures all of them. Reporting a set is not the
40
+ same as wrapping it: a set outside the five is visible to an evidence bundle
41
+ and is not an API this package offers.
42
+
43
+ **Two interchangeable backends.** The primitives run in OpenSSL 3.5 where the
44
+ runtime provides them and in JavaScript everywhere else. They produce identical
45
+ wire bytes, which is what makes the substitution safe: a signature made on one
46
+ verifies on the other, and the interoperability matrix in
47
+ [CONFORMANCE.md](CONFORMANCE.md) is what establishes that rather than an
48
+ assertion here.
49
+
50
+ **One abstraction, enforced.** [`eslint-plugin-kxco-pq`](https://www.npmjs.com/package/eslint-plugin-kxco-pq)
51
+ fails a build that reaches past the wrapper into `.ml_dsa65` or `.ml_kem768` on
52
+ the underlying library. The point is not style. A call site bound to a
53
+ primitive is a call site that a backend or parameter-set change has to rewrite
54
+ by hand, and the rule is what keeps the abstraction from being quietly holed.
55
+
56
+ The mechanism reports its own state. `backend()` returns which implementation
57
+ is doing the maths, its OpenSSL version and the sets it can express, so an
58
+ evidence bundle or a support ticket records the answer rather than inferring it
59
+ from `process.version`. It reports and does not switch, deliberately: a runtime
60
+ flag that changed which implementation signed would be a flag that changed what
61
+ a customer's evidence means.
62
+
63
+ The one operator control is the opposite of a switch. `requireNativeBackend()`,
64
+ or `KXCO_PQ_REQUIRE_NATIVE=1` in the environment, refuses to let a process
65
+ continue on the JavaScript backend when the deployment is under a control that
66
+ says the cryptography must execute inside a validated module. It fails at
67
+ import rather than at the first signature. It cannot make OpenSSL appear, and
68
+ it does not claim that the OpenSSL it found is a validated module, which is a
69
+ property of the operator's build.
70
+
71
+ ## 3. An interoperable transition
72
+
73
+ **Algorithm identifiers on the wire.** The JWS module resolves the
74
+ implementation from its own allowlist using the header `alg`, and rejects
75
+ anything outside it. That is what lets a verifier accept two algorithms during
76
+ a transition without becoming vulnerable to the algorithm-confusion failure
77
+ that has broken JWT libraries repeatedly. `JWS_ALGORITHMS` is the allowlist;
78
+ the RFC 9964 names are used, not private ones.
79
+
80
+ **Key identifiers.** `fingerprint()` gives a key a stable kid, and both signing
81
+ and verification take one, so a consumer can be pinned to a specific key while
82
+ the algorithm around it moves.
83
+
84
+ **Two signatures over identical bytes.** The webhook envelope signs
85
+ `${timestamp}.${rawBody}` with HMAC-SHA-256 and with ML-DSA-65, both covering
86
+ exactly the same bytes. A receiver that verifies only one cannot be tricked
87
+ into treating it as covering a different message. This is stage 2 and stage 3
88
+ of the plan, already built.
89
+
90
+ **Evidence that peers accept the output.** The transition claim is only worth
91
+ the interoperability behind it. Bouncy Castle, liboqs, two independent Python
92
+ implementations and OpenSSL 3.5 verify what this package produces, and it
93
+ verifies theirs, both directions, pinned by version, in
94
+ [CONFORMANCE.md](CONFORMANCE.md).
95
+
96
+ ## What this package does not give you
97
+
98
+ - **No runtime negotiation protocol.** Nothing here discovers what a peer
99
+ supports. The JWS allowlist lets a verifier accept more than one algorithm;
100
+ it does not ask a peer which one to use.
101
+ - **No automatic downgrade, ever.** There is no path that silently drops to a
102
+ weaker set when the stronger one is unavailable. A missing set is an error.
103
+ - **The set list is fixed at release.** A parameter set this package does not
104
+ wrap needs a release of this package, not a configuration change. That is the
105
+ honest ceiling of the abstraction.
106
+ - **The JWS allowlist is ML-DSA only.** SLH-DSA and ML-KEM are reached at the
107
+ key and signature layer, not through `alg`.
108
+ - **No TLS group negotiation.** Channel-level agility is not this package's
109
+ surface. See `kxco-pq-tls`.
110
+
111
+ ## Correcting this document
112
+
113
+ Every mechanism above is in `src/` and every evidence claim is in
114
+ [CONFORMANCE.md](CONFORMANCE.md). If a statement here does not match the code,
115
+ that is a defect worth reporting through [SECURITY.md](SECURITY.md).
package/BOUNDARY.md ADDED
@@ -0,0 +1,111 @@
1
+ # Product boundary
2
+
3
+ Which cryptography this package performs, which it depends on, and which it
4
+ merely offers to a caller. The three are routinely collapsed into one claim,
5
+ and collapsing them is how "quantum-safe" gets asserted for a product whose own
6
+ update path is classical.
7
+
8
+ ## What the assessed thing is
9
+
10
+ A library. It has no server, no console and no runtime service connections:
11
+ nothing in `src/` opens a socket. That is a narrow boundary and it makes most
12
+ of the questions below short, but the short answers are the point.
13
+
14
+ Record the configuration alongside any claim, because the answers differ by
15
+ runtime. `backend()` returns it: implementation, OpenSSL version, and the
16
+ parameter sets that implementation can express.
17
+
18
+ ## Start and update
19
+
20
+ **Quantum-safe.** Every release asset is signed with ML-DSA-65 by this
21
+ package's own signing path, and the public key is committed to the repository
22
+ rather than served only beside the artefact. Verification is four lines and is
23
+ in [README.md](README.md#verifying-a-release). Releases also carry SLSA
24
+ provenance recording the workflow that built them, and npm provenance is on.
25
+
26
+ **The classical part, stated.** Fetching the release is TLS to the npm registry
27
+ or to GitHub, and that TLS is classical. So is the transport that delivered the
28
+ git clone. This is outside our control and it is a real residual: an attacker
29
+ who could break that transport today could serve a different artefact, and the
30
+ ML-DSA signature is what would catch it, provided the verifier has the key from
31
+ a second path. That proviso is why the key is in the repository and why the
32
+ README says to compare the two copies.
33
+
34
+ ## Operate
35
+
36
+ **No required service connections.** The library performs no network operation
37
+ at any point. There is no licence check, no telemetry, no key server and no
38
+ call home. Nothing in the operating path can be a classical dependency, because
39
+ there is no operating path outside the caller's process.
40
+
41
+ **Key management is the caller's, with one supported escape.** Keys live in the
42
+ caller's memory unless the caller puts them somewhere better. `kxco-pq-hsm`
43
+ generates ML-DSA keys on a PKCS#11 token with `CKA_EXTRACTABLE=false`, at which
44
+ point this library handles verification only and touches no secret. The channel
45
+ to that token is the HSM vendor's, not ours, and its own post-quantum posture
46
+ is a question for the vendor.
47
+
48
+ **Storage and diagnostics.** Neither exists here. The package writes no files
49
+ and keeps no state between calls.
50
+
51
+ ## Protect records
52
+
53
+ Not this package's role, and it does not pretend otherwise. Log integrity and
54
+ timestamping in the KXCO estate are `kxco-pq-audit`: SHA-256 hash-chained
55
+ entries, each ML-DSA-65 signed, with periodic seals anchored on Armature L1 so
56
+ a verifier who does not trust the log operator has an independent time bound.
57
+
58
+ What this package contributes to that is the signature primitive and nothing
59
+ else.
60
+
61
+ ## Enforce policy
62
+
63
+ A prohibited fallback can be rejected. The package's default is to fall back
64
+ from OpenSSL to JavaScript, and that default is correct for almost everyone
65
+ because both produce identical wire bytes. It is wrong in one situation: a
66
+ deployment under a control requiring the cryptography to execute inside a
67
+ validated module, where a silent fallback means the control is not in force and
68
+ nothing says so.
69
+
70
+ `KXCO_PQ_REQUIRE_NATIVE=1`, or `requireNativeBackend([...])` in code, fails the
71
+ process at import instead. Note the limit of the assertion: it establishes that
72
+ OpenSSL is doing the maths, not that the OpenSSL it found is a validated
73
+ module. That is a property of the operator's build and this package cannot see
74
+ it.
75
+
76
+ ## Retain history
77
+
78
+ **Verification of old records: yes.** A signature made by any version of this
79
+ package verifies under any later version. Wire formats have not changed and the
80
+ conformance evidence is regenerated against pinned vectors on every dependency
81
+ bump, which is what would catch a change that broke them.
82
+
83
+ **Long-term trust: not solved here, and it is not the same question.** This
84
+ package has no notion of key validity windows, revocation or trusted
85
+ timestamps, so it can tell you a signature is arithmetically valid and cannot
86
+ tell you the key was still trusted when it was made. Anything that has to hold
87
+ for years needs a time anchor outside the signature. In the KXCO estate that is
88
+ `kxco-pq-audit`'s on-chain seals. For a deployment outside it, that is a gap
89
+ the deployment has to close.
90
+
91
+ ## Customer-selected outputs
92
+
93
+ The library will sign at whatever parameter set the caller selects, Category 1
94
+ included, and will verify Category 1 signatures from a peer. That is a customer
95
+ output, not a statement about this package's own operation, and the two should
96
+ not be reported as one number.
97
+
98
+ The KXCO estate itself signs at Category 3 with ML-DSA-65, including Armature
99
+ L1 from block 0 and every issued KXCO ID. Support for ML-DSA-87 and ML-KEM-1024
100
+ is a support claim and not a CNSA 2.0 compliance claim, for the reasons set out
101
+ in [CONFORMANCE.md](CONFORMANCE.md#what-this-evidence-does-not-cover).
102
+
103
+ ## The gaps, collected
104
+
105
+ Repeated here so they are not spread across six sections:
106
+
107
+ 1. Release transport is classical TLS, mitigated by an ML-DSA signature and a
108
+ second copy of the key, not eliminated.
109
+ 2. The PKCS#11 channel to an HSM is the vendor's, and outside our assessment.
110
+ 3. No long-term validation: no timestamps, no validity windows, no revocation.
111
+ 4. `requireNativeBackend` asserts OpenSSL, not a validated OpenSSL.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.7.2
4
+
5
+ Documentation, and one typing fix. No source change to the library and no wire
6
+ format change.
7
+
8
+ **Three documents the package needed and did not have.** Buyer readiness
9
+ assessments ask what the product boundary is, what cryptographic agility it
10
+ actually has, and what constrains its lifecycle. All three had real answers in
11
+ the code and the evidence. None was written down, and an answer that exists
12
+ only in a maintainer's head is not evidence.
13
+
14
+ BOUNDARY.md separates what this package performs from what it depends on and
15
+ what it merely offers a caller. Nothing in src/ opens a socket, so the
16
+ operating path has no classical service dependency at all. Release signing is
17
+ ML-DSA-65 with the key committed here; the transport that delivers the release
18
+ is classical TLS, and that is stated rather than folded into the claim.
19
+
20
+ AGILITY.md separates the three things the term is used for: a replacement plan,
21
+ an implemented mechanism, and an interoperable transition. It also names the
22
+ four kinds of agility this package does not give you, including that a
23
+ parameter set outside the published five needs a release rather than a
24
+ configuration change.
25
+
26
+ LIFECYCLE.md carries supported versions, the runtime ceiling, and the blocking
27
+ supplier dependency with its mitigations ranked. The end-of-support window is
28
+ marked not yet decided rather than invented.
29
+
30
+ All three are copied into the evidence bundle. An assessor reads the bundle,
31
+ not the repository.
32
+
33
+ **requireNativeBackend is now in the typings.** 1.7.0 shipped it, and shipped
34
+ it exported from index.js and documented in the README while declared in
35
+ neither backend.d.ts nor index.d.ts. A TypeScript caller therefore could not
36
+ reach the one control in this package that enforces a no-fallback policy, which
37
+ is precisely the deployment the feature exists for. Runtime behaviour was
38
+ always correct; the type surface hid it.
39
+
40
+ ## 1.7.1
41
+
42
+ Release integrity. No source change to the library.
43
+
44
+ Release assets are now signed with ML-DSA-65 through this package's own signing
45
+ path, and each release carries a SLSA provenance file. Until now the npm tarball
46
+ had provenance and the GitHub release, where the evidence bundle is actually
47
+ downloaded from, had neither a signature nor a statement of where it was built.
48
+
49
+ The public key is committed as release-signing-key.pub.hex and published with
50
+ each release. The signing script refuses to run if the seed in CI stops deriving
51
+ that key, and verifies every signature immediately after producing it.
52
+
3
53
  ## 1.7.0
4
54
 
5
55
  Additive. No existing export changes shape, no wire format moves, and the
package/LIFECYCLE.md ADDED
@@ -0,0 +1,124 @@
1
+ # Lifecycle, ceiling and dependencies
2
+
3
+ The questions a buyer needs answered before committing to a migration date,
4
+ rather than after. Maturity describes what has been achieved; none of it says
5
+ whether the thing will still be maintained, how far it can go without new
6
+ hardware, or what could block it. Those are here.
7
+
8
+ ## Guidance followed
9
+
10
+ FIPS 203, 204 and 205 for the primitives, verified against NIST's own ACVP
11
+ vectors rather than asserted. RFC 9964 for the JWS algorithm names. RFC 5869
12
+ for HKDF. Where a standard is supported but compliance is a property of a
13
+ deployment rather than of this package, the distinction is stated instead of
14
+ blurred: see the CNSA 2.0 entry in
15
+ [CONFORMANCE.md](CONFORMANCE.md#what-this-evidence-does-not-cover).
16
+
17
+ Risk review happened before the evidence, not after it:
18
+ [THREAT-MODEL.md](THREAT-MODEL.md) names what is in scope, what is out, and
19
+ where the residual risk actually sits, which is a long-lived signing key in the
20
+ memory of a general-purpose process.
21
+
22
+ Update and agility needs are in [AGILITY.md](AGILITY.md), including the
23
+ mechanisms this package does not have.
24
+
25
+ ## Supported versions
26
+
27
+ One line, moving forward. Releases run v1.x in sequence with no maintenance
28
+ branches, and fixes land in the next release rather than being backported.
29
+ Anyone on an older 1.x upgrades forward to receive a fix.
30
+
31
+ Upgrading forward is intended to be cheap and is treated as a compatibility
32
+ obligation: signatures made by earlier versions verify under later ones, and
33
+ [MIGRATION.md](MIGRATION.md#migrating-between-versions) records what changed at
34
+ each step where a caller had to do anything.
35
+
36
+ **Not yet decided:** a formal end-of-support window, expressed in months rather
37
+ than in practice. Until it is decided, the honest statement is the one above,
38
+ which describes what happens rather than what is promised. A buyer who needs a
39
+ contractual window should ask for one rather than infer it.
40
+
41
+ ## Ceiling without hardware replacement
42
+
43
+ There is no hardware ceiling in this package. It is software, the highest
44
+ parameter sets are already shipped rather than planned, and Category 5 needs no
45
+ different machine than Category 3: `mlDsa87` and `mlKem1024` are published
46
+ entry points today.
47
+
48
+ The ceiling that does exist is the runtime, and it is a performance ceiling
49
+ rather than a capability one:
50
+
51
+ | Runtime | Backend | Effect |
52
+ |---|---|---|
53
+ | Node 24 and later | OpenSSL 3.5 primitives | Roughly 4x to 8x faster, measured per operation in [BENCHMARKS.md](BENCHMARKS.md) |
54
+ | Node 20.19 to 23, browsers | JavaScript | Every algorithm available, identical wire bytes, slower |
55
+
56
+ Nothing becomes unavailable on the slower path. If a request budget cannot
57
+ absorb the JavaScript figures, the fix is a Node upgrade, which is not a
58
+ hardware replacement.
59
+
60
+ Two real ceilings sit outside this package and belong to whatever it is
61
+ deployed with. An HSM can only perform the algorithms its firmware implements,
62
+ and firmware is where hardware replacement genuinely appears; that question
63
+ belongs to `kxco-pq-hsm` and the token vendor. Signature size is the other:
64
+ ML-DSA-65 signatures are far larger than ECDSA, and a protocol with a fixed
65
+ field or an MTU assumption may need changing. [MIGRATION.md](MIGRATION.md) puts
66
+ proving that at stage 1 for exactly this reason.
67
+
68
+ ## Blocking supplier dependency
69
+
70
+ **The dependency.** `@noble/post-quantum`, pinned at exactly 0.7.0, supplies
71
+ the FIPS 203/204/205 arithmetic. This package does not reimplement it.
72
+
73
+ **Why it is a blocker and not just a dependency.** It is the one package in the
74
+ tree with no independent review. The maintainer's own self-audit covers 0.6.1,
75
+ which is not the version shipped. Version 0.7.1 regressed nine NIST SLH-DSA
76
+ verification vectors that 0.7.0 passes, shipped in this package's 1.5.1 and was
77
+ reverted in 1.5.2. So the supply risk here is demonstrated rather than
78
+ theoretical.
79
+
80
+ **Mitigations, in the order they actually help.**
81
+
82
+ 1. *Evidence instead of trust.* Every parameter set is checked against NIST's
83
+ ACVP vectors and cross-checked against liboqs, Bouncy Castle and two Python
84
+ implementations in both directions. That regression was caught by this
85
+ harness within hours of the dependency's release, which is the concrete
86
+ demonstration that the control works.
87
+ 2. *A second implementation on the same wire format.* On Node 24 and later the
88
+ OpenSSL backend performs ML-DSA, ML-KEM and SLH-DSA for the sets OpenSSL
89
+ expresses, and the dependency is not doing that arithmetic at all. The
90
+ exception is precise and worth stating: a FIPS 204 context string takes the
91
+ JavaScript path, because OpenSSL does not express it. So the dependency is
92
+ reduced on modern runtimes rather than removed.
93
+ 3. *An exact pin, enforced.* No ranges in `dependencies`, and the audit harness
94
+ fails the build on one. 0.7.1 is separately blocked in dependabot so it
95
+ cannot return through an automated bump.
96
+ 4. *A conformance gate on every bump.* A change to this dependency is a change
97
+ to the primitives, so it is not merged on a green test run. The full ACVP
98
+ and interoperability evidence has to regenerate clean.
99
+
100
+ **Milestone.** The route off this being a single point of review is external
101
+ audit, and that is on the roadmap rather than done. See below.
102
+
103
+ **Second-order dependency, disclosed.** `@noble/curves` is not imported by name
104
+ anywhere in this package and is on the hot path regardless: both ML-DSA and
105
+ ML-KEM reach it through the primitives. That was established by a reachability
106
+ walk rather than by reading manifests, and it is in
107
+ [DEPENDENCIES.md](DEPENDENCIES.md). It is the most heavily reviewed package in
108
+ the tree.
109
+
110
+ ## Roadmap status, beside the maturity result
111
+
112
+ The commitments and what has not happened yet are in
113
+ [AUDIT.md](AUDIT.md#3-audit-roadmap): an external auditor for the wrapper
114
+ integration patterns, a public bug bounty, and a FIPS 140-3 CMVP application
115
+ for a module deployment using this library with an HSM. Dates depend on
116
+ capacity; the order is committed.
117
+
118
+ Read that table beside any maturity claim, not after it. Nothing on it has been
119
+ delivered, and a roadmap entry is a vendor commitment rather than a result.
120
+
121
+ ## Correcting this document
122
+
123
+ If a figure or a pin here does not match the tree, that is a defect worth
124
+ reporting through [SECURITY.md](SECURITY.md).
package/README.md CHANGED
@@ -41,6 +41,9 @@ languages are **JavaScript and C** (OpenSSL 3.5 on Node 24+).
41
41
  - [THREAT-MODEL.md](./THREAT-MODEL.md): what this defends against and what it does not. Read the side-channel section before deciding where a signing key lives.
42
42
  - [MIGRATION.md](./MIGRATION.md): moving an RSA or ECDSA system across, and moving between versions of this package.
43
43
  - [SECURITY.md](./SECURITY.md): reporting, release integrity, and the dependency policy.
44
+ - [AGILITY.md](./AGILITY.md): what has to change when the algorithm changes. The replacement plan, the mechanisms that exist today, the transition peers can follow, and the four kinds of agility this package does not give you.
45
+ - [BOUNDARY.md](./BOUNDARY.md): which cryptography this package performs, which it depends on, and which it merely offers to a caller. Release signing is ML-DSA-65; the transport that delivers the release is classical TLS, and that is stated rather than folded into the claim.
46
+ - [LIFECYCLE.md](./LIFECYCLE.md): supported versions, the runtime ceiling, and the one blocking supplier dependency with its mitigations. Read the roadmap beside a maturity claim, not after it.
44
47
  - **Every release is reproducible and attested.** The published tarball rebuilds bit-for-bit from its own tag, verified in CI on every run, and each release carries a SLSA provenance attestation plus a CycloneDX SBOM at a permanent unauthenticated URL. A provenance attestation says a build happened in CI; the reproducible build says the artefact is the source. They are different claims and both are checkable without asking us for anything.
45
48
 
46
49
  ---
@@ -344,3 +347,29 @@ Apache-2.0. See [LICENSE](./LICENSE).
344
347
  ## Maintainers
345
348
 
346
349
  Shayne Heffernan and John Heffernan — [KXCO by Knightsbridge](https://kxco.ai)
350
+
351
+ ## Verifying a release
352
+
353
+ Every release asset is signed with ML-DSA-65 by this package's own signing path,
354
+ and carries a SLSA provenance file recording the workflow that built it.
355
+
356
+ ```
357
+ manifest-node24.x.json the bundle's manifest
358
+ evidence-node24.x.zip the bundle
359
+ evidence-node24.x.zip.sig ML-DSA-65 signature over the zip, hex
360
+ evidence.intoto.jsonl SLSA provenance
361
+ release-signing-key.pub.hex the public key, also committed to this repository
362
+ ```
363
+
364
+ ```js
365
+ import { readFileSync } from 'node:fs'
366
+ import { mlDsa } from 'kxco-post-quantum'
367
+
368
+ const pub = Buffer.from(readFileSync('release-signing-key.pub.hex', 'utf8').trim(), 'hex')
369
+ const sig = readFileSync('evidence-node24.x.zip.sig', 'utf8').trim()
370
+ mlDsa.verify(pub, readFileSync('evidence-node24.x.zip'), sig) // true
371
+ ```
372
+
373
+ Compare the public key against the copy in this repository before trusting a
374
+ signature: a key served alongside the artefact it signs proves only that the
375
+ same party produced both.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kxco-post-quantum",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "ML-DSA-65, ML-KEM-768 and SLH-DSA-SHA2-192s with key fingerprinting. OpenSSL 3.5 primitives on Node 24+, JavaScript elsewhere. 2,103 NIST ACVP vectors: 1,793 passed, 0 failed, 310 skipped. 225 interop checks, 0 failed. Reproducible builds and provenance.",
5
5
  "keywords": [
6
6
  "post-quantum",
@@ -109,12 +109,15 @@
109
109
  }
110
110
  },
111
111
  "files": [
112
+ "AGILITY.md",
112
113
  "BENCHMARKS.md",
114
+ "BOUNDARY.md",
113
115
  "CHANGELOG.md",
114
116
  "CONFORMANCE.md",
115
117
  "DEPENDENCIES.md",
116
118
  "LICENCE-PRODUCT.md",
117
119
  "LICENSE",
120
+ "LIFECYCLE.md",
118
121
  "MIGRATION.md",
119
122
  "README.md",
120
123
  "SECURITY.md",
@@ -129,6 +132,7 @@
129
132
  "@noble/post-quantum": "0.7.0"
130
133
  },
131
134
  "scripts": {
135
+ "fuzz": "node fuzz/fuzz.mjs",
132
136
  "test": "node --test test/basic.test.js && node --test test/backend.test.js && node --test test/context.test.js && node --test test/category5.test.js && node --test test/edge-cases.test.js && node --test test/seed.test.js && node --test test/browser-smoke.test.js && node test/run-vectors.js",
133
137
  "test:vectors": "node test/run-vectors.js",
134
138
  "generate:vectors": "node test/generate-vectors.js > test/vectors.json",
package/src/backend.d.ts CHANGED
@@ -14,3 +14,21 @@ export function backend(): BackendReport
14
14
 
15
15
  /** Whether a parameter set runs natively here, e.g. isNative('ML-DSA-65'). */
16
16
  export function isNative(alg: string): boolean
17
+
18
+ /**
19
+ * Refuse to run unless the cryptography is executing in the native backend.
20
+ *
21
+ * An assertion, not a switch: it cannot change which implementation signs, it
22
+ * stops a process that has silently landed on the JavaScript backend when the
23
+ * operator required otherwise. Also settable from the environment with
24
+ * `KXCO_PQ_REQUIRE_NATIVE=1`, which applies the check at import.
25
+ *
26
+ * Asserts that OpenSSL is doing the maths. Whether that OpenSSL is a
27
+ * FIPS-validated module is a property of the operator's build and is not
28
+ * visible from here.
29
+ *
30
+ * @param algorithms Parameter sets that must run natively. Omit to require
31
+ * only that the native backend is present.
32
+ * @throws Error with `code: 'ERR_KXCO_PQ_BACKEND'` when the requirement fails.
33
+ */
34
+ export function requireNativeBackend(algorithms?: string[]): BackendReport
package/src/index.d.ts CHANGED
@@ -16,4 +16,4 @@ export * as webhook from './webhook.js'
16
16
  export * as seed from './seed.js'
17
17
  /** Compact JWS using the RFC 9964 algorithm names. Format only, no network. */
18
18
  export * as jws from './jws.js'
19
- export { backend, isNative } from './backend.js'
19
+ export { backend, isNative, requireNativeBackend } from './backend.js'