kxco-post-quantum 1.0.2 → 1.1.0

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.
@@ -1,155 +0,0 @@
1
- // Run-vectors — verifies every entry in test/vectors.json against the library.
2
- //
3
- // Anyone can `git clone && npm install && node test/run-vectors.js` and see
4
- // the same bit-for-bit outputs. This is the reproducibility claim.
5
- //
6
- // Exit code 0 = all pass. Exit code 1 = at least one mismatch.
7
-
8
- import { readFileSync } from 'node:fs'
9
- import { createHash } from 'node:crypto'
10
- import { fileURLToPath } from 'node:url'
11
- import { dirname, join } from 'node:path'
12
-
13
- import {
14
- mlDsa, mlKem, deriveSeed, fingerprint, webhook,
15
- } from '../src/index.js'
16
-
17
- const here = dirname(fileURLToPath(import.meta.url))
18
- const vectors = JSON.parse(readFileSync(join(here, 'vectors.json'), 'utf8'))
19
-
20
- const sha256hex = (b) => createHash('sha256').update(b).digest('hex')
21
-
22
- let pass = 0
23
- let fail = 0
24
- const failures = []
25
-
26
- function check(group, name, expected, actual) {
27
- const ok = expected === actual ||
28
- (typeof expected === 'boolean' && expected === actual) ||
29
- (typeof expected === 'number' && expected === actual)
30
- if (ok) {
31
- pass++
32
- } else {
33
- fail++
34
- failures.push({ group, name, expected, actual })
35
- }
36
- }
37
-
38
- // deriveSeed
39
- for (const v of vectors.deriveSeed) {
40
- const master = Buffer.from(v.master_hex, 'hex')
41
- const got = deriveSeed(master, v.info, v.length).toString('hex')
42
- check('deriveSeed', v.name, v.expect_hex, got)
43
- }
44
-
45
- // mlDsa keypairFromMaster — pin SHA-256 of pub + secret bytes
46
- for (const v of vectors.mlDsa_keypairFromMaster) {
47
- const master = Buffer.from(v.master_hex, 'hex')
48
- const kp = mlDsa.keypairFromMaster(master, v.info)
49
- check('mlDsa.keypairFromMaster', v.name + ' [publicKey_sha256]', v.publicKey_sha256, sha256hex(kp.publicKey))
50
- check('mlDsa.keypairFromMaster', v.name + ' [secretKey_sha256]', v.secretKey_sha256, sha256hex(kp.secretKey))
51
- check('mlDsa.keypairFromMaster', v.name + ' [publicKey_bytes]', v.publicKey_bytes, kp.publicKey.length)
52
- check('mlDsa.keypairFromMaster', v.name + ' [secretKey_bytes]', v.secretKey_bytes, kp.secretKey.length)
53
- }
54
-
55
- // mlDsa sign round-trip
56
- for (const v of vectors.mlDsa_sign_roundtrip) {
57
- const master = Buffer.from(v.master_hex, 'hex')
58
- const kp = mlDsa.keypairFromMaster(master, v.info)
59
- const sig = mlDsa.sign(kp.secretKey, v.message_utf8)
60
- const verified = mlDsa.verify(kp.publicKey, v.message_utf8, sig)
61
- check('mlDsa.sign_roundtrip', v.name + ' [verify]', v.expect_verify, verified)
62
- check('mlDsa.sign_roundtrip', v.name + ' [sig_hex_length]', v.expect_sig_hex_length, sig.length)
63
- }
64
-
65
- // mlKem keypairFromMaster
66
- for (const v of vectors.mlKem_keypairFromMaster) {
67
- const master = Buffer.from(v.master_hex, 'hex')
68
- const kp = mlKem.keypairFromMaster(master, v.info)
69
- check('mlKem.keypairFromMaster', v.name + ' [publicKey_sha256]', v.publicKey_sha256, sha256hex(kp.publicKey))
70
- check('mlKem.keypairFromMaster', v.name + ' [secretKey_sha256]', v.secretKey_sha256, sha256hex(kp.secretKey))
71
- check('mlKem.keypairFromMaster', v.name + ' [publicKey_bytes]', v.publicKey_bytes, kp.publicKey.length)
72
- check('mlKem.keypairFromMaster', v.name + ' [secretKey_bytes]', v.secretKey_bytes, kp.secretKey.length)
73
- }
74
-
75
- // mlKem encapsulate round-trip
76
- for (const v of vectors.mlKem_encapsulate_roundtrip) {
77
- const master = Buffer.from(v.master_hex, 'hex')
78
- const kp = mlKem.keypairFromMaster(master, v.info)
79
- const { ciphertext, sharedSecret } = mlKem.encapsulate(kp.publicKey)
80
- const recovered = mlKem.decapsulate(ciphertext, kp.secretKey)
81
- check('mlKem.encapsulate_roundtrip', v.name + ' [secret_bytes]', v.expect_sharedSecret_bytes, sharedSecret.length)
82
- check('mlKem.encapsulate_roundtrip', v.name + ' [ct_bytes]', v.expect_ciphertext_bytes, ciphertext.length)
83
- check('mlKem.encapsulate_roundtrip', v.name + ' [recovered_eq]', sha256hex(sharedSecret), sha256hex(recovered))
84
- }
85
-
86
- // fingerprint
87
- for (const v of vectors.fingerprint) {
88
- let input
89
- if (v.input_hex) input = Buffer.from(v.input_hex, 'hex')
90
- else if (v.input_utf8) input = Buffer.from(v.input_utf8, 'utf8')
91
- else if (v.input_source && v.input_source.includes('mlDsa.keypairFromMaster')) {
92
- input = mlDsa.keypairFromMaster(Buffer.from('00'.repeat(32), 'hex'), 'platform-v1').publicKey
93
- }
94
- const got = fingerprint(input)
95
- check('fingerprint', v.name, v.expect_kid, got)
96
- }
97
-
98
- // webhook envelope
99
- for (const v of vectors.webhook_envelope) {
100
- const got = webhook.envelope(v.timestamp, v.body_utf8).toString('hex')
101
- check('webhook.envelope', v.name, v.expect_envelope_hex, got)
102
- }
103
-
104
- // webhook hmac
105
- for (const v of vectors.webhook_hmac) {
106
- const got = webhook.hmacHex(v.secret_utf8, v.timestamp, v.body_utf8)
107
- check('webhook.hmac', v.name, v.expect_hmac_hex, got)
108
- }
109
-
110
- // webhook hybrid round-trip with FRESH timestamp (vector's timestamp is stale on purpose; we use Date.now() here)
111
- for (const v of vectors.webhook_hybrid_roundtrip) {
112
- const master = Buffer.from(v.master_hex, 'hex')
113
- const kp = mlDsa.keypairFromMaster(master, v.info)
114
- const kid = fingerprint(kp.publicKey)
115
- const freshTs = Math.floor(Date.now() / 1000).toString()
116
- const headers = webhook.signDelivery({
117
- rawBody: v.body_utf8,
118
- hmacSecret: v.hmac_secret_utf8,
119
- pqSecretKey: kp.secretKey,
120
- pqKid: kid,
121
- })
122
- const lower = Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]))
123
- const r = webhook.verifyDelivery({
124
- headers: lower,
125
- rawBody: v.body_utf8,
126
- hmacSecret: v.hmac_secret_utf8,
127
- pqPublicKey: kp.publicKey,
128
- pinnedKid: kid,
129
- })
130
- check('webhook.hybrid_roundtrip', v.name + ' [hmac_ok]', v.expect_hmac_ok, r.hmacOk)
131
- check('webhook.hybrid_roundtrip', v.name + ' [pq_ok]', v.expect_pq_ok, r.pqOk)
132
- check('webhook.hybrid_roundtrip', v.name + ' [ts_ok]', v.expect_timestamp_ok_when_fresh, r.timestampOk)
133
- }
134
-
135
- // Report
136
- console.log('')
137
- console.log(`kxco-post-quantum test vectors`)
138
- console.log(`Generated: ${vectors.generated_at}`)
139
- console.log(`Library: ${vectors.library}`)
140
- console.log(`Underlying: ${vectors.underlying_primitive}`)
141
- console.log('')
142
-
143
- if (fail === 0) {
144
- console.log(`✓ All ${pass} checks pass — library output matches pinned vectors bit-for-bit.`)
145
- process.exit(0)
146
- } else {
147
- console.log(`✗ ${fail} failed, ${pass} passed.`)
148
- for (const f of failures) {
149
- console.log(`\n GROUP: ${f.group}`)
150
- console.log(` NAME: ${f.name}`)
151
- console.log(` EXPECTED: ${f.expected}`)
152
- console.log(` ACTUAL: ${f.actual}`)
153
- }
154
- process.exit(1)
155
- }
package/test/vectors.json DELETED
@@ -1,137 +0,0 @@
1
- {
2
- "version": "1.0",
3
- "generated_at": "2026-05-20T18:58:39.432Z",
4
- "description": "Deterministic test vectors for kxco-post-quantum. Run test/run-vectors.js to verify these match the library output bit-for-bit.",
5
- "library": "kxco-post-quantum",
6
- "underlying_primitive": "@noble/post-quantum",
7
- "notes": [
8
- "ML-DSA-65 signatures are randomized — we cannot pin signature bytes. We pin keypair hashes and verify round-trip.",
9
- "ML-KEM encapsulation is randomized — we pin keypair hashes and verify decapsulate round-trip in the runner.",
10
- "All hex is lowercase. All UTF-8 strings are explicit."
11
- ],
12
- "deriveSeed": [
13
- {
14
- "name": "zero master, info=role-a, length=32",
15
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
16
- "info": "role-a",
17
- "length": 32,
18
- "expect_hex": "873c6de3e076daede530be45f1f7953a29fec20648557429d2607e08c3ad8d7c"
19
- },
20
- {
21
- "name": "zero master, info=role-b, length=32 (different info → different seed)",
22
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
23
- "info": "role-b",
24
- "length": 32,
25
- "expect_hex": "80f852c16846b2a44c64c7742cd8df83f9b1a98f969dd0496904c3bdd08cd8cc"
26
- },
27
- {
28
- "name": "0x11 master, info=platform-v1, length=64",
29
- "master_hex": "1111111111111111111111111111111111111111111111111111111111111111",
30
- "info": "platform-v1",
31
- "length": 64,
32
- "expect_hex": "a9df8abe6efa342d56b911f8eeea67bb553d675e4411ea046b9aca2fa409fcc2b92166b6117606627faed0a66f890403ad2fa6d9d3dadc69bd5559f3da92eec3"
33
- }
34
- ],
35
- "mlDsa_keypairFromMaster": [
36
- {
37
- "name": "zero master, info=platform-v1",
38
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
39
- "info": "platform-v1",
40
- "publicKey_sha256": "dc29825b2f9cc9f1671ca499c27d013c2b4ac86e1c6ce365f40556ec82916e57",
41
- "publicKey_bytes": 1952,
42
- "secretKey_sha256": "fad19856458d742f62d79dbd772cfaea7ad2e432111a9663d159b079d15f51eb",
43
- "secretKey_bytes": 4032
44
- },
45
- {
46
- "name": "0xff master, info=test",
47
- "master_hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
48
- "info": "test",
49
- "publicKey_sha256": "b5efe3602d7d87e29ce642ff44a682ded5408e43b965b2a22e5aedec0d8ab9aa",
50
- "publicKey_bytes": 1952,
51
- "secretKey_sha256": "abd46a2431e7e82249e798e7ca634785ba1f43e052e4649a4e0e02536db47770",
52
- "secretKey_bytes": 4032
53
- }
54
- ],
55
- "mlDsa_sign_roundtrip": [
56
- {
57
- "name": "sign known message with keypair from zero master; verify true",
58
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
59
- "info": "platform-v1",
60
- "message_utf8": "hello kxco",
61
- "expect_verify": true,
62
- "expect_sig_hex_length": 6618
63
- }
64
- ],
65
- "mlKem_keypairFromMaster": [
66
- {
67
- "name": "zero master, info=platform-v1",
68
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
69
- "info": "platform-v1",
70
- "publicKey_sha256": "8393aadd8e9070c8b43b65a1e05eb43f9c9e7e32ba7880261a2cbcabda58c188",
71
- "publicKey_bytes": 1184,
72
- "secretKey_sha256": "8d21f316efc198ba3cab8e7c9270f57efe351bed4d0f66c66f6d454d4282423e",
73
- "secretKey_bytes": 2400
74
- }
75
- ],
76
- "mlKem_encapsulate_roundtrip": [
77
- {
78
- "name": "encapsulate to keypair from zero master; decapsulate recovers same secret",
79
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
80
- "info": "platform-v1",
81
- "expect_sharedSecret_bytes": 32,
82
- "expect_ciphertext_bytes": 1088
83
- }
84
- ],
85
- "fingerprint": [
86
- {
87
- "name": "fingerprint of 32 zero bytes",
88
- "input_hex": "0000000000000000000000000000000000000000000000000000000000000000",
89
- "expect_kid": "66687aadf862bd77"
90
- },
91
- {
92
- "name": "fingerprint of 32 0xff bytes",
93
- "input_hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
94
- "expect_kid": "af9613760f72635f"
95
- },
96
- {
97
- "name": "fingerprint of UTF-8 string 'kxco'",
98
- "input_utf8": "kxco",
99
- "expect_kid": "00932b8f81e33fa2"
100
- },
101
- {
102
- "name": "fingerprint of ML-DSA-65 platform pubkey from zero master",
103
- "input_source": "mlDsa.keypairFromMaster(0x00*32, \"platform-v1\").publicKey",
104
- "expect_kid": "dc29825b2f9cc9f1"
105
- }
106
- ],
107
- "webhook_envelope": [
108
- {
109
- "name": "envelope concatenation",
110
- "timestamp": "1748000000",
111
- "body_utf8": "{\"event\":\"payment.settled\",\"amount\":1000}",
112
- "expect_envelope_hex": "313734383030303030302e7b226576656e74223a227061796d656e742e736574746c6564222c22616d6f756e74223a313030307d"
113
- }
114
- ],
115
- "webhook_hmac": [
116
- {
117
- "name": "HMAC-SHA-256 over envelope",
118
- "secret_utf8": "shared-secret-bytes",
119
- "timestamp": "1748000000",
120
- "body_utf8": "{\"event\":\"payment.settled\",\"amount\":1000}",
121
- "expect_hmac_hex": "63ed31450be6c8771c2eb9a92888aecd814431360ac06aaac95faa94cf5bd665"
122
- }
123
- ],
124
- "webhook_hybrid_roundtrip": [
125
- {
126
- "name": "sign + verify with platform keypair from zero master",
127
- "master_hex": "0000000000000000000000000000000000000000000000000000000000000000",
128
- "info": "platform-v1",
129
- "timestamp": "1748000000",
130
- "body_utf8": "{\"event\":\"payment.settled\",\"amount\":1000}",
131
- "hmac_secret_utf8": "shared-secret-bytes",
132
- "expect_hmac_ok": true,
133
- "expect_pq_ok": true,
134
- "expect_timestamp_ok_when_fresh": true
135
- }
136
- ]
137
- }