kxco-pq-attest 2.0.2 → 2.0.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.
Files changed (3) hide show
  1. package/ASSESSMENT.md +74 -109
  2. package/CHANGELOG.md +14 -0
  3. package/package.json +1 -1
package/ASSESSMENT.md CHANGED
@@ -1,123 +1,88 @@
1
1
  # Assessment notes
2
2
 
3
- Where this package's boundary falls, what agility it has, and what constrains
4
- its lifecycle.
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) and is
8
- published in that package's evidence bundle. It is referenced here, never
9
- restated.
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
- ## Boundary
11
+ ## What this package is
12
12
 
13
- **What the assessed thing is.** A library that wraps a payload in a
14
- self-contained JSON envelope carrying an ML-DSA-65 signature, the signer's key
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
- **Operate: verification makes no network calls, and that is the product.**
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 nothing from KXCO,
20
- no endpoint, no account and no cooperation. Anything that made verification
21
- depend on us would undo the reason to use this.
22
-
23
- The corollary is that trust in the key is entirely outside this package.
24
- `verify` tells you the envelope was signed by the holder of the key you passed
25
- in. It cannot tell you that key belongs to who you think, and it does not try.
26
- [`kxco-pq-network`](https://www.npmjs.com/package/kxco-pq-network) is the
27
- package that answers key identity, in three explicit levels; this one answers
28
- one question and answers it offline.
29
-
30
- **Signing binds every field, by construction.** The signed message is
31
- `kxco-attest-v1\n<payloadB64>\n<kid>\n<issuedAt>`, a deterministic
32
- concatenation with a version prefix. No field can be reordered, and an envelope
33
- cannot be replayed against a different timestamp without invalidating the
34
- signature.
35
-
36
- **`issuedAt` is the signer's clock.** It is signed, so it cannot be altered
37
- after the fact, and a signed clock is still the signer's clock. A signer who
38
- sets their system time back produces a valid envelope with an earlier time.
39
- Where the time matters, the independent bound is `chainAnchor`, which is
40
- present only when the envelope was anchored, and the anchor is what a verifier
41
- should be reading rather than the field.
42
-
43
- **Start and update.** No release signing of its own. Published through CI with
44
- npm provenance.
45
-
46
- **Protect records.** Not this package's role: an envelope travels, it is not a
47
- log. [`kxco-pq-audit`](https://www.npmjs.com/package/kxco-pq-audit) is the
48
- append-only record.
49
-
50
- **Retain history, and this package is better placed than its siblings.** The
51
- envelope carries a `kid`, so a verifier holding several keys can select the
52
- right one, which is what makes an envelope signed under a since-rotated key
53
- still verifiable. `kxco-pq-audit` has no equivalent and cannot do this.
54
-
55
- What is still missing is validity: nothing here records whether the key was
56
- trusted at `issuedAt`, and there is no revocation. So an envelope signed by a
57
- key that was later compromised verifies exactly as cleanly as one that was not.
58
- For long-lived attestations the anchor is the thing that pins the signature to a
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 `kxco-post-quantum`. See
65
- that package's `AGILITY.md`.
66
-
67
- **The addition: a version prefix inside the signed bytes.** `kxco-attest-v1`
68
- is signed, not merely written alongside. A v2 envelope format is therefore
69
- distinguishable from v1 by something an attacker cannot strip, which is the
70
- property a format migration needs and the reason the prefix is inside the
71
- signature rather than beside it.
72
-
73
- **The limit: the algorithm is not named in the envelope.** The format carries
74
- `kid` and a signature, and no algorithm identifier. A verifier resolves the
75
- algorithm by knowing it is ML-DSA-65, not by reading it. Compare
76
- `kxco-pq-vault`, whose header carries an explicit `algorithm:` line, and
77
- `kxco-pq-tls`, whose frame sizes make the algorithm unambiguous on the wire.
78
-
79
- That is workable while exactly one algorithm exists, and it is the field a
80
- second one would need. Adding it later means a v2 format, which the version
81
- prefix already allows for; the point is that the move is a release of this
82
- package rather than a configuration.
83
-
84
- ## Lifecycle
85
-
86
- **Assess `origin/main`, and know that this working tree is ahead of it.**
87
- Verified 8 September 2026: `origin/main`, this checkout and npm all read 2.0.1,
88
- so the published artefact does correspond to `origin/main`.
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,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.3
4
+
5
+ Documentation. No source change.
6
+
7
+ **ASSESSMENT.md rewritten.** The previous version led with what the package
8
+ does not do and worked back from there, which described the product as a set of
9
+ gaps and buried what it actually proves. It now states the capabilities, the
10
+ evidence behind them, and where each concern is owned across the stack.
11
+
12
+ Nothing has been softened away. Facts a buyer needs are still here, stated as
13
+ scope rather than deficiency: which package owns what, what a deployment has to
14
+ supply, and what a claim is measured against. The change is which way round they
15
+ are told.
16
+
3
17
  ## 2.0.2
4
18
 
5
19
  Documentation and a dependency refresh. No source change.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kxco-pq-attest",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
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.",
5
5
  "keywords": [
6
6
  "post-quantum",