kxco-post-quantum 1.7.1 → 1.7.3
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 +115 -0
- package/BOUNDARY.md +111 -0
- package/CHANGELOG.md +62 -0
- package/CRYPTO-INVENTORY.md +227 -0
- package/HNDL.md +84 -0
- package/LIFECYCLE.md +124 -0
- package/PQCMM.md +164 -0
- package/README.md +4 -1
- package/package.json +33 -13
- package/src/backend.d.ts +18 -0
- package/src/index.d.ts +1 -1
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,67 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.7.3
|
|
4
|
+
|
|
5
|
+
One resolution fix that consumers could hit, plus the ACVTS tooling and two
|
|
6
|
+
maturity documents. No change to any algorithm, key format or wire format.
|
|
7
|
+
|
|
8
|
+
**Every export subpath now carries a `default` condition.** All twelve declared
|
|
9
|
+
only `types` and `import`, so a consumer resolving outside an import context got
|
|
10
|
+
`ERR_PACKAGE_PATH_NOT_EXPORTED` and could not load the package at all. That is a
|
|
11
|
+
packaging defect rather than a library one: nothing about the code changed, only
|
|
12
|
+
what Node is willing to resolve. It was found from the other side, in a project
|
|
13
|
+
that had to pass `--conditions=import` on every test invocation to work around
|
|
14
|
+
it, and that workaround can come out once this is published.
|
|
15
|
+
|
|
16
|
+
The addition is purely additive. A subpath that resolved before resolves to the
|
|
17
|
+
same file now.
|
|
18
|
+
|
|
19
|
+
**ACVTS tooling is committed rather than kept locally.** The selftest, the
|
|
20
|
+
validation printer and the demo certificate verifier were written against the
|
|
21
|
+
NIST demo submission and existed only on one machine, which makes them evidence
|
|
22
|
+
nobody else can re-run.
|
|
23
|
+
|
|
24
|
+
**Two PQCMM gaps closed and Level 3 re-declared**, against the PKI Consortium's
|
|
25
|
+
Post-Quantum Cryptography Maturity Model, with the self-assessment written down
|
|
26
|
+
rather than asserted.
|
|
27
|
+
|
|
28
|
+
## 1.7.2
|
|
29
|
+
|
|
30
|
+
Documentation, and one typing fix. No source change to the library and no wire
|
|
31
|
+
format change.
|
|
32
|
+
|
|
33
|
+
**Three documents the package needed and did not have.** Buyer readiness
|
|
34
|
+
assessments ask what the product boundary is, what cryptographic agility it
|
|
35
|
+
actually has, and what constrains its lifecycle. All three had real answers in
|
|
36
|
+
the code and the evidence. None was written down, and an answer that exists
|
|
37
|
+
only in a maintainer's head is not evidence.
|
|
38
|
+
|
|
39
|
+
BOUNDARY.md separates what this package performs from what it depends on and
|
|
40
|
+
what it merely offers a caller. Nothing in src/ opens a socket, so the
|
|
41
|
+
operating path has no classical service dependency at all. Release signing is
|
|
42
|
+
ML-DSA-65 with the key committed here; the transport that delivers the release
|
|
43
|
+
is classical TLS, and that is stated rather than folded into the claim.
|
|
44
|
+
|
|
45
|
+
AGILITY.md separates the three things the term is used for: a replacement plan,
|
|
46
|
+
an implemented mechanism, and an interoperable transition. It also names the
|
|
47
|
+
four kinds of agility this package does not give you, including that a
|
|
48
|
+
parameter set outside the published five needs a release rather than a
|
|
49
|
+
configuration change.
|
|
50
|
+
|
|
51
|
+
LIFECYCLE.md carries supported versions, the runtime ceiling, and the blocking
|
|
52
|
+
supplier dependency with its mitigations ranked. The end-of-support window is
|
|
53
|
+
marked not yet decided rather than invented.
|
|
54
|
+
|
|
55
|
+
All three are copied into the evidence bundle. An assessor reads the bundle,
|
|
56
|
+
not the repository.
|
|
57
|
+
|
|
58
|
+
**requireNativeBackend is now in the typings.** 1.7.0 shipped it, and shipped
|
|
59
|
+
it exported from index.js and documented in the README while declared in
|
|
60
|
+
neither backend.d.ts nor index.d.ts. A TypeScript caller therefore could not
|
|
61
|
+
reach the one control in this package that enforces a no-fallback policy, which
|
|
62
|
+
is precisely the deployment the feature exists for. Runtime behaviour was
|
|
63
|
+
always correct; the type surface hid it.
|
|
64
|
+
|
|
3
65
|
## 1.7.1
|
|
4
66
|
|
|
5
67
|
Release integrity. No source change to the library.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Cryptographic inventory
|
|
2
|
+
|
|
3
|
+
Every use case in this package where cryptography is applied, mapped onto the
|
|
4
|
+
ten categories of the PKI Consortium PQCMM cryptographic inventory taxonomy.
|
|
5
|
+
|
|
6
|
+
The model requires that no category is omitted. A category that does not apply
|
|
7
|
+
is marked not applicable with a written reason, because "we do not do that" and
|
|
8
|
+
"we did not look" are indistinguishable from the outside unless the reason is
|
|
9
|
+
recorded. Four of the ten genuinely do not apply to a library with no sockets
|
|
10
|
+
and no persistent state, and saying so in one line each is the honest answer
|
|
11
|
+
rather than a padded one.
|
|
12
|
+
|
|
13
|
+
Scope: `kxco-post-quantum` 1.7.2. `BOUNDARY.md` is the companion document and
|
|
14
|
+
explains the difference between cryptography this package performs, depends on,
|
|
15
|
+
and merely offers to a caller. The distinction matters here: a caller can use
|
|
16
|
+
this package inside a category that the package itself does not implement.
|
|
17
|
+
|
|
18
|
+
## 1. Data in transit
|
|
19
|
+
|
|
20
|
+
**Not applicable to the library. Applicable to its delivery.**
|
|
21
|
+
|
|
22
|
+
Nothing in `src/` opens a socket. The library performs no network operation at
|
|
23
|
+
any point: no licence check, no telemetry, no key server, no call home. There
|
|
24
|
+
is no transport for it to protect.
|
|
25
|
+
|
|
26
|
+
Its own delivery is a different matter and is not quantum-safe. Fetching the
|
|
27
|
+
package is classical TLS to the npm registry or to GitHub, as was the transport
|
|
28
|
+
that delivered any git clone. That residual is stated in `BOUNDARY.md` and is
|
|
29
|
+
mitigated by an ML-DSA-65 signature over every release asset plus a second copy
|
|
30
|
+
of the public key committed to the repository, so that a verifier is not
|
|
31
|
+
trusting the same channel twice. Mitigated, not eliminated.
|
|
32
|
+
|
|
33
|
+
What the package offers a caller for this category is the ML-KEM key
|
|
34
|
+
encapsulation below, from which a caller may build a transport. Offering a
|
|
35
|
+
primitive is not the same as protecting a transport, and the two should not be
|
|
36
|
+
reported as one.
|
|
37
|
+
|
|
38
|
+
| Use | Algorithm | Quantum-safe |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| Package delivery | TLS to registry and GitHub, classical | No, mitigated by signature |
|
|
41
|
+
| Primitive offered to callers | ML-KEM-768, ML-KEM-1024 | Yes |
|
|
42
|
+
|
|
43
|
+
## 2. Data at rest
|
|
44
|
+
|
|
45
|
+
**Not applicable.** The package writes no files and keeps no state between
|
|
46
|
+
calls. There is no disk, database or object storage in its boundary to encrypt.
|
|
47
|
+
|
|
48
|
+
A caller who persists a key or a seed produced by this package is performing
|
|
49
|
+
data-at-rest protection in their own system, with their own choice of
|
|
50
|
+
algorithm, and this package neither sees nor constrains it. The one supported
|
|
51
|
+
route that removes the question is `kxco-pq-hsm`, which generates ML-DSA keys
|
|
52
|
+
on a PKCS#11 token with `CKA_EXTRACTABLE=false`, after which this package
|
|
53
|
+
handles verification only and touches no secret.
|
|
54
|
+
|
|
55
|
+
## 3. Identity, authentication and certificates
|
|
56
|
+
|
|
57
|
+
**Applicable, and quantum-safe within the package.**
|
|
58
|
+
|
|
59
|
+
No X.509. No certificate chain, no path validation, no revocation. Identity
|
|
60
|
+
here is a raw key plus a key identifier, and the package deliberately does not
|
|
61
|
+
pretend to be a PKI.
|
|
62
|
+
|
|
63
|
+
| Use | Algorithm | Quantum-safe |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| Signature and verification | ML-DSA-65, ML-DSA-87 (FIPS 204) | Yes |
|
|
66
|
+
| Hash-based alternative | SLH-DSA, ten parameter sets (FIPS 205) | Yes |
|
|
67
|
+
| Key identifier | SHA-256 over the public key, truncated to 16 hex characters (`kid.js`) | Yes, second-preimage bound |
|
|
68
|
+
| Key interchange | JWK `kty: AKP` per RFC 9964, and PKCS#8 seed export | Format, not an algorithm |
|
|
69
|
+
| JWS signing | ML-DSA over the JWS signing input (`jws.js`) | Yes |
|
|
70
|
+
| Webhook delivery signing | HMAC-SHA-256 **and** ML-DSA-65, both (`webhook.js`) | Yes, hybrid |
|
|
71
|
+
|
|
72
|
+
The `kid` is a truncated SHA-256, so it is a 64-bit identifier and not a
|
|
73
|
+
security boundary. It identifies which key to try; it does not authenticate
|
|
74
|
+
anything. Grover halves the preimage work on SHA-256 and the truncation
|
|
75
|
+
dominates that anyway, which is why nothing in the package makes a trust
|
|
76
|
+
decision on a `kid` alone.
|
|
77
|
+
|
|
78
|
+
## 4. Code, firmware and update signing
|
|
79
|
+
|
|
80
|
+
**Applicable, and quantum-safe.**
|
|
81
|
+
|
|
82
|
+
Every release asset is signed with ML-DSA-65 by this package's own signing
|
|
83
|
+
path, which means the signing of the package is performed by the thing being
|
|
84
|
+
signed. The public key is committed to the repository at
|
|
85
|
+
`release-signing-key.pub.hex` and published as a release asset, so a verifier
|
|
86
|
+
can obtain it by a path other than the one that delivered the artefact.
|
|
87
|
+
Verification is four lines and is in `README.md`.
|
|
88
|
+
|
|
89
|
+
No firmware. The package ships no binary and no native build of its own; the
|
|
90
|
+
native path calls the OpenSSL already present in the host runtime.
|
|
91
|
+
|
|
92
|
+
| Use | Algorithm | Quantum-safe |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| Release asset signing | ML-DSA-65 | Yes |
|
|
95
|
+
| Signature over the evidence bundle | ML-DSA-65, `.sig` beside each bundle | Yes |
|
|
96
|
+
|
|
97
|
+
## 5. Key wrapping and key-encryption keys
|
|
98
|
+
|
|
99
|
+
**Applicable in a limited form, and quantum-safe.**
|
|
100
|
+
|
|
101
|
+
The package wraps no keys. It has no KEK hierarchy, no envelope encryption and
|
|
102
|
+
no key store.
|
|
103
|
+
|
|
104
|
+
What it does have is deterministic derivation, which occupies the same place in
|
|
105
|
+
a design without being key wrapping. `keypairFromMaster` derives a per-purpose
|
|
106
|
+
seed from a caller-held master secret using HKDF-SHA-512 with an empty salt and
|
|
107
|
+
a caller-chosen `info` string, then generates the keypair from that seed. A
|
|
108
|
+
master secret therefore stands in the position a KEK would occupy, and its
|
|
109
|
+
protection is entirely the caller's.
|
|
110
|
+
|
|
111
|
+
| Use | Algorithm | Quantum-safe |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| Seed derivation from a master secret | HKDF-SHA-512 (`derive.js`) | Yes, symmetric |
|
|
114
|
+
| Key encapsulation offered to callers | ML-KEM-768, ML-KEM-1024 (FIPS 203) | Yes |
|
|
115
|
+
| Hybrid key establishment | `deriveSeed` combines an ML-KEM shared secret with a classical secret through the KDF | Yes, holds if either half holds |
|
|
116
|
+
|
|
117
|
+
## 6. Random number generation and entropy sources
|
|
118
|
+
|
|
119
|
+
**Applicable, and this is the entry a reader should not skim.**
|
|
120
|
+
|
|
121
|
+
**This package generates no randomness in its JavaScript backend.** There is no
|
|
122
|
+
`randomBytes`, no `getRandomValues`, no `randomFillSync` anywhere in `src/`
|
|
123
|
+
outside the native path. Every keypair is produced from a seed the caller
|
|
124
|
+
supplies, either directly through `keypairFromSeed` or derived from a
|
|
125
|
+
caller-held master through `keypairFromMaster`. Seed length is enforced against
|
|
126
|
+
the FIPS parameter set and a wrong length is a `RangeError`, not a silent pad.
|
|
127
|
+
|
|
128
|
+
The consequence is worth stating plainly: **in the JavaScript backend, the
|
|
129
|
+
quality of every private key in this system is a property of the caller's
|
|
130
|
+
entropy source, not of this package.** A caller who supplies a low-entropy seed
|
|
131
|
+
gets a low-entropy key and the library cannot detect it. This is a deliberate
|
|
132
|
+
design choice, because deterministic derivation is what makes the keys
|
|
133
|
+
reproducible and the conformance vectors pinnable, but it relocates a security
|
|
134
|
+
property rather than solving it.
|
|
135
|
+
|
|
136
|
+
The native backend is different. `crypto.generateKeyPairSync` in `_native.node.js`
|
|
137
|
+
delegates to Node and thence to the OpenSSL DRBG, so on that path entropy comes
|
|
138
|
+
from the operating system.
|
|
139
|
+
|
|
140
|
+
| Path | Entropy source | Whose responsibility |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| JavaScript backend | Caller-supplied seed | The caller |
|
|
143
|
+
| JavaScript backend, derived | HKDF-SHA-512 over a caller-supplied master | The caller |
|
|
144
|
+
| Native backend keygen | OpenSSL DRBG via `crypto.generateKeyPairSync` | The operating system |
|
|
145
|
+
|
|
146
|
+
No category of quantum attack applies to a DRBG in the way it applies to a
|
|
147
|
+
public-key primitive; the risk here is classical and it is entropy starvation.
|
|
148
|
+
|
|
149
|
+
## 7. Telemetry, logging and audit-trail integrity
|
|
150
|
+
|
|
151
|
+
**Not applicable.** The package emits no telemetry and writes no log. It keeps
|
|
152
|
+
no state between calls, so there is no audit trail of its own to protect.
|
|
153
|
+
|
|
154
|
+
Log integrity in the wider KXCO estate is `kxco-pq-audit`, a separate package:
|
|
155
|
+
SHA-256 hash-chained entries, each signed with ML-DSA-65, with periodic seals
|
|
156
|
+
anchored on Armature L1 so that a verifier who does not trust the log operator
|
|
157
|
+
still has an independent time bound. What this package contributes to that is
|
|
158
|
+
the signature primitive and nothing else.
|
|
159
|
+
|
|
160
|
+
## 8. Build, CI/CD and supply-chain signing
|
|
161
|
+
|
|
162
|
+
**Applicable, and quantum-safe for what we sign, classical for what GitHub and
|
|
163
|
+
npm sign.** This split is the honest answer and collapsing it would be an
|
|
164
|
+
overclaim.
|
|
165
|
+
|
|
166
|
+
| Use | Algorithm | Quantum-safe |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| Release assets and evidence bundle | ML-DSA-65, ours | Yes |
|
|
169
|
+
| SLSA provenance attestation | Sigstore, classical (ECDSA and the Fulcio and Rekor chain) | **No** |
|
|
170
|
+
| npm registry signature and provenance | npm's own, classical | **No** |
|
|
171
|
+
| Reproducible build | Not a signature. The published tarball rebuilds bit-for-bit from its own tag, verified in CI on every run | Not applicable |
|
|
172
|
+
| CycloneDX SBOM | Generated by `npm sbom` from the published tree; integrity carried by the ML-DSA signature over the release | Yes, via the release signature |
|
|
173
|
+
|
|
174
|
+
The reproducible build is the part of this category that does not depend on any
|
|
175
|
+
signature algorithm at all, and it is the reason the classical rows above are a
|
|
176
|
+
weaker residual than they look: a verifier who rebuilds from source is not
|
|
177
|
+
trusting Sigstore, npm or us.
|
|
178
|
+
|
|
179
|
+
## 9. Attestation and remote attestation
|
|
180
|
+
|
|
181
|
+
**Applicable in the supply-chain sense only.**
|
|
182
|
+
|
|
183
|
+
There is no hardware attestation, no TPM quote, no confidential-computing
|
|
184
|
+
report and no remote-attestation protocol in this package.
|
|
185
|
+
|
|
186
|
+
What exists is build attestation: an in-toto statement at
|
|
187
|
+
`evidence.intoto.jsonl` and an evidence manifest that pins the commit, the
|
|
188
|
+
toolchain, the backend, the dependency versions and every step with its exact
|
|
189
|
+
command, result and duration, with a SHA-256 for each file in the bundle. The
|
|
190
|
+
in-toto and SLSA layer is signed classically by Sigstore, as row 2 of category
|
|
191
|
+
8 records. The manifest and bundle are additionally signed with ML-DSA-65 by us.
|
|
192
|
+
|
|
193
|
+
## 10. Backup, archival and long-term storage
|
|
194
|
+
|
|
195
|
+
**Applicable as a stated gap, and this is the weakest category.**
|
|
196
|
+
|
|
197
|
+
Verification of old records holds: a signature made by any version of this
|
|
198
|
+
package verifies under any later version. Wire formats have not changed, and
|
|
199
|
+
the conformance evidence is regenerated against pinned vectors on every
|
|
200
|
+
dependency bump, which is the control that would catch a change breaking them.
|
|
201
|
+
|
|
202
|
+
Long-term trust does not hold and is a different question. This package has no
|
|
203
|
+
notion of key validity windows, no revocation and no trusted timestamps. It can
|
|
204
|
+
tell you a signature is arithmetically valid. It cannot tell you the key was
|
|
205
|
+
still trusted when the signature was made. Anything that has to hold for years
|
|
206
|
+
needs a time anchor outside the signature. Inside the KXCO estate that anchor
|
|
207
|
+
is `kxco-pq-audit`'s on-chain seals. Outside it, this is a gap the deployment
|
|
208
|
+
has to close, and this package should not be cited as closing it.
|
|
209
|
+
|
|
210
|
+
## Summary
|
|
211
|
+
|
|
212
|
+
| # | Category | Status |
|
|
213
|
+
|---|---|---|
|
|
214
|
+
| 1 | Data in transit | Library N/A; delivery is classical TLS, signature-mitigated |
|
|
215
|
+
| 2 | Data at rest | N/A, no persistence |
|
|
216
|
+
| 3 | Identity, authentication, certificates | Quantum-safe, no X.509 |
|
|
217
|
+
| 4 | Code, firmware, update signing | Quantum-safe |
|
|
218
|
+
| 5 | Key wrapping and KEKs | Quantum-safe, no wrapping; master secret is the caller's |
|
|
219
|
+
| 6 | RNG and entropy | **Relocated to the caller** in the JavaScript backend |
|
|
220
|
+
| 7 | Telemetry, logging, audit integrity | N/A, separate package |
|
|
221
|
+
| 8 | Build, CI/CD, supply chain | Split: ours quantum-safe, Sigstore and npm classical |
|
|
222
|
+
| 9 | Attestation | Build attestation only, classically signed at the Sigstore layer |
|
|
223
|
+
| 10 | Backup, archival, long-term | **Gap.** No timestamps, validity windows or revocation |
|
|
224
|
+
|
|
225
|
+
Three residuals carry forward into `PQCMM.md` and `HNDL.md` rather than being
|
|
226
|
+
recorded only here: classical release transport, the classical Sigstore and npm
|
|
227
|
+
signature layer, and the absence of long-term validation.
|
package/HNDL.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# HNDL exposure register
|
|
2
|
+
|
|
3
|
+
Harvest-now-decrypt-later exposure for `kxco-post-quantum` 1.7.2, as required
|
|
4
|
+
at Level 3 of the PKI Consortium PQCMM: which data flows handled by the product
|
|
5
|
+
carry a long-lived confidentiality requirement, and which algorithm protects
|
|
6
|
+
each.
|
|
7
|
+
|
|
8
|
+
## What HNDL is, and why most of this register is short
|
|
9
|
+
|
|
10
|
+
HNDL is a confidentiality attack. An adversary records ciphertext today and
|
|
11
|
+
decrypts it once a cryptographically relevant quantum computer exists. It
|
|
12
|
+
applies to data whose confidentiality must outlast that machine.
|
|
13
|
+
|
|
14
|
+
It does **not** apply to signatures in the same way. A signature forged in 2040
|
|
15
|
+
is worthless against a document verified in 2026, because the verification
|
|
16
|
+
already happened. The quantum risk to a signature scheme is forgery of *future*
|
|
17
|
+
verifications, which is an integrity and non-repudiation problem with a
|
|
18
|
+
different shape and a different deadline. Registers that fold the two together
|
|
19
|
+
overstate their own exposure, and this one keeps them apart.
|
|
20
|
+
|
|
21
|
+
**This package is predominantly a signing library.** Most of what it handles is
|
|
22
|
+
therefore outside HNDL by nature, and the register is short for a real reason
|
|
23
|
+
rather than an incomplete one. Where confidentiality genuinely is at stake, the
|
|
24
|
+
rows below say so.
|
|
25
|
+
|
|
26
|
+
## Register
|
|
27
|
+
|
|
28
|
+
| # | Data flow | Long-lived confidentiality? | Protected by | Quantum-safe | Residual |
|
|
29
|
+
|---|---|---|---|---|---|
|
|
30
|
+
| 1 | Private keys and seeds held in caller memory | **Yes, for the key's whole life** | Nothing in this package. Caller's process isolation | Not a cryptographic control | **The primary exposure.** See below |
|
|
31
|
+
| 2 | Master secret used by `keypairFromMaster` | **Yes, and it is worse than a single key** | Nothing in this package. Caller's storage | Not a cryptographic control | Compromise derives every child key, past and future |
|
|
32
|
+
| 3 | ML-KEM shared secret, in transit | Depends entirely on the caller's use | ML-KEM-768 or ML-KEM-1024 (FIPS 203) | **Yes** | The classical half of a hybrid pairing, if the caller adds one |
|
|
33
|
+
| 4 | Payload a caller encrypts under an ML-KEM-derived key | Caller's to determine | The caller's AEAD, not ours | Symmetric, so Grover only | Key size is the caller's choice; this package does not perform the encryption |
|
|
34
|
+
| 5 | Package delivery over TLS to npm or GitHub | **No** | Classical TLS | No | Confidentiality is irrelevant: the package is public. Integrity is the concern and ML-DSA-65 covers it |
|
|
35
|
+
| 6 | Signatures, JWS, webhook envelopes | **No** | ML-DSA-65 or SLH-DSA, plus HMAC-SHA-256 on webhooks | Yes | Integrity risk, not HNDL. Recorded here only so its absence is deliberate |
|
|
36
|
+
| 7 | Evidence bundle and SBOM | **No** | Published deliberately | Not applicable | Public by design |
|
|
37
|
+
| 8 | Keys held on a PKCS#11 token via `kxco-pq-hsm` | Yes, but outside this boundary | `CKA_EXTRACTABLE=false` | Hardware control | The vendor's channel to the token is the vendor's, per `BOUNDARY.md` |
|
|
38
|
+
|
|
39
|
+
## The two rows that matter
|
|
40
|
+
|
|
41
|
+
**Rows 1 and 2 are the real HNDL exposure of this product, and neither is
|
|
42
|
+
solved by a post-quantum algorithm.**
|
|
43
|
+
|
|
44
|
+
A private key is confidential for its entire life, which is exactly the
|
|
45
|
+
long-lived requirement HNDL describes. An adversary who harvests a stored
|
|
46
|
+
ML-DSA private key today does not need a quantum computer at all; they need the
|
|
47
|
+
key. The algorithm being post-quantum protects the signature against
|
|
48
|
+
cryptanalysis, not the key against theft. So the strongest post-quantum
|
|
49
|
+
posture in this package does nothing for its largest confidentiality asset.
|
|
50
|
+
|
|
51
|
+
Row 2 is worse in one specific way. A master secret under HKDF-SHA-512
|
|
52
|
+
regenerates every derived keypair, for every `info` string, past and future. It
|
|
53
|
+
is a single point whose compromise is not bounded in time. That is the correct
|
|
54
|
+
tradeoff for reproducible keys and pinnable conformance vectors, and it is the
|
|
55
|
+
right design, but the concentration of risk should be visible rather than
|
|
56
|
+
implied.
|
|
57
|
+
|
|
58
|
+
Neither row has a cryptographic mitigation available inside a library. What
|
|
59
|
+
exists is the escape in row 8: `kxco-pq-hsm` generates keys on a PKCS#11 token
|
|
60
|
+
with `CKA_EXTRACTABLE=false`, after which this package handles verification
|
|
61
|
+
only and touches no secret. **A deployment with a long-lived confidentiality
|
|
62
|
+
requirement on its keys should be using that path, and this register exists in
|
|
63
|
+
part to say so.**
|
|
64
|
+
|
|
65
|
+
## What this register does not cover
|
|
66
|
+
|
|
67
|
+
Callers. This package has no visibility into what a caller encrypts, how long
|
|
68
|
+
they need it confidential, or where they put a key. Rows 3 and 4 are marked as
|
|
69
|
+
the caller's to determine because they genuinely are, and a register claiming
|
|
70
|
+
otherwise would be inventing knowledge it does not have.
|
|
71
|
+
|
|
72
|
+
The absence of long-term validation, recorded as category 10 of
|
|
73
|
+
`CRYPTO-INVENTORY.md`, is an adjacent gap rather than an HNDL one. No
|
|
74
|
+
timestamps, no validity windows, no revocation. It bears on whether an old
|
|
75
|
+
signature can still be trusted, not on whether old ciphertext can be read.
|
|
76
|
+
|
|
77
|
+
## Review
|
|
78
|
+
|
|
79
|
+
This register is a claim about a shipped version and it ages. It should be
|
|
80
|
+
re-read whenever a new cryptographic use case enters the package, whenever a
|
|
81
|
+
category in `CRYPTO-INVENTORY.md` changes status, and on any change to how
|
|
82
|
+
seeds or master secrets are handled.
|
|
83
|
+
|
|
84
|
+
Last reviewed against 1.7.2 on 2026-09-09.
|
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/PQCMM.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# PQC Maturity Model self-assessment
|
|
2
|
+
|
|
3
|
+
PKI Consortium PQC Maturity Model (PQCMM), assessed against the model as
|
|
4
|
+
published at <https://pkic.org/wg/pqc/pqcmm/>.
|
|
5
|
+
|
|
6
|
+
**Declared level: 3 (Advanced).**
|
|
7
|
+
|
|
8
|
+
A self-assessment is indicative, not authoritative. It is the vendor's own view
|
|
9
|
+
and it has not been independently verified. The model says relying parties
|
|
10
|
+
should treat a self-assessed level as a signal rather than a guarantee, and that
|
|
11
|
+
is how this document should be read. Where a criterion is not met, this document
|
|
12
|
+
says so and names what is missing rather than reporting a partial as a pass.
|
|
13
|
+
|
|
14
|
+
## Scope
|
|
15
|
+
|
|
16
|
+
| Field | Value |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Product | `kxco-post-quantum` |
|
|
19
|
+
| Version assessed | 1.7.2 |
|
|
20
|
+
| Commit | `90b5778b4d19ed0f78715e143a6a50b9170b70f9`, clean tree |
|
|
21
|
+
| Registry | <https://www.npmjs.com/package/kxco-post-quantum> |
|
|
22
|
+
| Licence | Apache-2.0 |
|
|
23
|
+
| Assurance method | Self-assessment |
|
|
24
|
+
| Date | 2026-09-09 |
|
|
25
|
+
|
|
26
|
+
A PQCMM assessment applies to a single product. The other `kxco-pq-*` packages
|
|
27
|
+
depend on this one and are not covered here; each needs its own assessment.
|
|
28
|
+
|
|
29
|
+
## Evidence available to an assessor
|
|
30
|
+
|
|
31
|
+
Everything cited below is a permanent unauthenticated URL. No account, no
|
|
32
|
+
request to us, no expiring artifact.
|
|
33
|
+
|
|
34
|
+
| Artefact | Path under the release |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Evidence manifest | `releases/latest/download/manifest-node24.x.json` |
|
|
37
|
+
| Evidence bundle | `releases/latest/download/evidence-node24.x.zip` |
|
|
38
|
+
| CycloneDX SBOM | `releases/latest/download/sbom.cyclonedx.json` |
|
|
39
|
+
| In-toto attestation | `releases/latest/download/evidence.intoto.jsonl` |
|
|
40
|
+
| Release signing key | `releases/latest/download/release-signing-key.pub.hex` |
|
|
41
|
+
|
|
42
|
+
Base: `https://github.com/KnightsbridgeAIQ/kxco-post-quantum/`
|
|
43
|
+
|
|
44
|
+
Two documents in the repository carry the Level 3 evidence and are cited
|
|
45
|
+
throughout: `CRYPTO-INVENTORY.md` for the ten-category cryptographic inventory,
|
|
46
|
+
and `HNDL.md` for the harvest-now-decrypt-later exposure register.
|
|
47
|
+
|
|
48
|
+
The manifest pins the commit, the toolchain (Node 24.20.0, OpenSSL 3.5.7), the
|
|
49
|
+
backend, the dependency versions, and every step with its exact command, its
|
|
50
|
+
result and its duration. Each file in the bundle carries its SHA-256. The
|
|
51
|
+
conformance and interop runs are reproducible from the commands recorded in the
|
|
52
|
+
manifest rather than taken on our word.
|
|
53
|
+
|
|
54
|
+
## Level 1, Initial: MET
|
|
55
|
+
|
|
56
|
+
| # | Criterion | Answer | Evidence |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| 1 | A quantum-safe algorithm in a released build | Yes | ML-DSA-65 (FIPS 204), ML-KEM-768 (FIPS 203), SLH-DSA-SHA2-192s (FIPS 205) in 1.7.2 on the public npm stable channel. Category 5 sets ML-DSA-87 and ML-KEM-1024 also ship. |
|
|
59
|
+
| 2 | The feature can be enabled | Yes | Available on import. No flag, no custom build, no source modification. |
|
|
60
|
+
| 3 | Documented for evaluation | Yes | `README.md`, with a per-function API reference. |
|
|
61
|
+
|
|
62
|
+
Release channel is stable and public, available to all consumers, with no
|
|
63
|
+
programme enrolment. Ten SLH-DSA parameter sets are exposed, not one.
|
|
64
|
+
|
|
65
|
+
## Level 2, Foundational: MET
|
|
66
|
+
|
|
67
|
+
| # | Criterion | Answer | Evidence |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| 1 | Quantum-safe algorithms in core production functionality | Yes | Signing, key encapsulation, key derivation and fingerprinting are the product's core, not a preview channel. |
|
|
70
|
+
| 2 | Compatibility with a published standard from a recognised body | Yes | NIST FIPS 203, 204 and 205. |
|
|
71
|
+
| 3a | Standards-conformance testing | Yes | 2,103 NIST ACVP vectors: 1,793 passed, 0 failed, 310 skipped. Every skip is this library refusing a pre-hash weaker than the parameter set, listed individually with its reason. A skip is not counted as a pass. See `CONFORMANCE.md` and manifest steps `acvp`, `acvp-fips205`, `acvp-fips205-siggen`. |
|
|
72
|
+
| 3b | Interoperability against an independent conformant implementation | Yes, four of them | 225 cross-implementation checks, 0 failed, both directions, with negative controls, against liboqs, Bouncy Castle and two pure-Python implementations. Run against both backends. Manifest step `interop`. |
|
|
73
|
+
| 3c | Functional security testing as configured for production | Yes | `npm test` recorded in the manifest, plus the fuzz corpus under `fuzz/`. |
|
|
74
|
+
| 3d | Smoke performance test under representative load | Yes | `BENCHMARKS.md`: per-algorithm p95 and p99 on both backends, on x86-64 and arm64, plus memory. |
|
|
75
|
+
| 4 | Documented for production use including known limitations | Yes | `THREAT-MODEL.md` for what is and is not defended, `BOUNDARY.md` for which cryptography this package performs versus depends on versus offers, plus `LIFECYCLE.md` and `MIGRATION.md`. |
|
|
76
|
+
|
|
77
|
+
Level 2 asks for CAVP or ACVTS results "or equivalent". Ours are the NIST ACVP
|
|
78
|
+
vectors run by us and published with the commands to reproduce them. That is
|
|
79
|
+
equivalent evidence, not a NIST certificate. **There is no CAVP certificate
|
|
80
|
+
number for this product.** An assessor checking the CAVP or CMVP database will
|
|
81
|
+
find nothing, and should record that.
|
|
82
|
+
|
|
83
|
+
## Level 3, Advanced: MET
|
|
84
|
+
|
|
85
|
+
| # | Criterion | Answer | Evidence |
|
|
86
|
+
|---|---|---|---|
|
|
87
|
+
| 1 | Full cryptographic inventory across every applicable taxonomy category | Yes | `CRYPTO-INVENTORY.md` covers all ten categories. Four are marked not applicable with a written reason; none is omitted. |
|
|
88
|
+
| 2 | Non-quantum-safe features documented and flagged | Yes | `BOUNDARY.md`, "The gaps, collected", and the classical rows of `CRYPTO-INVENTORY.md` categories 1, 8 and 9. |
|
|
89
|
+
| 3 | SBOM or equivalent component inventory | Yes | CycloneDX SBOM as a release asset at a permanent URL, generated by `npm sbom` from the tree that was published. Manifest step `sbom`. |
|
|
90
|
+
| 4 | Crypto agility, two algorithms selectable in the same role | Yes | ML-DSA-65 and SLH-DSA-SHA2-192s are both available in the signature role by configuration, which is two different algorithms rather than two parameter sets of one. `AGILITY.md` separates the replacement plan, the implemented mechanism and the interoperable transition, and states what the package does not give you. |
|
|
91
|
+
| 5 | Documented HNDL exposure register | Yes | `HNDL.md`. Eight data flows, each with its confidentiality horizon, its protecting algorithm and its residual. |
|
|
92
|
+
|
|
93
|
+
Writing the inventory against the taxonomy produced two findings that this
|
|
94
|
+
package's own audit documents had not stated, and both are recorded rather than
|
|
95
|
+
smoothed over.
|
|
96
|
+
|
|
97
|
+
**The library generates no randomness in its JavaScript backend.** Every
|
|
98
|
+
keypair comes from a caller-supplied seed, or from a master secret through
|
|
99
|
+
HKDF-SHA-512. So in that backend the entropy quality of every private key is a
|
|
100
|
+
property of the caller, not of this package, and the package cannot detect a
|
|
101
|
+
weak seed. The native backend differs: `crypto.generateKeyPairSync` uses the
|
|
102
|
+
OpenSSL DRBG. `CRYPTO-INVENTORY.md` category 6.
|
|
103
|
+
|
|
104
|
+
**The largest HNDL exposure in this product has no post-quantum mitigation.**
|
|
105
|
+
Private keys and, more sharply, the master secret behind `keypairFromMaster`
|
|
106
|
+
are confidential for their whole life, which is precisely the long-lived
|
|
107
|
+
requirement HNDL describes. A post-quantum algorithm protects the signature
|
|
108
|
+
against cryptanalysis and does nothing for the key against theft. The available
|
|
109
|
+
answer is the PKCS#11 path in `kxco-pq-hsm`, not an algorithm choice here.
|
|
110
|
+
`HNDL.md`, rows 1 and 2.
|
|
111
|
+
|
|
112
|
+
## Level 4, Managed: NOT MET
|
|
113
|
+
|
|
114
|
+
| # | Criterion | Answer | Evidence or gap |
|
|
115
|
+
|---|---|---|---|
|
|
116
|
+
| 1 | CBOM maintained | **No** | The product publishes an SBOM, not a CBOM. `CRYPTO-INVENTORY.md` now records each algorithm with its protocol context and usage purpose, which is the human-readable half of what a CBOM carries, but it is prose rather than a machine-readable CBOM and key sizes are stated by parameter set rather than per field. Closing this means emitting a CycloneDX CBOM as a release asset, which is a build change rather than a document. |
|
|
117
|
+
| 2 | Zero-legacy capability across every in-scope component | **Not determined** | Not assessed against the model's wording, which extends to boot, firmware update signing, hardware-bound operations and internal diagnostics. Claiming it without that determination would be an overclaim. |
|
|
118
|
+
| 3 | Symmetric and hash strengths adequate beyond CRQC availability | Yes | SHA-256 for key identifiers and webhook HMAC, SHA-512 under HKDF for seed derivation, via `@noble/hashes` 2.4.0. Per-use detail in `CRYPTO-INVENTORY.md`. |
|
|
119
|
+
| 4 | Hybrid and composite support documented, with contexts | Partial | Hybrid is documented: `webhook` performs HMAC plus ML-DSA-65 delivery signing, and `deriveSeed` exists to combine an ML-KEM shared secret with a classical secret through a KDF. Composite, in the algorithm-fused sense, is not supported and is not currently stated as unsupported. |
|
|
120
|
+
|
|
121
|
+
## Level 5, Optimized: NOT MET
|
|
122
|
+
|
|
123
|
+
| # | Criterion | Answer | Evidence or gap |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| 1 | Quantum-safe by default, legacy requires explicit enablement | Yes | There is no classical algorithm to fall back to. The default parameter set is Category 3. |
|
|
126
|
+
| 2 | Benchmarked and tuned against operational requirements | Yes | `BENCHMARKS.md`. Two figures stated rather than hidden: ML-DSA signing keeps a rejection-sampling tail on either backend, and SLH-DSA-SHA2-192s signs in seconds rather than milliseconds. |
|
|
127
|
+
| 3 | Primarily follows standards from a recognised body | Yes | FIPS 203, 204 and 205. |
|
|
128
|
+
| 4 | **Independently verified or certified** | **No** | No FIPS 140 validation. No Common Criteria evaluation. The underlying `@noble/post-quantum` 0.7.0 has no published third-party formal security analysis covering the implemented PQC algorithms, so the library-based route in the model's wording is not available either. |
|
|
129
|
+
|
|
130
|
+
Criterion 4 is the binding constraint and it cannot be closed by documentation.
|
|
131
|
+
It requires a paid engagement: a CAVP or CMVP validation through an accredited
|
|
132
|
+
laboratory, a Common Criteria evaluation, or a published independent audit of
|
|
133
|
+
the cryptographic implementation.
|
|
134
|
+
|
|
135
|
+
## Assessment methodology record
|
|
136
|
+
|
|
137
|
+
The model requires this record to accompany an assessment.
|
|
138
|
+
|
|
139
|
+
- **Evidence method.** Documentation review, plus the published evidence
|
|
140
|
+
manifest, plus verification of the licence state of every published package
|
|
141
|
+
against the public npm registry on 2026-09-09.
|
|
142
|
+
- **Cryptographic library.** `@noble/post-quantum` 0.7.0 and `@noble/hashes`
|
|
143
|
+
2.4.0 in the JavaScript backend; `node:crypto` on OpenSSL 3.5.7 in the native
|
|
144
|
+
backend. Both versions are recorded in the manifest. `@noble/post-quantum` is
|
|
145
|
+
maintained, and its PQC support is confirmed from its public repository. It
|
|
146
|
+
has **not** been independently audited. Version 0.7.1 is deliberately not
|
|
147
|
+
adopted because it regressed SLH-DSA, so the dependency is pinned at 0.7.0.
|
|
148
|
+
- **Reproduction attempts.** All conformance, interop, test and SBOM steps in
|
|
149
|
+
the manifest ran in CI from a clean tree at commit `90b5778`, on Node
|
|
150
|
+
24.20.0, linux-x64, on 2026-09-08. Every step reports `ok: true`, and
|
|
151
|
+
`sampling.full` is `true`.
|
|
152
|
+
- **Public database checks.** npm registry, all sixteen published `kxco-*`
|
|
153
|
+
packages, 2026-09-09: every one reports `Apache-2.0`. NIST CAVP and CMVP: no
|
|
154
|
+
certificate exists for this product, confirmed rather than assumed.
|
|
155
|
+
- **Unverified claims.** Every criterion answered Yes above is vendor-stated and
|
|
156
|
+
not independently verified. That is what a self-assessment is. The manifest
|
|
157
|
+
and the bundle exist so that an assessor can convert these into verified
|
|
158
|
+
findings without asking us for anything.
|
|
159
|
+
|
|
160
|
+
## Corrections
|
|
161
|
+
|
|
162
|
+
If any statement here is wrong, it should be corrected here first, and the
|
|
163
|
+
correction dated. Facts about this package that are commonly recorded wrongly,
|
|
164
|
+
with the one-line check for each, are listed at the top of `README.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
|
---
|
|
@@ -333,7 +336,7 @@ Install this package directly when you need ML-DSA or ML-KEM without the rest of
|
|
|
333
336
|
|
|
334
337
|
## Security
|
|
335
338
|
|
|
336
|
-
Cryptographic operations delegate entirely to [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) and [`@noble/hashes`](https://github.com/paulmillr/noble-hashes)
|
|
339
|
+
Cryptographic operations delegate entirely to [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) and [`@noble/hashes`](https://github.com/paulmillr/noble-hashes); this package does not reimplement any NIST primitive. `@noble/post-quantum` has never been audited by a third party. It has been self-audited by its maintainer (v0.6.1, April 2026). The other Noble packages were audited separately, at different dates, and none of those engagements covered the post-quantum package: `@noble/hashes` by Cure53 in January 2022, `@noble/curves` by Trail of Bits in February 2023, Kudelski Security in September 2023 and Cure53 in September 2024, and `@noble/ciphers` by Cure53 in September 2024. See [AUDIT.md](./AUDIT.md) for the full posture.
|
|
337
340
|
|
|
338
341
|
To report a vulnerability: [open a private security advisory](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/security/advisories/new) or email **john@knightsbridgelaw.com**. Acknowledgement within 2 business days, triage decision within 5. Full policy, including safe harbour for good-faith research: <https://kxco.ai/security>.
|
|
339
342
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kxco-post-quantum",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
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",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"dilithium",
|
|
13
13
|
"kyber",
|
|
14
14
|
"nist",
|
|
15
|
+
"acvp",
|
|
15
16
|
"fips-203",
|
|
16
17
|
"fips-204",
|
|
17
18
|
"fips-205",
|
|
@@ -61,61 +62,79 @@
|
|
|
61
62
|
"exports": {
|
|
62
63
|
".": {
|
|
63
64
|
"types": "./src/index.d.ts",
|
|
64
|
-
"import": "./src/index.js"
|
|
65
|
+
"import": "./src/index.js",
|
|
66
|
+
"default": "./src/index.js"
|
|
65
67
|
},
|
|
66
68
|
"./ml-dsa": {
|
|
67
69
|
"types": "./src/ml-dsa.d.ts",
|
|
68
|
-
"import": "./src/ml-dsa.js"
|
|
70
|
+
"import": "./src/ml-dsa.js",
|
|
71
|
+
"default": "./src/ml-dsa.js"
|
|
69
72
|
},
|
|
70
73
|
"./ml-dsa-87": {
|
|
71
74
|
"types": "./src/ml-dsa-87.d.ts",
|
|
72
|
-
"import": "./src/ml-dsa-87.js"
|
|
75
|
+
"import": "./src/ml-dsa-87.js",
|
|
76
|
+
"default": "./src/ml-dsa-87.js"
|
|
73
77
|
},
|
|
74
78
|
"./ml-kem": {
|
|
75
79
|
"types": "./src/ml-kem.d.ts",
|
|
76
|
-
"import": "./src/ml-kem.js"
|
|
80
|
+
"import": "./src/ml-kem.js",
|
|
81
|
+
"default": "./src/ml-kem.js"
|
|
77
82
|
},
|
|
78
83
|
"./ml-kem-1024": {
|
|
79
84
|
"types": "./src/ml-kem-1024.d.ts",
|
|
80
|
-
"import": "./src/ml-kem-1024.js"
|
|
85
|
+
"import": "./src/ml-kem-1024.js",
|
|
86
|
+
"default": "./src/ml-kem-1024.js"
|
|
81
87
|
},
|
|
82
88
|
"./slh-dsa": {
|
|
83
89
|
"types": "./src/slh-dsa.d.ts",
|
|
84
|
-
"import": "./src/slh-dsa.js"
|
|
90
|
+
"import": "./src/slh-dsa.js",
|
|
91
|
+
"default": "./src/slh-dsa.js"
|
|
85
92
|
},
|
|
86
93
|
"./derive": {
|
|
87
94
|
"types": "./src/derive.d.ts",
|
|
88
|
-
"import": "./src/derive.js"
|
|
95
|
+
"import": "./src/derive.js",
|
|
96
|
+
"default": "./src/derive.js"
|
|
89
97
|
},
|
|
90
98
|
"./webhook": {
|
|
91
99
|
"types": "./src/webhook.d.ts",
|
|
92
|
-
"import": "./src/webhook.js"
|
|
100
|
+
"import": "./src/webhook.js",
|
|
101
|
+
"default": "./src/webhook.js"
|
|
93
102
|
},
|
|
94
103
|
"./kid": {
|
|
95
104
|
"types": "./src/kid.d.ts",
|
|
96
|
-
"import": "./src/kid.js"
|
|
105
|
+
"import": "./src/kid.js",
|
|
106
|
+
"default": "./src/kid.js"
|
|
97
107
|
},
|
|
98
108
|
"./seed": {
|
|
99
109
|
"types": "./src/seed.d.ts",
|
|
100
|
-
"import": "./src/seed.js"
|
|
110
|
+
"import": "./src/seed.js",
|
|
111
|
+
"default": "./src/seed.js"
|
|
101
112
|
},
|
|
102
113
|
"./jws": {
|
|
103
114
|
"types": "./src/jws.d.ts",
|
|
104
|
-
"import": "./src/jws.js"
|
|
115
|
+
"import": "./src/jws.js",
|
|
116
|
+
"default": "./src/jws.js"
|
|
105
117
|
},
|
|
106
118
|
"./backend": {
|
|
107
119
|
"types": "./src/backend.d.ts",
|
|
108
|
-
"import": "./src/backend.js"
|
|
120
|
+
"import": "./src/backend.js",
|
|
121
|
+
"default": "./src/backend.js"
|
|
109
122
|
}
|
|
110
123
|
},
|
|
111
124
|
"files": [
|
|
125
|
+
"AGILITY.md",
|
|
112
126
|
"BENCHMARKS.md",
|
|
127
|
+
"BOUNDARY.md",
|
|
113
128
|
"CHANGELOG.md",
|
|
114
129
|
"CONFORMANCE.md",
|
|
130
|
+
"CRYPTO-INVENTORY.md",
|
|
115
131
|
"DEPENDENCIES.md",
|
|
132
|
+
"HNDL.md",
|
|
116
133
|
"LICENCE-PRODUCT.md",
|
|
117
134
|
"LICENSE",
|
|
135
|
+
"LIFECYCLE.md",
|
|
118
136
|
"MIGRATION.md",
|
|
137
|
+
"PQCMM.md",
|
|
119
138
|
"README.md",
|
|
120
139
|
"SECURITY.md",
|
|
121
140
|
"THREAT-MODEL.md",
|
|
@@ -129,6 +148,7 @@
|
|
|
129
148
|
"@noble/post-quantum": "0.7.0"
|
|
130
149
|
},
|
|
131
150
|
"scripts": {
|
|
151
|
+
"fuzz": "node fuzz/fuzz.mjs",
|
|
132
152
|
"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
153
|
"test:vectors": "node test/run-vectors.js",
|
|
134
154
|
"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'
|