kxco-verify 1.2.0 → 1.2.2
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 +83 -71
- package/LICENSE +202 -202
- package/README.md +275 -252
- package/package.json +87 -87
- package/src/fetch.d.ts +28 -28
- package/src/fetch.js +100 -100
- package/src/index.d.ts +57 -57
- package/src/index.js +212 -212
- package/src/parse.d.ts +31 -31
- package/src/parse.js +116 -116
- package/src/verify.d.ts +24 -24
- package/src/verify.js +103 -103
package/package.json
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kxco-verify",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Standalone, browser-safe verifier for KXCO ML-DSA-65 post-quantum signed attestations and credentials, for auditors, regulators, counterparties, and anyone who needs to confirm a signature without running the full KXCO SDK.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"post-quantum",
|
|
7
|
-
"pqc",
|
|
8
|
-
"ml-dsa",
|
|
9
|
-
"dilithium",
|
|
10
|
-
"nist",
|
|
11
|
-
"fips-204",
|
|
12
|
-
"verification",
|
|
13
|
-
"attestation",
|
|
14
|
-
"verify",
|
|
15
|
-
"kxco-post-quantum",
|
|
16
|
-
"browser",
|
|
17
|
-
"browser-safe",
|
|
18
|
-
"zero-dependencies",
|
|
19
|
-
"supply-chain",
|
|
20
|
-
"deploy-attestation",
|
|
21
|
-
"provenance",
|
|
22
|
-
"manifest-verification"
|
|
23
|
-
],
|
|
24
|
-
"license": "Apache-2.0",
|
|
25
|
-
"author": "Shayne Heffernan and John Heffernan",
|
|
26
|
-
"contributors": [
|
|
27
|
-
{
|
|
28
|
-
"name": "Shayne Heffernan"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"name": "John Heffernan"
|
|
32
|
-
}
|
|
33
|
-
],
|
|
34
|
-
"homepage": "https://verify.kxco.ai",
|
|
35
|
-
"funding": "https://kxco.ai",
|
|
36
|
-
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "https://github.com/KnightsbridgeAIQ/kxco-verify.git"
|
|
39
|
-
},
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://github.com/KnightsbridgeAIQ/kxco-verify/issues"
|
|
42
|
-
},
|
|
43
|
-
"type": "module",
|
|
44
|
-
"sideEffects": false,
|
|
45
|
-
"main": "./src/index.js",
|
|
46
|
-
"types": "./src/index.d.ts",
|
|
47
|
-
"exports": {
|
|
48
|
-
".": {
|
|
49
|
-
"types": "./src/index.d.ts",
|
|
50
|
-
"import": "./src/index.js"
|
|
51
|
-
},
|
|
52
|
-
"./parse": {
|
|
53
|
-
"types": "./src/parse.d.ts",
|
|
54
|
-
"import": "./src/parse.js"
|
|
55
|
-
},
|
|
56
|
-
"./verify": {
|
|
57
|
-
"types": "./src/verify.d.ts",
|
|
58
|
-
"import": "./src/verify.js"
|
|
59
|
-
},
|
|
60
|
-
"./fetch": {
|
|
61
|
-
"types": "./src/fetch.d.ts",
|
|
62
|
-
"import": "./src/fetch.js"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"files": [
|
|
66
|
-
"src",
|
|
67
|
-
"README.md",
|
|
68
|
-
"LICENSE",
|
|
69
|
-
"CHANGELOG.md"
|
|
70
|
-
],
|
|
71
|
-
"engines": {
|
|
72
|
-
"node": ">=20.19"
|
|
73
|
-
},
|
|
74
|
-
"dependencies": {
|
|
75
|
-
"@noble/post-quantum": "0.7.0"
|
|
76
|
-
},
|
|
77
|
-
"scripts": {
|
|
78
|
-
"test": "node --test --test-reporter=spec test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js",
|
|
79
|
-
"test:cov": "node --test --experimental-test-coverage test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js",
|
|
80
|
-
"smoke": "node scripts/smoke.js",
|
|
81
|
-
"lint": "node -c src/index.js && node -c src/parse.js && node -c src/verify.js && node -c src/fetch.js"
|
|
82
|
-
},
|
|
83
|
-
"publishConfig": {
|
|
84
|
-
"provenance": true,
|
|
85
|
-
"access": "public"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "kxco-verify",
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "Standalone, browser-safe verifier for KXCO ML-DSA-65 post-quantum signed attestations and credentials, for auditors, regulators, counterparties, and anyone who needs to confirm a signature without running the full KXCO SDK.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"post-quantum",
|
|
7
|
+
"pqc",
|
|
8
|
+
"ml-dsa",
|
|
9
|
+
"dilithium",
|
|
10
|
+
"nist",
|
|
11
|
+
"fips-204",
|
|
12
|
+
"verification",
|
|
13
|
+
"attestation",
|
|
14
|
+
"verify",
|
|
15
|
+
"kxco-post-quantum",
|
|
16
|
+
"browser",
|
|
17
|
+
"browser-safe",
|
|
18
|
+
"zero-dependencies",
|
|
19
|
+
"supply-chain",
|
|
20
|
+
"deploy-attestation",
|
|
21
|
+
"provenance",
|
|
22
|
+
"manifest-verification"
|
|
23
|
+
],
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"author": "Shayne Heffernan and John Heffernan",
|
|
26
|
+
"contributors": [
|
|
27
|
+
{
|
|
28
|
+
"name": "Shayne Heffernan"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "John Heffernan"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"homepage": "https://verify.kxco.ai",
|
|
35
|
+
"funding": "https://kxco.ai",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/KnightsbridgeAIQ/kxco-verify.git"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/KnightsbridgeAIQ/kxco-verify/issues"
|
|
42
|
+
},
|
|
43
|
+
"type": "module",
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"main": "./src/index.js",
|
|
46
|
+
"types": "./src/index.d.ts",
|
|
47
|
+
"exports": {
|
|
48
|
+
".": {
|
|
49
|
+
"types": "./src/index.d.ts",
|
|
50
|
+
"import": "./src/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./parse": {
|
|
53
|
+
"types": "./src/parse.d.ts",
|
|
54
|
+
"import": "./src/parse.js"
|
|
55
|
+
},
|
|
56
|
+
"./verify": {
|
|
57
|
+
"types": "./src/verify.d.ts",
|
|
58
|
+
"import": "./src/verify.js"
|
|
59
|
+
},
|
|
60
|
+
"./fetch": {
|
|
61
|
+
"types": "./src/fetch.d.ts",
|
|
62
|
+
"import": "./src/fetch.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"files": [
|
|
66
|
+
"src",
|
|
67
|
+
"README.md",
|
|
68
|
+
"LICENSE",
|
|
69
|
+
"CHANGELOG.md"
|
|
70
|
+
],
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=20.19"
|
|
73
|
+
},
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"@noble/post-quantum": "0.7.0"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"test": "node --test --test-reporter=spec test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js",
|
|
79
|
+
"test:cov": "node --test --experimental-test-coverage test/parse.test.js test/verify.test.js test/fetch.test.js test/index.test.js",
|
|
80
|
+
"smoke": "node scripts/smoke.js",
|
|
81
|
+
"lint": "node -c src/index.js && node -c src/parse.js && node -c src/verify.js && node -c src/fetch.js"
|
|
82
|
+
},
|
|
83
|
+
"publishConfig": {
|
|
84
|
+
"provenance": true,
|
|
85
|
+
"access": "public"
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/fetch.d.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
export interface FetchOk {
|
|
2
|
-
ok: true
|
|
3
|
-
url: string
|
|
4
|
-
status: number
|
|
5
|
-
body: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface FetchErr {
|
|
9
|
-
ok: false
|
|
10
|
-
error: {
|
|
11
|
-
kind: 'fetch'
|
|
12
|
-
code: 'no_fetch' | 'invalid_url' | 'timeout' | 'network' | 'http_status' | 'too_large' | 'read'
|
|
13
|
-
message: string
|
|
14
|
-
url?: string
|
|
15
|
-
status?: number
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface GetJsonBodyOpts {
|
|
20
|
-
timeoutMs?: number
|
|
21
|
-
maxBytes?: number
|
|
22
|
-
fetchImpl?: typeof fetch
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function getJsonBody(
|
|
26
|
-
url: string,
|
|
27
|
-
opts?: GetJsonBodyOpts,
|
|
28
|
-
): Promise<FetchOk | FetchErr>
|
|
1
|
+
export interface FetchOk {
|
|
2
|
+
ok: true
|
|
3
|
+
url: string
|
|
4
|
+
status: number
|
|
5
|
+
body: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface FetchErr {
|
|
9
|
+
ok: false
|
|
10
|
+
error: {
|
|
11
|
+
kind: 'fetch'
|
|
12
|
+
code: 'no_fetch' | 'invalid_url' | 'timeout' | 'network' | 'http_status' | 'too_large' | 'read'
|
|
13
|
+
message: string
|
|
14
|
+
url?: string
|
|
15
|
+
status?: number
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GetJsonBodyOpts {
|
|
20
|
+
timeoutMs?: number
|
|
21
|
+
maxBytes?: number
|
|
22
|
+
fetchImpl?: typeof fetch
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getJsonBody(
|
|
26
|
+
url: string,
|
|
27
|
+
opts?: GetJsonBodyOpts,
|
|
28
|
+
): Promise<FetchOk | FetchErr>
|
package/src/fetch.js
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
// Network fetching. Browser-safe (uses global `fetch`), with a hard timeout
|
|
2
|
-
// per request via AbortController so a slow target site cannot hang the
|
|
3
|
-
// verifier. Returns either { ok: true, body, url, status } or
|
|
4
|
-
// { ok: false, error: { kind: 'fetch', code, message } }.
|
|
5
|
-
|
|
6
|
-
const DEFAULT_TIMEOUT_MS = 3000
|
|
7
|
-
const DEFAULT_MAX_BYTES = 200_000 // attestation manifests are ~11KB; cap at 200KB
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Object} FetchOk
|
|
11
|
-
* @property {true} ok
|
|
12
|
-
* @property {string} url
|
|
13
|
-
* @property {number} status
|
|
14
|
-
* @property {string} body
|
|
15
|
-
*
|
|
16
|
-
* @typedef {Object} FetchErr
|
|
17
|
-
* @property {false} ok
|
|
18
|
-
* @property {{ kind: 'fetch', code: string, message: string, url?: string, status?: number }} error
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* GET a URL with a timeout, returning the body as a UTF-8 string. Caps the
|
|
23
|
-
* response at maxBytes to defend against memory blow-ups from a hostile
|
|
24
|
-
* target. Does NOT throw — always returns a tagged result.
|
|
25
|
-
*
|
|
26
|
-
* @param {string} url
|
|
27
|
-
* @param {{ timeoutMs?: number, maxBytes?: number, fetchImpl?: typeof fetch }} [opts]
|
|
28
|
-
* @returns {Promise<FetchOk | FetchErr>}
|
|
29
|
-
*/
|
|
30
|
-
export async function getJsonBody(url, opts = {}) {
|
|
31
|
-
const timeoutMs = typeof opts.timeoutMs === 'number' ? opts.timeoutMs : DEFAULT_TIMEOUT_MS
|
|
32
|
-
const maxBytes = typeof opts.maxBytes === 'number' ? opts.maxBytes : DEFAULT_MAX_BYTES
|
|
33
|
-
const f = opts.fetchImpl || globalThis.fetch
|
|
34
|
-
|
|
35
|
-
if (typeof f !== 'function') {
|
|
36
|
-
return errFetch_('no_fetch', 'global fetch() not available; pass opts.fetchImpl', { url })
|
|
37
|
-
}
|
|
38
|
-
if (typeof url !== 'string' || (!url.startsWith('http://') && !url.startsWith('https://'))) {
|
|
39
|
-
return errFetch_('invalid_url', 'url must be an absolute http(s) URL', { url })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const ctrl = new AbortController()
|
|
43
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs)
|
|
44
|
-
let res
|
|
45
|
-
try {
|
|
46
|
-
res = await f(url, { signal: ctrl.signal, redirect: 'follow', headers: { 'Accept': 'application/json' } })
|
|
47
|
-
} catch (err) {
|
|
48
|
-
clearTimeout(timer)
|
|
49
|
-
const code = err && err.name === 'AbortError' ? 'timeout' : 'network'
|
|
50
|
-
return errFetch_(code, code === 'timeout' ? `fetch timed out after ${timeoutMs}ms` : `network error: ${err.message}`, { url })
|
|
51
|
-
}
|
|
52
|
-
clearTimeout(timer)
|
|
53
|
-
|
|
54
|
-
if (!res.ok) {
|
|
55
|
-
return errFetch_('http_status', `HTTP ${res.status}`, { url, status: res.status })
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Read with a byte cap to prevent OOM on hostile responses.
|
|
59
|
-
let body
|
|
60
|
-
try {
|
|
61
|
-
body = await readWithCap_(res, maxBytes)
|
|
62
|
-
} catch (err) {
|
|
63
|
-
if (err && err.code === 'too_large') {
|
|
64
|
-
return errFetch_('too_large', `response exceeded ${maxBytes} bytes`, { url, status: res.status })
|
|
65
|
-
}
|
|
66
|
-
return errFetch_('read', `failed to read response body: ${err.message}`, { url, status: res.status })
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { ok: true, url, status: res.status, body }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function readWithCap_(res, maxBytes) {
|
|
73
|
-
if (!res.body || typeof res.body.getReader !== 'function') {
|
|
74
|
-
// Fallback for environments without streams: read the whole text, check size.
|
|
75
|
-
const text = await res.text()
|
|
76
|
-
if (text.length > maxBytes * 4) { const e = new Error('too large'); e.code = 'too_large'; throw e }
|
|
77
|
-
return text
|
|
78
|
-
}
|
|
79
|
-
const reader = res.body.getReader()
|
|
80
|
-
const chunks = []
|
|
81
|
-
let total = 0
|
|
82
|
-
while (true) {
|
|
83
|
-
const { done, value } = await reader.read()
|
|
84
|
-
if (done) break
|
|
85
|
-
total += value.length
|
|
86
|
-
if (total > maxBytes) {
|
|
87
|
-
try { await reader.cancel() } catch {}
|
|
88
|
-
const e = new Error('too large'); e.code = 'too_large'; throw e
|
|
89
|
-
}
|
|
90
|
-
chunks.push(value)
|
|
91
|
-
}
|
|
92
|
-
const merged = new Uint8Array(total)
|
|
93
|
-
let off = 0
|
|
94
|
-
for (const c of chunks) { merged.set(c, off); off += c.length }
|
|
95
|
-
return new TextDecoder('utf-8').decode(merged)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function errFetch_(code, message, extra = {}) {
|
|
99
|
-
return { ok: false, error: { kind: 'fetch', code, message, ...extra } }
|
|
100
|
-
}
|
|
1
|
+
// Network fetching. Browser-safe (uses global `fetch`), with a hard timeout
|
|
2
|
+
// per request via AbortController so a slow target site cannot hang the
|
|
3
|
+
// verifier. Returns either { ok: true, body, url, status } or
|
|
4
|
+
// { ok: false, error: { kind: 'fetch', code, message } }.
|
|
5
|
+
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 3000
|
|
7
|
+
const DEFAULT_MAX_BYTES = 200_000 // attestation manifests are ~11KB; cap at 200KB
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} FetchOk
|
|
11
|
+
* @property {true} ok
|
|
12
|
+
* @property {string} url
|
|
13
|
+
* @property {number} status
|
|
14
|
+
* @property {string} body
|
|
15
|
+
*
|
|
16
|
+
* @typedef {Object} FetchErr
|
|
17
|
+
* @property {false} ok
|
|
18
|
+
* @property {{ kind: 'fetch', code: string, message: string, url?: string, status?: number }} error
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* GET a URL with a timeout, returning the body as a UTF-8 string. Caps the
|
|
23
|
+
* response at maxBytes to defend against memory blow-ups from a hostile
|
|
24
|
+
* target. Does NOT throw — always returns a tagged result.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} url
|
|
27
|
+
* @param {{ timeoutMs?: number, maxBytes?: number, fetchImpl?: typeof fetch }} [opts]
|
|
28
|
+
* @returns {Promise<FetchOk | FetchErr>}
|
|
29
|
+
*/
|
|
30
|
+
export async function getJsonBody(url, opts = {}) {
|
|
31
|
+
const timeoutMs = typeof opts.timeoutMs === 'number' ? opts.timeoutMs : DEFAULT_TIMEOUT_MS
|
|
32
|
+
const maxBytes = typeof opts.maxBytes === 'number' ? opts.maxBytes : DEFAULT_MAX_BYTES
|
|
33
|
+
const f = opts.fetchImpl || globalThis.fetch
|
|
34
|
+
|
|
35
|
+
if (typeof f !== 'function') {
|
|
36
|
+
return errFetch_('no_fetch', 'global fetch() not available; pass opts.fetchImpl', { url })
|
|
37
|
+
}
|
|
38
|
+
if (typeof url !== 'string' || (!url.startsWith('http://') && !url.startsWith('https://'))) {
|
|
39
|
+
return errFetch_('invalid_url', 'url must be an absolute http(s) URL', { url })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ctrl = new AbortController()
|
|
43
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs)
|
|
44
|
+
let res
|
|
45
|
+
try {
|
|
46
|
+
res = await f(url, { signal: ctrl.signal, redirect: 'follow', headers: { 'Accept': 'application/json' } })
|
|
47
|
+
} catch (err) {
|
|
48
|
+
clearTimeout(timer)
|
|
49
|
+
const code = err && err.name === 'AbortError' ? 'timeout' : 'network'
|
|
50
|
+
return errFetch_(code, code === 'timeout' ? `fetch timed out after ${timeoutMs}ms` : `network error: ${err.message}`, { url })
|
|
51
|
+
}
|
|
52
|
+
clearTimeout(timer)
|
|
53
|
+
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
return errFetch_('http_status', `HTTP ${res.status}`, { url, status: res.status })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Read with a byte cap to prevent OOM on hostile responses.
|
|
59
|
+
let body
|
|
60
|
+
try {
|
|
61
|
+
body = await readWithCap_(res, maxBytes)
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err && err.code === 'too_large') {
|
|
64
|
+
return errFetch_('too_large', `response exceeded ${maxBytes} bytes`, { url, status: res.status })
|
|
65
|
+
}
|
|
66
|
+
return errFetch_('read', `failed to read response body: ${err.message}`, { url, status: res.status })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { ok: true, url, status: res.status, body }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function readWithCap_(res, maxBytes) {
|
|
73
|
+
if (!res.body || typeof res.body.getReader !== 'function') {
|
|
74
|
+
// Fallback for environments without streams: read the whole text, check size.
|
|
75
|
+
const text = await res.text()
|
|
76
|
+
if (text.length > maxBytes * 4) { const e = new Error('too large'); e.code = 'too_large'; throw e }
|
|
77
|
+
return text
|
|
78
|
+
}
|
|
79
|
+
const reader = res.body.getReader()
|
|
80
|
+
const chunks = []
|
|
81
|
+
let total = 0
|
|
82
|
+
while (true) {
|
|
83
|
+
const { done, value } = await reader.read()
|
|
84
|
+
if (done) break
|
|
85
|
+
total += value.length
|
|
86
|
+
if (total > maxBytes) {
|
|
87
|
+
try { await reader.cancel() } catch {}
|
|
88
|
+
const e = new Error('too large'); e.code = 'too_large'; throw e
|
|
89
|
+
}
|
|
90
|
+
chunks.push(value)
|
|
91
|
+
}
|
|
92
|
+
const merged = new Uint8Array(total)
|
|
93
|
+
let off = 0
|
|
94
|
+
for (const c of chunks) { merged.set(c, off); off += c.length }
|
|
95
|
+
return new TextDecoder('utf-8').decode(merged)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function errFetch_(code, message, extra = {}) {
|
|
99
|
+
return { ok: false, error: { kind: 'fetch', code, message, ...extra } }
|
|
100
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
export type VerifyState = 'valid' | 'rotated' | 'invalid' | 'error'
|
|
2
|
-
|
|
3
|
-
export interface VerifyResultError {
|
|
4
|
-
kind: 'parse' | 'fetch' | 'signature' | 'consistency' | 'rotation'
|
|
5
|
-
code: string
|
|
6
|
-
message: string
|
|
7
|
-
/** true when the error is non-fatal (math succeeded but a soft check failed). */
|
|
8
|
-
soft?: boolean
|
|
9
|
-
[k: string]: unknown
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface VerifyResult {
|
|
13
|
-
state: VerifyState
|
|
14
|
-
algorithm?: 'ML-DSA-65'
|
|
15
|
-
/** kid as declared inside the manifest itself */
|
|
16
|
-
manifestKid?: string
|
|
17
|
-
/** kid currently served at the live well-known pubkey endpoint (when fetched) */
|
|
18
|
-
livePubkeyKid?: string
|
|
19
|
-
/** site identifier as declared by the manifest */
|
|
20
|
-
site?: string
|
|
21
|
-
/** opaque deployment metadata from the manifest */
|
|
22
|
-
deployment?: Record<string, unknown>
|
|
23
|
-
/** full parsed manifest JSON, for UI display */
|
|
24
|
-
manifestRaw?: Record<string, unknown>
|
|
25
|
-
/** present when state is "error", "invalid", or "rotated" */
|
|
26
|
-
error?: VerifyResultError
|
|
27
|
-
attestationUrl?: string
|
|
28
|
-
/** URL of the live well-known pubkey endpoint (only present when fetched) */
|
|
29
|
-
pubkeyUrl?: string
|
|
30
|
-
/** Date.now() at the moment verification finished */
|
|
31
|
-
verifiedAtMs?: number
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface VerifyUrlOpts {
|
|
35
|
-
timeoutMs?: number
|
|
36
|
-
maxBytes?: number
|
|
37
|
-
fetchImpl?: typeof fetch
|
|
38
|
-
/** When true, skip fetching the live well-known pubkey (no rotation detection). */
|
|
39
|
-
skipLivePubkey?: boolean
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Verify an attestation manifest you already have in hand.
|
|
44
|
-
* Result is "valid" / "invalid" / "error" — never "rotated" (no live fetch).
|
|
45
|
-
*/
|
|
46
|
-
export function verifyManifest(manifestBody: string | object): Promise<VerifyResult>
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Verify an attestation by URL. May return "rotated" if the live well-known
|
|
50
|
-
* pubkey endpoint serves a different kid than the manifest declared.
|
|
51
|
-
*/
|
|
52
|
-
export function verifyUrl(attestationUrl: string, opts?: VerifyUrlOpts): Promise<VerifyResult>
|
|
53
|
-
|
|
54
|
-
// Re-exports.
|
|
55
|
-
export { parseManifest, ParseResult, ParsedManifest, ParseError } from './parse.js'
|
|
56
|
-
export { verifySignature, computeKid, hexToBytes, bytesToHex, hexEquals } from './verify.js'
|
|
57
|
-
export { getJsonBody, FetchOk, FetchErr, GetJsonBodyOpts } from './fetch.js'
|
|
1
|
+
export type VerifyState = 'valid' | 'rotated' | 'invalid' | 'error'
|
|
2
|
+
|
|
3
|
+
export interface VerifyResultError {
|
|
4
|
+
kind: 'parse' | 'fetch' | 'signature' | 'consistency' | 'rotation'
|
|
5
|
+
code: string
|
|
6
|
+
message: string
|
|
7
|
+
/** true when the error is non-fatal (math succeeded but a soft check failed). */
|
|
8
|
+
soft?: boolean
|
|
9
|
+
[k: string]: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface VerifyResult {
|
|
13
|
+
state: VerifyState
|
|
14
|
+
algorithm?: 'ML-DSA-65'
|
|
15
|
+
/** kid as declared inside the manifest itself */
|
|
16
|
+
manifestKid?: string
|
|
17
|
+
/** kid currently served at the live well-known pubkey endpoint (when fetched) */
|
|
18
|
+
livePubkeyKid?: string
|
|
19
|
+
/** site identifier as declared by the manifest */
|
|
20
|
+
site?: string
|
|
21
|
+
/** opaque deployment metadata from the manifest */
|
|
22
|
+
deployment?: Record<string, unknown>
|
|
23
|
+
/** full parsed manifest JSON, for UI display */
|
|
24
|
+
manifestRaw?: Record<string, unknown>
|
|
25
|
+
/** present when state is "error", "invalid", or "rotated" */
|
|
26
|
+
error?: VerifyResultError
|
|
27
|
+
attestationUrl?: string
|
|
28
|
+
/** URL of the live well-known pubkey endpoint (only present when fetched) */
|
|
29
|
+
pubkeyUrl?: string
|
|
30
|
+
/** Date.now() at the moment verification finished */
|
|
31
|
+
verifiedAtMs?: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface VerifyUrlOpts {
|
|
35
|
+
timeoutMs?: number
|
|
36
|
+
maxBytes?: number
|
|
37
|
+
fetchImpl?: typeof fetch
|
|
38
|
+
/** When true, skip fetching the live well-known pubkey (no rotation detection). */
|
|
39
|
+
skipLivePubkey?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Verify an attestation manifest you already have in hand.
|
|
44
|
+
* Result is "valid" / "invalid" / "error" — never "rotated" (no live fetch).
|
|
45
|
+
*/
|
|
46
|
+
export function verifyManifest(manifestBody: string | object): Promise<VerifyResult>
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Verify an attestation by URL. May return "rotated" if the live well-known
|
|
50
|
+
* pubkey endpoint serves a different kid than the manifest declared.
|
|
51
|
+
*/
|
|
52
|
+
export function verifyUrl(attestationUrl: string, opts?: VerifyUrlOpts): Promise<VerifyResult>
|
|
53
|
+
|
|
54
|
+
// Re-exports.
|
|
55
|
+
export { parseManifest, ParseResult, ParsedManifest, ParseError } from './parse.js'
|
|
56
|
+
export { verifySignature, computeKid, hexToBytes, bytesToHex, hexEquals } from './verify.js'
|
|
57
|
+
export { getJsonBody, FetchOk, FetchErr, GetJsonBodyOpts } from './fetch.js'
|