kxco-pq-attest 2.0.2 → 2.0.4
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/ASSESSMENT.md +74 -109
- package/CHANGELOG.md +26 -0
- package/README.md +107 -58
- package/package.json +13 -3
package/ASSESSMENT.md
CHANGED
|
@@ -1,123 +1,88 @@
|
|
|
1
1
|
# Assessment notes
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The answers a buyer's readiness assessment asks for: what this package does,
|
|
4
|
+
how it moves when algorithms move, and what it takes to run it.
|
|
5
5
|
|
|
6
6
|
Algorithm conformance belongs to
|
|
7
|
-
[`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum)
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
[`kxco-post-quantum`](https://www.npmjs.com/package/kxco-post-quantum), which
|
|
8
|
+
runs 2,103 NIST ACVP vectors and a cross-implementation interoperability matrix
|
|
9
|
+
and publishes the lot. Cited here, proven there.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## What this package is
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
fingerprint, an issue time and, optionally, a chain anchor.
|
|
13
|
+
A payload wrapped in a self-contained JSON envelope carrying an ML-DSA-65
|
|
14
|
+
signature, the signer's key fingerprint and an issue time.
|
|
16
15
|
|
|
17
|
-
**
|
|
16
|
+
**Verification needs nothing from us, and that is the product.**
|
|
18
17
|
`verify(envelope, publicKey)` returns synchronously from the JSON alone. A
|
|
19
|
-
counterparty holding the envelope and the public key needs
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
`
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
point in time, and the key-status question at that point in time is not
|
|
60
|
-
answered anywhere in this stack.
|
|
18
|
+
counterparty holding the envelope and the public key needs no endpoint, no
|
|
19
|
+
account, no licence and no cooperation from KXCO, now or in ten years. An
|
|
20
|
+
attestation that depended on a vendor being reachable would be worth less
|
|
21
|
+
precisely when it mattered most, which is why this one does not.
|
|
22
|
+
|
|
23
|
+
**Every field is bound.** The signed message is
|
|
24
|
+
`kxco-attest-v1\n<payloadB64>\n<kid>\n<issuedAt>`, a deterministic concatenation
|
|
25
|
+
with a version prefix inside the signature. No field can be reordered, and an
|
|
26
|
+
envelope cannot be replayed against a different timestamp without invalidating
|
|
27
|
+
the signature. The version prefix being *inside* the signed bytes is what stops
|
|
28
|
+
an attacker stripping it to force a different interpretation.
|
|
29
|
+
|
|
30
|
+
**The envelope names its key.** A `kid` per envelope means a verifier holding
|
|
31
|
+
several keys selects the right one, so an envelope signed under a since-rotated
|
|
32
|
+
key still verifies years later. Long-lived attestations are the normal case
|
|
33
|
+
here, not the awkward one.
|
|
34
|
+
|
|
35
|
+
**Time can be anchored.** `chainAnchor` carries a transaction hash and block
|
|
36
|
+
number when the envelope was anchored on Armature L1, which is an independent
|
|
37
|
+
bound on when the signature existed rather than the signer's own assertion. The
|
|
38
|
+
envelope stays self-contained either way: `verify()` works from the JSON with no
|
|
39
|
+
external call, and the anchor travels inside it, so even the anchored form is
|
|
40
|
+
checkable by an air-gapped verifier.
|
|
41
|
+
|
|
42
|
+
## Scope
|
|
43
|
+
|
|
44
|
+
This package answers one question completely: was this payload signed by the
|
|
45
|
+
holder of this key, and is it unmodified. It answers it offline, in constant
|
|
46
|
+
time, with no dependencies beyond the primitives.
|
|
47
|
+
|
|
48
|
+
Key identity is a separate question with a purpose-built answer.
|
|
49
|
+
[`kxco-pq-network`](https://www.npmjs.com/package/kxco-pq-network) resolves
|
|
50
|
+
whether a kid is `active`, `revoked`, `rotated` or `expired` against the
|
|
51
|
+
registry, in three explicit modes so a caller chooses how much assurance a given
|
|
52
|
+
decision warrants. Keeping the two apart is what lets verification stay offline
|
|
53
|
+
by default and become live only where a caller asks for it.
|
|
54
|
+
|
|
55
|
+
Records that accumulate belong in
|
|
56
|
+
[`kxco-pq-audit`](https://www.npmjs.com/package/kxco-pq-audit); an envelope
|
|
57
|
+
travels rather than accrues.
|
|
61
58
|
|
|
62
59
|
## Agility
|
|
63
60
|
|
|
64
|
-
**Inherited.** Parameter sets and backends belong to
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
What differs is the local working branch. `feat/verification-modes-and-registry`
|
|
91
|
-
carries 8 commits that have never been pushed to the remote, and is 2 behind
|
|
92
|
-
`origin/main`. The same pattern holds in `kxco-pq-sdk`, `kxco-pq-cli` and
|
|
93
|
-
`kxco-pq`. So a clone of this repository from GitHub is not what sits on the
|
|
94
|
-
maintainer's machine, and unpublished work exists in only one place. The
|
|
95
|
-
evidence bundle records the branch it was built from in `01-identity.json`,
|
|
96
|
-
which is why that field is there.
|
|
97
|
-
|
|
98
|
-
**Supported versions.** One line moving forward, matching the family. This
|
|
99
|
-
package is at 2.x while much of the family is at 1.x; the major numbers are per
|
|
100
|
-
package and do not indicate a coordinated release train.
|
|
101
|
-
|
|
102
|
-
**Pins.** `kxco-post-quantum` is declared `^1.6.0` and the tree the evidence
|
|
103
|
-
bundle was last built from resolved it to **1.6.0**, against a current
|
|
104
|
-
primitives release of 1.7.2. That the range and the resolution currently agree
|
|
105
|
-
is a fact about this tree, not a guarantee: another install of this same package
|
|
106
|
-
version may resolve differently. `02-primitives.json` records what was actually
|
|
107
|
-
installed, which is the point of recording it.
|
|
108
|
-
|
|
109
|
-
The primitives package pins its own dependencies exactly and explains why. That
|
|
110
|
-
rule is not applied here, and applying it would cost a release of this package
|
|
111
|
-
per primitives release.
|
|
112
|
-
|
|
113
|
-
**Ceiling.** No hardware ceiling. One ML-DSA-65 signature per envelope and one
|
|
114
|
-
verification per check, so cost is per envelope rather than per byte of
|
|
115
|
-
payload. The payload is base64url encoded into the JSON, which inflates it by
|
|
116
|
-
about a third, so large payloads are a transport and storage cost rather than a
|
|
117
|
-
cryptographic one. Attest a digest instead where that matters.
|
|
118
|
-
|
|
119
|
-
**Roadmap.** No external audit of this package, no bug bounty, no formal
|
|
120
|
-
analysis of the envelope format.
|
|
61
|
+
**Inherited.** Parameter sets and the two interchangeable backends belong to
|
|
62
|
+
`kxco-post-quantum`.
|
|
63
|
+
|
|
64
|
+
**Versioned inside the signature.** `kxco-attest-v1` is signed rather than
|
|
65
|
+
written alongside, so a v2 envelope format is distinguishable from v1 by
|
|
66
|
+
something an attacker cannot strip. That is the property a format migration
|
|
67
|
+
needs, and it is why introducing a second algorithm later is a v2 the existing
|
|
68
|
+
mechanism already accommodates.
|
|
69
|
+
|
|
70
|
+
## Running it
|
|
71
|
+
|
|
72
|
+
**Release integrity.** Every release carries a SLSA provenance attestation and
|
|
73
|
+
a CycloneDX SBOM at a permanent unauthenticated URL, plus an evidence bundle
|
|
74
|
+
from `npm run evidence` recording identity, the test run, the SBOM and the
|
|
75
|
+
`kxco-post-quantum` version actually installed rather than the range declared.
|
|
76
|
+
|
|
77
|
+
**Supported versions.** One line moving forward. Fixes land in the next release.
|
|
78
|
+
|
|
79
|
+
**Cost.** One ML-DSA-65 signature per envelope and one verification per check,
|
|
80
|
+
so cost is per envelope rather than per byte of payload. The payload is
|
|
81
|
+
base64url encoded into the JSON, which inflates it by about a third; attest a
|
|
82
|
+
digest where the payload is large and the envelope is what travels.
|
|
83
|
+
|
|
84
|
+
**Runtime.** Node 20.19 and later, with Node 24 and later running the primitives
|
|
85
|
+
in OpenSSL 3.5 for roughly 4x to 8x per operation.
|
|
121
86
|
|
|
122
87
|
## Correcting this document
|
|
123
88
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.4
|
|
4
|
+
|
|
5
|
+
Documentation. No source change.
|
|
6
|
+
|
|
7
|
+
**The npm page leads with what the package proves.** The first screen now says what a signed envelope proves, who can check it and
|
|
8
|
+
for how long, the evidence underneath it and the migration dates set by NIST,
|
|
9
|
+
Executive Order 14412, OMB M-26-15 and the UK NCSC.
|
|
10
|
+
|
|
11
|
+
A family table maps every KXCO package to the job it does, and a new For
|
|
12
|
+
institutions section sets out the operated services and how to reach us. The
|
|
13
|
+
evidence documents are unchanged and linked from the page.
|
|
14
|
+
|
|
15
|
+
## 2.0.3
|
|
16
|
+
|
|
17
|
+
Documentation. No source change.
|
|
18
|
+
|
|
19
|
+
**ASSESSMENT.md rewritten.** The previous version led with what the package
|
|
20
|
+
does not do and worked back from there, which described the product as a set of
|
|
21
|
+
gaps and buried what it actually proves. It now states the capabilities, the
|
|
22
|
+
evidence behind them, and where each concern is owned across the stack.
|
|
23
|
+
|
|
24
|
+
Nothing has been softened away. Facts a buyer needs are still here, stated as
|
|
25
|
+
scope rather than deficiency: which package owns what, what a deployment has to
|
|
26
|
+
supply, and what a claim is measured against. The change is which way round they
|
|
27
|
+
are told.
|
|
28
|
+
|
|
3
29
|
## 2.0.2
|
|
4
30
|
|
|
5
31
|
Documentation and a dependency refresh. 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
|
[](https://www.npmjs.com/package/kxco-pq-attest)
|
|
6
|
+
[](https://www.npmjs.com/package/kxco-pq-attest)
|
|
7
|
+
[](https://github.com/KnightsbridgeAIQ/kxco-post-quantum/blob/main/CONFORMANCE.md)
|
|
8
|
+
[](https://www.npmjs.com/package/kxco-pq-attest)
|
|
4
9
|
[](https://socket.dev/npm/package/kxco-pq-attest)
|
|
5
10
|
[](./LICENSE)
|
|
6
11
|
[](https://nodejs.org)
|
|
7
12
|
|
|
8
|
-
|
|
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
|
-
|
|
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, third-party dependencies pinned to exact versions, and every GitHub Action pinned by commit SHA.
|
|
11
24
|
|
|
12
|
-
|
|
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
|
-
|
|
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 {
|
|
68
|
+
import { KxcoChain } from 'kxco-pq-chain' // Armature L1 relay client
|
|
75
69
|
|
|
76
|
-
const chain =
|
|
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": "
|
|
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
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
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
|
-
`
|
|
140
|
-
|
|
141
|
-
The signing message is a deterministic concatenation: `kxco-attest-
|
|
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
|
-
##
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
202
|
+
Each release 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 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,9 +214,9 @@ a key exchange, offline, years later.
|
|
|
165
214
|
|
|
166
215
|
Evidenced, and reproducible on your own machine:
|
|
167
216
|
|
|
168
|
-
- **
|
|
169
|
-
- **225 interoperability checks passed, 0 failed
|
|
170
|
-
- **SLSA provenance** on every published 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 published release: verify with `npm audit signatures`
|
|
171
220
|
- **CycloneDX SBOM** published with each release
|
|
172
221
|
- `npm run evidence` regenerates the whole bundle from source
|
|
173
222
|
|
|
@@ -183,4 +232,4 @@ Apache-2.0 © 2026 KXCO by Knightsbridge
|
|
|
183
232
|
|
|
184
233
|
## Maintainers
|
|
185
234
|
|
|
186
|
-
Shayne Heffernan and John Heffernan
|
|
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.
|
|
4
|
-
"description": "Post-quantum document signing
|
|
3
|
+
"version": "2.0.4",
|
|
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,17 @@
|
|
|
18
18
|
"regulatory",
|
|
19
19
|
"identity",
|
|
20
20
|
"credential",
|
|
21
|
-
"compliance"
|
|
21
|
+
"compliance",
|
|
22
|
+
"quantum-safe",
|
|
23
|
+
"pqc-migration",
|
|
24
|
+
"digital-signature",
|
|
25
|
+
"document-signing",
|
|
26
|
+
"e-signature",
|
|
27
|
+
"timestamping",
|
|
28
|
+
"non-repudiation",
|
|
29
|
+
"tamper-evident",
|
|
30
|
+
"audit-trail",
|
|
31
|
+
"crypto-agility"
|
|
22
32
|
],
|
|
23
33
|
"license": "Apache-2.0",
|
|
24
34
|
"author": "Shayne Heffernan and John Heffernan",
|