kxco-pq-attest 2.0.3 → 2.0.5

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +108 -59
  3. package/package.json +12 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.5
4
+
5
+ Documentation. No source change.
6
+
7
+ The release-integrity lines now name the version that SLSA provenance and
8
+ the CycloneDX SBOM start from, and the keyword list drops `quantum-safe`,
9
+ which was removed on purpose in an earlier release.
10
+
11
+ Every GitHub Action in CI is now pinned by commit SHA, as the page states.
12
+
13
+ ## 2.0.4
14
+
15
+ Documentation. No source change.
16
+
17
+ **The npm page leads with what the package proves.** The first screen now says what a signed envelope proves, who can check it and
18
+ for how long, the evidence underneath it and the migration dates set by NIST,
19
+ Executive Order 14412, OMB M-26-15 and the UK NCSC.
20
+
21
+ A family table maps every KXCO package to the job it does, and a new For
22
+ institutions section sets out the operated services and how to reach us. The
23
+ evidence documents are unchanged and linked from the page.
24
+
3
25
  ## 2.0.3
4
26
 
5
27
  Documentation. No source change.
package/README.md CHANGED
@@ -1,40 +1,34 @@
1
1
  # kxco-pq-attest
2
2
 
3
+ **Post-quantum document signing any counterparty can verify offline, years later, with nobody to ask.**
4
+
3
5
  [![npm](https://img.shields.io/npm/v/kxco-pq-attest?label=npm&color=b0964f)](https://www.npmjs.com/package/kxco-pq-attest)
6
+ [![downloads](https://img.shields.io/npm/dm/kxco-pq-attest?label=downloads&color=b0964f)](https://www.npmjs.com/package/kxco-pq-attest)
7
+ [![NIST ACVP](https://img.shields.io/badge/NIST_ACVP-1,793_passed,_0_failed-2ea44f)](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md)
8
+ [![npm provenance](https://img.shields.io/badge/npm-provenance-2ea44f)](https://www.npmjs.com/package/kxco-pq-attest)
4
9
  [![Socket](https://socket.dev/api/badge/npm/package/kxco-pq-attest)](https://socket.dev/npm/package/kxco-pq-attest)
5
10
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE)
6
11
  [![node](https://img.shields.io/node/v/kxco-pq-attest.svg)](https://nodejs.org)
7
12
 
8
- Post-quantum document signing and on-chain attestation.
13
+ Signs arbitrary data (strings, Buffers, objects) with ML-DSA-65 (NIST FIPS 204) and produces a self-contained JSON envelope any counterparty can verify without trust delegation. Optionally anchors the envelope hash on Armature L1 via the KXCO relay, creating a permanent timestamped on-chain record.
9
14
 
10
- Signs arbitrary data — strings, Buffers, objects — with ML-DSA-65 (NIST FIPS 204) and produces a self-contained JSON envelope any counterparty can verify without trust delegation. Optionally anchors the envelope hash on Armature L1 via the KXCO relay, creating a permanent timestamped on-chain record.
15
+ - **Verification needs nothing from us.** `verify(envelope, publicKey)` returns from the JSON alone: no endpoint, no account and no licence, now or in ten years.
16
+ - **Every field is bound.** Payload, key id and issue time sit inside the signed bytes behind a version prefix, so no field can be reordered or replayed against a different timestamp.
17
+ - **Survives key rotation.** Each envelope names its key, so an envelope signed under a since-rotated key still verifies years later.
18
+ - **Time the chain itself vouches for.** Anchor on Armature L1 and the transaction hash and block number travel inside the signed message, still checkable by an air-gapped verifier.
19
+ - **Hybrid when a policy asks for it.** `attest(payload, keypair, { classical })` adds an Ed25519 or ECDSA-P256 co-signature over the same message, and `verifyAsync(envelope, key, { requireBoth: true })` demands both.
20
+ - **Three levels of proof.** `signature` and `anchored` verify offline for good; `anchored+live` adds the KXCO registry's answer that the signing key is still trusted now.
21
+ - **Runs where you do.** Node.js 20.19 and later, and Cloudflare Workers.
22
+ - **Proven underneath.** 1,793 NIST ACVP vectors passed, 0 failed, and 225 interoperability checks against liboqs, Bouncy Castle and the Python reference implementations, 0 failed, in [`kxco-post-quantum`](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md).
23
+ - **A supply chain you can check.** SLSA provenance and a CycloneDX SBOM on every release since 1.1.5, third-party dependencies pinned to exact versions, and every GitHub Action pinned by commit SHA.
11
24
 
12
- ## Release integrity
25
+ **The migration has dates.**
26
+
27
+ - **NIST** published [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final), [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) and [FIPS 205](https://csrc.nist.gov/pubs/fips/205/final) in August 2024.
28
+ - **United States:** [Executive Order 14412](https://www.federalregister.gov/documents/2026/06/25/2026-12909/securing-the-nation-against-advanced-cryptographic-attacks), signed on 22 June 2026, moves federal high-value and high-impact systems to post-quantum key establishment by 31 December 2030 and to post-quantum signatures by 31 December 2031. [OMB M-26-15](https://www.whitehouse.gov/wp-content/uploads/2026/06/M-26-15-Execution-of-the-Migration-to-Post-Quantum-Cryptography.pdf) requires PQC-agile libraries for all new applications.
29
+ - **United Kingdom:** the [NCSC](https://www.ncsc.gov.uk/guidance/pqc-migration-timelines) sets 2028, 2031 and 2035 as its migration milestones.
13
30
 
14
- Every release of this package is checkable without asking us for anything.
15
-
16
- - **Provenance.** Each release carries a SLSA provenance attestation tying the
17
- published tarball to the commit and workflow that built it. Verify with
18
- `npm audit signatures`, or read it directly from
19
- `registry.npmjs.org/-/npm/v1/attestations/kxco-pq-attest@<version>`.
20
- - **Bill of materials.** A CycloneDX SBOM is published as a GitHub Release asset
21
- at `releases/download/v<version>/sbom.cyclonedx.json`, a permanent
22
- unauthenticated URL. Not an expiring build artifact.
23
- - **Pinned where it matters.** Third-party dependencies are pinned to exact
24
- versions, never ranges, so the code that performs the cryptography cannot
25
- change without a release. Sibling `kxco-*` packages sit on caret ranges
26
- deliberately: it means a correctness fix in the base package reaches you
27
- without a release of every package above it. That is not theoretical. When
28
- `@noble/post-quantum` 0.7.1 was found to fail NIST SLH-DSA verification
29
- vectors, the revert in the base package propagated here on the next install.
30
- Every GitHub Action is pinned by 40-character commit SHA.
31
- - **Conformance underneath.** The cryptography comes from
32
- [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum), which
33
- is run against **2,103 NIST ACVP vectors: 1,793 passed, 0 failed, 310 skipped** and a **225-check
34
- cross-implementation interoperability matrix** against liboqs, Bouncy Castle
35
- and two pure-Python implementations, in both directions and with negative
36
- controls. Its published tarball also rebuilds bit-for-bit from its own tag,
37
- verified in CI on every run.
31
+ [Quick start](#quick-start) · [Envelope format](#envelope-format) · [For institutions](#for-institutions) · [Assessment notes](./ASSESSMENT.md) · [Changelog](./CHANGELOG.md) · [kxco.ai](https://kxco.ai)
38
32
 
39
33
  ## When to use this
40
34
 
@@ -71,9 +65,12 @@ const result = verify(envelope, keypair.publicKey)
71
65
 
72
66
  ```js
73
67
  import { attest } from 'kxco-pq-attest'
74
- import { createChain } from 'kxco-chain' // Armature L1 relay client
68
+ import { KxcoChain } from 'kxco-pq-chain' // Armature L1 relay client
75
69
 
76
- const chain = createChain({ endpoint: 'https://chain.kxco.ai' })
70
+ const chain = new KxcoChain({
71
+ identity: institutionIdentity, // a KxcoIdentity from kxco-pq-sdk
72
+ licenceKey: process.env.KXCO_LICENCE_KEY, // the hosted anchoring service
73
+ })
77
74
 
78
75
  const envelope = await attest(
79
76
  { ref: 'INV-2024-0042', amount: 50000 },
@@ -83,6 +80,26 @@ const envelope = await attest(
83
80
  // envelope.chainAnchor: { txHash: '0x...', blockNumber: 1234567 }
84
81
  ```
85
82
 
83
+ ## For institutions
84
+
85
+ The cryptography is free under Apache-2.0, works offline and needs nothing from
86
+ KXCO, now or in ten years. What KXCO sells is the part that has to be operated:
87
+ an answer about the present.
88
+
89
+ | Service | What you get |
90
+ |---|---|
91
+ | Hosted key registry | Whether a key is active, revoked or rotated, answered at verification time |
92
+ | Meta-transaction relay | KXCO validates your signed intent, pays the gas and submits it, so you never hold a token or run a node |
93
+ | On-chain anchoring | A timestamp on Armature L1 that the chain itself has verified |
94
+ | Live revocation | `anchored+live` verification, which confirms the signing key is still trusted now |
95
+ | Support and SLA | Availability commitments, an escalation path and a named contact |
96
+
97
+ Priced in USD, per seat, per year. No tokens, no nodes and no wallets. The line
98
+ between free and paid is set out in
99
+ [LICENCE-PRODUCT.md](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/LICENCE-PRODUCT.md).
100
+
101
+ **Talk to us: [admin@kxco.ai](mailto:admin@kxco.ai)** · [kxco.ai](https://kxco.ai)
102
+
86
103
  ## API
87
104
 
88
105
  ### `attest(payload, keypair, options?)`
@@ -96,6 +113,7 @@ Signs `payload` with ML-DSA-65 and returns an envelope. Returns a Promise.
96
113
  | `options.anchor` | `boolean` | Default `false`. When `true`, anchors the envelope hash on-chain. Requires `chain`. |
97
114
  | `options.purpose` | `string` | Optional label stored with the on-chain anchor (e.g. `'trade-confirm'`). |
98
115
  | `options.chain` | `object` | Armature L1 relay client. Required when `anchor: true`. Must implement `anchorAttestation({ payloadHash, purpose })`. |
116
+ | `options.classical` | `{ alg, privateKey, publicKey }` | Optional Ed25519 or ECDSA-P256 co-signature over the same message the ML-DSA-65 signature covers. Generate the pair with `generateClassicalKeypair(alg)`, which uses WebCrypto and runs in Node, Workers and browsers. |
99
117
 
100
118
  When `anchor: true`, the envelope hash (SHA-256 of the signed JSON) is posted to the relay and the result is attached to the envelope as `chainAnchor`.
101
119
 
@@ -120,44 +138,75 @@ Verifies the ML-DSA-65 signature on an envelope. Returns synchronously.
120
138
 
121
139
  `payload` is the raw bytes of the original data. For strings, decode with `new TextDecoder().decode(result.payload)`.
122
140
 
141
+ ---
142
+
143
+ ### `verifyAsync(envelope, publicKey, opts?)`
144
+
145
+ The full verifier, for the checks that need more than the JSON.
146
+
147
+ | Option | Type | Description |
148
+ |-----------|------|-------------|
149
+ | `opts.mode` | `'signature' \| 'anchored' \| 'anchored+live'` | `anchored+live` asks the KXCO registry whether the signing key is still trusted now, and fails closed. |
150
+ | `opts.requireBoth` | `boolean` | Require a valid classical co-signature as well as the ML-DSA-65 signature. |
151
+ | `opts.classicalPublicKey` | `Uint8Array` | Pin the classical key rather than accepting the one the envelope names. |
152
+
123
153
  ## Envelope format
124
154
 
125
155
  ```json
126
156
  {
127
- "kxco-attest": "1",
157
+ "kxco-attest": "2",
128
158
  "payload": "<base64url-encoded bytes>",
159
+ "alg": "ML-DSA-65",
129
160
  "kid": "<ML-DSA-65 public key fingerprint>",
130
- "issuedAt": "2026-05-28T09:32:11.000Z",
131
- "signature": "<base64url-encoded ML-DSA-65 signature>",
132
- "chainAnchor": {
161
+ "sig": "<base64url-encoded ML-DSA-65 signature>",
162
+ "issuedAt": "2026-09-29T09:32:11.000Z",
163
+ "chainId": 1111111,
164
+ "anchor": {
133
165
  "txHash": "0xabc123...",
134
166
  "blockNumber": 1234567
135
- }
167
+ },
168
+ "verifyModeHint": "anchored"
136
169
  }
137
170
  ```
138
171
 
139
- `chainAnchor` is present only when the envelope was anchored on-chain. The envelope is self-contained without it — `verify()` works from the JSON alone, with no external calls.
140
-
141
- The signing message is a deterministic concatenation: `kxco-attest-v1\n<payloadB64>\n<kid>\n<issuedAt>`. No field can be silently reordered or replayed against a different timestamp without invalidating the signature.
172
+ `anchor` is present when the envelope was anchored on-chain, `chainId` when the relay confirmed the chain, and `classical` when the envelope was co-signed. The envelope is self-contained either way: `verify()` works from the JSON alone, with no external calls.
173
+
174
+ The signing message is a deterministic concatenation behind a version prefix: `kxco-attest-v2`, then the payload, algorithm, kid, issue time, chain id, anchor transaction hash and block number, and the verify-mode hint, one per line. No field can be reordered, replayed against a different timestamp or given a different anchor without invalidating the signature.
175
+
176
+ ## The KXCO post-quantum family
177
+
178
+ This signs, so anyone can prove where a payload came from and that it is
179
+ unchanged. The rest of the family covers the jobs around it:
180
+
181
+ | You need to | Install |
182
+ |---|---|
183
+ | Put the whole stack in one install | [`kxco-pq`](https://www.npmjs.com/package/kxco-pq) |
184
+ | Use ML-DSA, ML-KEM and SLH-DSA directly | [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum) |
185
+ | Keep signing keys on the HSM you already run | [`kxco-pq-hsm`](https://www.npmjs.com/package/kxco-pq-hsm) |
186
+ | Sign a document or record anyone can verify offline | [`kxco-pq-attest`](https://www.npmjs.com/package/kxco-pq-attest) |
187
+ | Keep a tamper-evident audit trail | [`kxco-pq-audit`](https://www.npmjs.com/package/kxco-pq-audit) |
188
+ | Verify a signature in a browser, with no server | [`kxco-verify`](https://www.npmjs.com/package/kxco-verify) |
189
+ | Issue institution identity credentials | [`kxco-pq-sdk`](https://www.npmjs.com/package/kxco-pq-sdk) |
190
+ | Encrypt files and payloads to one or many recipients | [`kxco-pq-vault`](https://www.npmjs.com/package/kxco-pq-vault) |
191
+ | Encrypt Node streams and WebSockets | [`kxco-pq-tls`](https://www.npmjs.com/package/kxco-pq-tls) |
192
+ | Sign and verify webhooks | [`kxco-post-quantum-webhook`](https://www.npmjs.com/package/kxco-post-quantum-webhook) |
193
+ | Give an AI agent an identity a verified institution sponsors | [`kxco-pq-agent`](https://www.npmjs.com/package/kxco-pq-agent) |
194
+ | Have Armature L1 verify a signature in consensus | [`kxco-pq-chain`](https://www.npmjs.com/package/kxco-pq-chain) |
195
+ | Prove an envelope at three levels, offline to on-chain | [`kxco-pq-network`](https://www.npmjs.com/package/kxco-pq-network) |
196
+ | Generate and rotate keys from a terminal | [`kxco-pq-cli`](https://www.npmjs.com/package/kxco-pq-cli) |
197
+ | Find quantum-vulnerable cryptography in a dependency tree | [`kxco-pq-scan`](https://www.npmjs.com/package/kxco-pq-scan) |
198
+ | Fail the build when code reaches past the wrapper | [`eslint-plugin-kxco-pq`](https://www.npmjs.com/package/eslint-plugin-kxco-pq) |
142
199
 
143
- ## Where this fits
144
-
145
- This signs, so anyone can prove where a payload came from and that it has not
146
- changed. The envelope is readable by design: a counterparty verifies it without
147
- a key exchange, offline, years later.
148
-
149
- - [`kxco-pq-vault`](https://www.npmjs.com/package/kxco-pq-vault) when the payload must be unreadable as well as provable
150
- - [`kxco-pq-sdk`](https://www.npmjs.com/package/kxco-pq-sdk) to issue and verify identity credentials
151
- - [`kxco-pq-hsm`](https://www.npmjs.com/package/kxco-pq-hsm) for key generation, storage and rotation in hardware
152
-
153
- ## Part of the KXCO stack
200
+ ## Release integrity
154
201
 
155
- | Package | Purpose |
156
- |---------|---------|
157
- | [`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum) | ML-DSA-65 and ML-KEM-768 primitives (NIST FIPS 204/203) |
158
- | `kxco-pq-attest` | This package — payload signing and on-chain attestation |
159
- | `kxco-pq-vault` | Post-quantum encryption |
160
- | [`kxco-pq-sdk`](https://www.npmjs.com/package/kxco-pq-sdk) | KxcoIdentity credential issuance and verification |
202
+ Every release since 1.1.5 carries a SLSA provenance attestation tying the published tarball to
203
+ the commit and workflow that built it: verify with `npm audit signatures`, or read
204
+ it from `registry.npmjs.org/-/npm/v1/attestations/kxco-pq-attest@<version>`. A CycloneDX
205
+ SBOM is published, from v1.1.5, as a GitHub Release asset at
206
+ `releases/download/v<version>/sbom.cyclonedx.json`, a permanent unauthenticated
207
+ URL. Sibling `kxco-*` packages sit on caret ranges so a correctness fix in the
208
+ base package reaches you on the next install, with no release of every package
209
+ above it.
161
210
 
162
211
  ## Security
163
212
 
@@ -165,10 +214,10 @@ a key exchange, offline, years later.
165
214
 
166
215
  Evidenced, and reproducible on your own machine:
167
216
 
168
- - **2,103 NIST ACVP vectors** across FIPS 203, 204 and 205, pinned by digest: 1,793 passed, 0 failed, 310 skipped, where each skip is the library refusing a pre-hash weaker than the parameter set
169
- - **225 interoperability checks passed, 0 failed, 42 not applicable** against OpenSSL 3.5, liboqs, Bouncy Castle and dilithium-py/kyber-py, in both directions
170
- - **SLSA provenance** on every published release — verify with `npm audit signatures`
171
- - **CycloneDX SBOM** published with each release
217
+ - **1,793 NIST ACVP vectors passed, 0 failed** across FIPS 203, 204 and 205, pinned by digest, per [CONFORMANCE.md](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md). The other 310 are pairings the library refuses as weaker than the parameter set
218
+ - **225 interoperability checks passed, 0 failed**, against OpenSSL 3.5, liboqs, Bouncy Castle and dilithium-py/kyber-py, in both directions
219
+ - **SLSA provenance** on every release since 1.1.5: verify with `npm audit signatures`
220
+ - **CycloneDX SBOM** published with every release since 1.1.5
172
221
  - `npm run evidence` regenerates the whole bundle from source
173
222
 
174
223
  Dependency audit history is recorded in [AUDIT.md](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/AUDIT.md).
@@ -183,4 +232,4 @@ Apache-2.0 © 2026 KXCO by Knightsbridge
183
232
 
184
233
  ## Maintainers
185
234
 
186
- Shayne Heffernan and John Heffernan — [KXCO by Knightsbridge](https://kxco.ai)
235
+ Shayne Heffernan and John Heffernan, [KXCO by Knightsbridge](https://kxco.ai)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kxco-pq-attest",
3
- "version": "2.0.3",
4
- "description": "Post-quantum document signing and on-chain attestation. ML-DSA-65 (FIPS 204) over any payload, producing a self-contained JSON envelope a counterparty verifies offline. Anchor it on Armature L1 for a timestamped record the chain itself has verified.",
3
+ "version": "2.0.5",
4
+ "description": "Post-quantum document signing any counterparty can verify offline, years later. ML-DSA-65 (NIST FIPS 204) over any payload in a self-contained JSON envelope, with optional anchoring on Armature L1 for a timestamp the chain itself has verified.",
5
5
  "keywords": [
6
6
  "post-quantum",
7
7
  "pqc",
@@ -18,7 +18,16 @@
18
18
  "regulatory",
19
19
  "identity",
20
20
  "credential",
21
- "compliance"
21
+ "compliance",
22
+ "pqc-migration",
23
+ "digital-signature",
24
+ "document-signing",
25
+ "e-signature",
26
+ "timestamping",
27
+ "non-repudiation",
28
+ "tamper-evident",
29
+ "audit-trail",
30
+ "crypto-agility"
22
31
  ],
23
32
  "license": "Apache-2.0",
24
33
  "author": "Shayne Heffernan and John Heffernan",