kxco-verify 1.3.3 → 1.4.1
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/CHANGELOG.md +26 -0
- package/README.md +8 -7
- package/package.json +5 -4
- package/src/index.d.ts +1 -1
- package/src/index.js +8 -4
- package/src/parse.d.ts +1 -1
- package/src/parse.js +31 -12
- package/src/verify.d.ts +8 -1
- package/src/verify.js +35 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.1
|
|
4
|
+
|
|
5
|
+
Documentation. No source change.
|
|
6
|
+
|
|
7
|
+
The package description, the opening of the README and the keywords now say
|
|
8
|
+
what 1.4.0 already does: it verifies ML-DSA-87 signatures as well as ML-DSA-65.
|
|
9
|
+
The description now fits the 255 characters the npm registry keeps, so it no
|
|
10
|
+
longer ends mid-sentence. `ml-dsa-87` joins the keywords.
|
|
11
|
+
|
|
12
|
+
## 1.4.0
|
|
13
|
+
ML-DSA-87 manifests verify alongside ML-DSA-65. parseManifest accepts
|
|
14
|
+
`"ML-DSA-87"` where all three alg fields name it, and requires the public key
|
|
15
|
+
and signature to be exactly that set's sizes: 2592 and 4627 bytes for
|
|
16
|
+
ML-DSA-87, 1952 and 3309 for ML-DSA-65. A manifest whose alg fields disagree,
|
|
17
|
+
or whose key is the size of the other set, is refused with `invalid_field`.
|
|
18
|
+
verifyManifest verifies under the manifest's set and reports it as
|
|
19
|
+
`algorithm`.
|
|
20
|
+
|
|
21
|
+
verifySignature takes an optional fourth argument, `alg`. Without it the public
|
|
22
|
+
key decides the set by its length; with it, a key or signature of the other set
|
|
23
|
+
returns false, and an algorithm it does not speak throws a TypeError. Every
|
|
24
|
+
ML-DSA-65 manifest and call that verified before verifies unchanged.
|
|
25
|
+
|
|
26
|
+
The ML-DSA-87 path uses `ml_dsa87` from the same exactly pinned
|
|
27
|
+
`@noble/post-quantum` 0.7.0, so the runtime dependency is unchanged.
|
|
28
|
+
|
|
3
29
|
## 1.3.3
|
|
4
30
|
|
|
5
31
|
A manifest body that is valid JSON but not an object is refused with
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://nodejs.org)
|
|
12
12
|
[](https://verify.kxco.ai)
|
|
13
13
|
|
|
14
|
-
Standalone post-quantum credential and attestation verifier for KXCO ML-DSA-65 signed documents. One dependency, pinned exactly. Works in any modern browser and in Node.js 20.19 and later.
|
|
14
|
+
Standalone post-quantum credential and attestation verifier for KXCO ML-DSA-87 and ML-DSA-65 signed documents. One dependency, pinned exactly. Works in any modern browser and in Node.js 20.19 and later.
|
|
15
15
|
|
|
16
16
|
- **No KXCO server in the trust path.** `verifyManifest()` makes no network request at all, and `verifyUrl()` contacts only the attestation URL you pass and the key endpoint the manifest names.
|
|
17
17
|
- **Runs in the browser as shipped.** No `Buffer`, no `process` and SHA-256 from WebCrypto, which is how [verify.kxco.ai](https://verify.kxco.ai) checks a signature entirely in the tab.
|
|
@@ -159,7 +159,7 @@ All exports are re-exported from the main entry point (`import ... from 'kxco-ve
|
|
|
159
159
|
|
|
160
160
|
### `verifyUrl(attestationUrl, opts?) → Promise<VerifyResult>`
|
|
161
161
|
|
|
162
|
-
Fetches the attestation at `attestationUrl`, verifies the ML-DSA-65
|
|
162
|
+
Fetches the attestation at `attestationUrl`, verifies the signature under the parameter set the manifest names (ML-DSA-65 or ML-DSA-87), then fetches the live well-known pubkey endpoint declared in the manifest (`publicKey.pinAt`) to detect key rotation. May return `"rotated"` if the live endpoint now serves a different key identifier.
|
|
163
163
|
|
|
164
164
|
```ts
|
|
165
165
|
verifyUrl(
|
|
@@ -186,7 +186,7 @@ verifyManifest(manifestBody: string | object): Promise<VerifyResult>
|
|
|
186
186
|
```ts
|
|
187
187
|
interface VerifyResult {
|
|
188
188
|
state: 'valid' | 'rotated' | 'invalid' | 'error'
|
|
189
|
-
algorithm?: 'ML-DSA-65'
|
|
189
|
+
algorithm?: 'ML-DSA-65' | 'ML-DSA-87'
|
|
190
190
|
manifestKid?: string // key identifier declared in the manifest
|
|
191
191
|
livePubkeyKid?: string // key identifier currently at the well-known endpoint
|
|
192
192
|
site?: string // site identifier declared by the manifest
|
|
@@ -212,22 +212,23 @@ These are exported for callers who want to compose their own verification logic.
|
|
|
212
212
|
|
|
213
213
|
#### `parseManifest(input) → ParseResult`
|
|
214
214
|
|
|
215
|
-
Parse and validate a raw manifest body without running signature verification.
|
|
215
|
+
Parse and validate a raw manifest body without running signature verification. `manifest.alg`, `signature.alg` and `publicKey.alg` must name the same parameter set, `ML-DSA-65` or `ML-DSA-87`, and the public key and signature must be exactly that set's sizes (1952 and 3309 bytes for ML-DSA-65, 2592 and 4627 for ML-DSA-87). A key of one set labelled as the other is refused.
|
|
216
216
|
|
|
217
217
|
```ts
|
|
218
218
|
parseManifest(input: string | object): ParseResult
|
|
219
219
|
// ParseResult is { ok: true; manifest: ParsedManifest } | { ok: false; error: ParseError }
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
-
#### `verifySignature(publicKey, message, signature) → boolean`
|
|
222
|
+
#### `verifySignature(publicKey, message, signature, alg?) → boolean`
|
|
223
223
|
|
|
224
|
-
Run ML-DSA-65 signature verification directly.
|
|
224
|
+
Run ML-DSA-65 or ML-DSA-87 signature verification directly. The public key decides the parameter set: 1952 bytes is ML-DSA-65 and 2592 is ML-DSA-87. Pass `alg` to require a set, and a key or signature of the other set returns `false`. An `alg` other than these two throws a `TypeError`.
|
|
225
225
|
|
|
226
226
|
```ts
|
|
227
227
|
verifySignature(
|
|
228
228
|
publicKey: Uint8Array | string, // hex or bytes
|
|
229
229
|
message: Uint8Array | string, // UTF-8 string or bytes
|
|
230
230
|
signature: Uint8Array | string, // hex or bytes
|
|
231
|
+
alg?: 'ML-DSA-65' | 'ML-DSA-87',
|
|
231
232
|
): boolean
|
|
232
233
|
```
|
|
233
234
|
|
|
@@ -321,7 +322,7 @@ version, and every GitHub Action is pinned by commit SHA.
|
|
|
321
322
|
|
|
322
323
|
## Security
|
|
323
324
|
|
|
324
|
-
**ML-DSA-65** (NIST FIPS 204) via [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) 0.7.0, pinned exactly. No custom cryptography.
|
|
325
|
+
**ML-DSA-87** and **ML-DSA-65** (NIST FIPS 204) via [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) 0.7.0, pinned exactly. No custom cryptography.
|
|
325
326
|
|
|
326
327
|
Evidenced, and reproducible on your own machine:
|
|
327
328
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kxco-verify",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Free, browser-safe verifier for KXCO ML-DSA-
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "Free, browser-safe verifier for KXCO ML-DSA-87 and ML-DSA-65 signatures, with no KXCO server in the trust path. An auditor, regulator or counterparty confirms a signature from the artefact alone, years later, and can check the key is still live on chain.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"post-quantum",
|
|
7
7
|
"pqc",
|
|
8
8
|
"ml-dsa",
|
|
9
|
+
"ml-dsa-87",
|
|
9
10
|
"dilithium",
|
|
10
11
|
"nist",
|
|
11
12
|
"fips-204",
|
|
@@ -83,8 +84,8 @@
|
|
|
83
84
|
"@noble/post-quantum": "0.7.0"
|
|
84
85
|
},
|
|
85
86
|
"scripts": {
|
|
86
|
-
"test": "node --test --test-reporter=spec test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js test/property.test.js",
|
|
87
|
-
"test:cov": "node --test --experimental-test-coverage test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js",
|
|
87
|
+
"test": "node --test --test-reporter=spec test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js test/ml-dsa-87.test.js test/property.test.js",
|
|
88
|
+
"test:cov": "node --test --experimental-test-coverage test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js test/ml-dsa-87.test.js",
|
|
88
89
|
"smoke": "node scripts/smoke.js",
|
|
89
90
|
"lint": "node -c src/index.js && node -c src/parse.js && node -c src/verify.js && node -c src/fetch.js"
|
|
90
91
|
},
|
package/src/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface VerifyResultError {
|
|
|
11
11
|
|
|
12
12
|
export interface VerifyResult {
|
|
13
13
|
state: VerifyState
|
|
14
|
-
algorithm?: 'ML-DSA-65'
|
|
14
|
+
algorithm?: 'ML-DSA-65' | 'ML-DSA-87'
|
|
15
15
|
/** kid as declared inside the manifest itself */
|
|
16
16
|
manifestKid?: string
|
|
17
17
|
/** kid currently served at the live well-known pubkey endpoint (when fetched) */
|
package/src/index.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// is trustworthy.
|
|
12
12
|
// - Maintain a registry of approved (domain, kid) pairs. That is a future
|
|
13
13
|
// phase of kxco-post-quantum / verify.kxco.ai and not in this library.
|
|
14
|
-
// - Speak any algorithm other than ML-DSA-65 with hex
|
|
15
|
-
// release. SLH-DSA-128s and hybrid envelopes are
|
|
14
|
+
// - Speak any algorithm other than ML-DSA-65 or ML-DSA-87 with hex
|
|
15
|
+
// encoding in this release. SLH-DSA-128s and hybrid envelopes are
|
|
16
|
+
// deferred.
|
|
16
17
|
//
|
|
17
18
|
// The result envelope is intentionally three-valued so the UI can distinguish
|
|
18
19
|
// signature failure from in-flight key rotation:
|
|
@@ -41,7 +42,7 @@ import { getJsonBody } from './fetch.js'
|
|
|
41
42
|
*
|
|
42
43
|
* @typedef {Object} VerifyResult
|
|
43
44
|
* @property {VerifyState} state
|
|
44
|
-
* @property {string=} algorithm —
|
|
45
|
+
* @property {string=} algorithm — "ML-DSA-65" or "ML-DSA-87"
|
|
45
46
|
* @property {string=} manifestKid — kid the manifest declared
|
|
46
47
|
* @property {string=} livePubkeyKid — kid currently served at the well-known endpoint (only present when fetched)
|
|
47
48
|
* @property {string=} site — site identifier as declared by the manifest
|
|
@@ -88,7 +89,10 @@ export async function verifyManifest(manifestBody) {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
// The manifest's algorithm, which parseManifest has already bound to the
|
|
93
|
+
// key and signature sizes, is required here as well, so verification runs
|
|
94
|
+
// under that parameter set and no other.
|
|
95
|
+
const sigValid = verifySignature(m.publicKeyHex, m.signedMessage, m.signatureHex, m.alg)
|
|
92
96
|
return {
|
|
93
97
|
state: sigValid ? 'valid' : 'invalid',
|
|
94
98
|
algorithm: m.alg,
|
package/src/parse.d.ts
CHANGED
package/src/parse.js
CHANGED
|
@@ -21,15 +21,27 @@
|
|
|
21
21
|
// "publicKey": { "alg": "ML-DSA-65", "encoding": "hex", "value": "<3904 hex chars>", "kid": "<same as manifest.kid>", "pinAt": "<relative URL>" }
|
|
22
22
|
// }
|
|
23
23
|
//
|
|
24
|
-
// We accept
|
|
25
|
-
// and
|
|
24
|
+
// We accept ML-DSA-65 and ML-DSA-87 with hex encoding. The three alg fields
|
|
25
|
+
// must name the same parameter set, and the public key and signature must be
|
|
26
|
+
// exactly that set's sizes, so the algorithm is bound to the key rather than
|
|
27
|
+
// taken from a label. SLH-DSA-128s and Ed25519+ML-DSA hybrid envelopes are
|
|
28
|
+
// reserved for later.
|
|
26
29
|
|
|
27
30
|
const HEX_RE = /^[0-9a-f]+$/i
|
|
28
31
|
|
|
32
|
+
// FIPS 204 sizes in bytes. A manifest whose key is the size of one set and
|
|
33
|
+
// whose alg names the other is refused here, before any verification.
|
|
34
|
+
const ML_DSA_SIZES = {
|
|
35
|
+
'ML-DSA-65': { publicKey: 1952, signature: 3309 },
|
|
36
|
+
'ML-DSA-87': { publicKey: 2592, signature: 4627 },
|
|
37
|
+
}
|
|
38
|
+
const SUPPORTED = Object.keys(ML_DSA_SIZES)
|
|
39
|
+
const isSupported = (alg) => typeof alg === 'string' && Object.hasOwn(ML_DSA_SIZES, alg)
|
|
40
|
+
|
|
29
41
|
/**
|
|
30
42
|
* @typedef {Object} ParsedManifest
|
|
31
43
|
* @property {string} site
|
|
32
|
-
* @property {string} alg —
|
|
44
|
+
* @property {string} alg — "ML-DSA-65" or "ML-DSA-87", matching the key and signature sizes
|
|
33
45
|
* @property {string} kid
|
|
34
46
|
* @property {string} signedMessage — the exact bytes the signature covers
|
|
35
47
|
* @property {string} signatureHex
|
|
@@ -78,11 +90,16 @@ export function parseManifest(input) {
|
|
|
78
90
|
if (!s || typeof s !== 'object') return err_('missing_field', 'top-level "signature" is missing or not an object', 'signature')
|
|
79
91
|
if (!pk || typeof pk !== 'object') return err_('missing_field', 'top-level "publicKey" is missing or not an object', 'publicKey')
|
|
80
92
|
|
|
81
|
-
// We
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
if (
|
|
93
|
+
// We verify ML-DSA-65 and ML-DSA-87 with hex-encoded signature + pubkey.
|
|
94
|
+
// Anything else is a feature we deferred to a later version.
|
|
95
|
+
const allowed = SUPPORTED.map((a) => `"${a}"`).join(' or ')
|
|
96
|
+
if (!isSupported(m.alg)) return err_('unsupported_algorithm', `manifest.alg must be ${allowed} (got ${JSON.stringify(m.alg)})`, 'manifest.alg')
|
|
97
|
+
if (!isSupported(s.alg)) return err_('unsupported_algorithm', `signature.alg must be ${allowed} (got ${JSON.stringify(s.alg)})`, 'signature.alg')
|
|
98
|
+
if (!isSupported(pk.alg)) return err_('unsupported_algorithm', `publicKey.alg must be ${allowed} (got ${JSON.stringify(pk.alg)})`, 'publicKey.alg')
|
|
99
|
+
// One parameter set per manifest: a signature or key labelled as the other
|
|
100
|
+
// set is a manifest that disagrees with itself.
|
|
101
|
+
if (s.alg !== m.alg) return err_('invalid_field', `signature.alg (${JSON.stringify(s.alg)}) must match manifest.alg (${JSON.stringify(m.alg)})`, 'signature.alg')
|
|
102
|
+
if (pk.alg !== m.alg) return err_('invalid_field', `publicKey.alg (${JSON.stringify(pk.alg)}) must match manifest.alg (${JSON.stringify(m.alg)})`, 'publicKey.alg')
|
|
86
103
|
if (s.encoding && s.encoding !== 'hex') return err_('unsupported_encoding', `signature.encoding must be "hex" (got ${JSON.stringify(s.encoding)})`, 'signature.encoding')
|
|
87
104
|
if (pk.encoding && pk.encoding !== 'hex') return err_('unsupported_encoding', `publicKey.encoding must be "hex" (got ${JSON.stringify(pk.encoding)})`, 'publicKey.encoding')
|
|
88
105
|
|
|
@@ -93,10 +110,12 @@ export function parseManifest(input) {
|
|
|
93
110
|
if (typeof pk.value !== 'string' || !HEX_RE.test(pk.value)) return err_('invalid_field', 'publicKey.value must be a hex string', 'publicKey.value')
|
|
94
111
|
if (typeof pk.kid !== 'string' || !HEX_RE.test(pk.kid)) return err_('invalid_field', 'publicKey.kid must be a hex string', 'publicKey.kid')
|
|
95
112
|
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
113
|
+
// Size sanity, per parameter set. Don't trust the label saying which set it
|
|
114
|
+
// is: the byte counts must be that set's, so an ML-DSA-65 key labelled
|
|
115
|
+
// ML-DSA-87 (or the reverse) is refused here. Catches malformed payloads early.
|
|
116
|
+
const size = ML_DSA_SIZES[m.alg]
|
|
117
|
+
if (pk.value.length !== size.publicKey * 2) return err_('invalid_field', `publicKey.value must be ${size.publicKey} bytes (${size.publicKey * 2} hex chars) for ${m.alg}; got ${pk.value.length / 2}`, 'publicKey.value')
|
|
118
|
+
if (s.value.length !== size.signature * 2) return err_('invalid_field', `signature.value must be ${size.signature} bytes (${size.signature * 2} hex chars) for ${m.alg}; got ${s.value.length / 2}`, 'signature.value')
|
|
100
119
|
|
|
101
120
|
return {
|
|
102
121
|
ok: true,
|
package/src/verify.d.ts
CHANGED
|
@@ -13,11 +13,18 @@ export function utf8(s: string): Uint8Array
|
|
|
13
13
|
*/
|
|
14
14
|
export function computeKid(publicKey: Uint8Array | string): Promise<string>
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Verify an ML-DSA-65 or ML-DSA-87 signature.
|
|
18
|
+
*
|
|
19
|
+
* The public key decides the parameter set: 1952 bytes is ML-DSA-65, 2592 is
|
|
20
|
+
* ML-DSA-87. Pass `alg` to require a set; a key or signature that is not that
|
|
21
|
+
* set's size returns false. An unsupported `alg` throws a TypeError.
|
|
22
|
+
*/
|
|
17
23
|
export function verifySignature(
|
|
18
24
|
publicKey: Uint8Array | string,
|
|
19
25
|
message: Uint8Array | string,
|
|
20
26
|
signature: Uint8Array | string,
|
|
27
|
+
alg?: 'ML-DSA-65' | 'ML-DSA-87',
|
|
21
28
|
): boolean
|
|
22
29
|
|
|
23
30
|
/** Length-equal hex comparison. */
|
package/src/verify.js
CHANGED
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
// from the Web Crypto API where available (browser, Node 20+), fallback to
|
|
6
6
|
// node:crypto's createHash where SubtleCrypto.digest is not present.
|
|
7
7
|
|
|
8
|
-
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js'
|
|
8
|
+
import { ml_dsa65, ml_dsa87 } from '@noble/post-quantum/ml-dsa.js'
|
|
9
|
+
|
|
10
|
+
// The parameter sets this verifier speaks, with their FIPS 204 public-key sizes.
|
|
11
|
+
// The key decides which one a signature is checked under: its length picks
|
|
12
|
+
// the set, and a key that is not exactly a set's size verifies nothing.
|
|
13
|
+
const ML_DSA = {
|
|
14
|
+
'ML-DSA-65': { primitive: ml_dsa65, publicKey: 1952 },
|
|
15
|
+
'ML-DSA-87': { primitive: ml_dsa87, publicKey: 2592 },
|
|
16
|
+
}
|
|
9
17
|
|
|
10
18
|
/**
|
|
11
19
|
* Hex string → Uint8Array. Throws on malformed input.
|
|
@@ -68,21 +76,41 @@ export async function computeKid(publicKey) {
|
|
|
68
76
|
return bytesToHex(hashBytes.subarray(0, 8))
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
// The ML-DSA parameter set a public key's bytes belong to, by length, or null.
|
|
80
|
+
const algorithmOfKey = (pk) => Object.keys(ML_DSA).find((alg) => ML_DSA[alg].publicKey === pk?.length) ?? null
|
|
81
|
+
|
|
71
82
|
/**
|
|
72
|
-
* Verify an ML-DSA-65 signature.
|
|
73
|
-
*
|
|
83
|
+
* Verify an ML-DSA-65 or ML-DSA-87 signature.
|
|
84
|
+
*
|
|
85
|
+
* The public key decides the parameter set: 1952 bytes is ML-DSA-65 and 2592
|
|
86
|
+
* is ML-DSA-87. Pass `alg` to require a set; a key or signature that is not
|
|
87
|
+
* that set's size then returns false, so a key of one set is never checked
|
|
88
|
+
* as the other. An `alg` this verifier does not speak throws, as malformed
|
|
89
|
+
* hex does, because it is a caller error rather than a failed signature.
|
|
90
|
+
*
|
|
91
|
+
* @param {string|Uint8Array} publicKey — 1952 or 2592 bytes (3904 or 5184 hex chars)
|
|
74
92
|
* @param {string|Uint8Array} message — string (utf8'd) or raw bytes
|
|
75
|
-
* @param {string|Uint8Array} signature — 3309 bytes (6618 hex chars)
|
|
93
|
+
* @param {string|Uint8Array} signature — 3309 or 4627 bytes (6618 or 9254 hex chars)
|
|
94
|
+
* @param {'ML-DSA-65'|'ML-DSA-87'} [alg] — the set the caller requires
|
|
76
95
|
* @returns {boolean}
|
|
77
96
|
*/
|
|
78
|
-
export function verifySignature(publicKey, message, signature) {
|
|
97
|
+
export function verifySignature(publicKey, message, signature, alg) {
|
|
98
|
+
if (alg !== undefined && !(typeof alg === 'string' && Object.hasOwn(ML_DSA, alg))) {
|
|
99
|
+
throw new TypeError(`unsupported algorithm ${JSON.stringify(alg)}: expected "ML-DSA-65" or "ML-DSA-87"`)
|
|
100
|
+
}
|
|
79
101
|
const pk = typeof publicKey === 'string' ? hexToBytes(publicKey) : publicKey
|
|
80
102
|
const sig = typeof signature === 'string' ? hexToBytes(signature) : signature
|
|
81
103
|
const msg = typeof message === 'string' ? utf8(message) : message
|
|
104
|
+
const set = ML_DSA[alg ?? algorithmOfKey(pk)]
|
|
105
|
+
if (!set) return false
|
|
106
|
+
// Checked here, not left to the primitive: a named set must match the key.
|
|
107
|
+
if (pk?.length !== set.publicKey) return false
|
|
82
108
|
// @noble/post-quantum ≥0.6 signature order: (signature, message, publicKey).
|
|
83
|
-
// Matches the canonical wrapper in kxco-post-quantum/src/ml-dsa.js.
|
|
109
|
+
// Matches the canonical wrapper in kxco-post-quantum/src/ml-dsa.js. The
|
|
110
|
+
// primitive refuses a key or signature that is not its set's length, so a
|
|
111
|
+
// key of one set is never checked as the other.
|
|
84
112
|
try {
|
|
85
|
-
return
|
|
113
|
+
return set.primitive.verify(sig, msg, pk)
|
|
86
114
|
} catch {
|
|
87
115
|
return false
|
|
88
116
|
}
|