@optimystic/quereus-plugin-crypto 0.22.0 → 0.24.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.
- package/CHANGELOG.md +91 -91
- package/dist/index.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -2
- package/src/cid.ts +200 -200
- package/src/crypto.ts +547 -547
- package/src/sd.ts +250 -250
package/CHANGELOG.md
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.14.0 — BREAKING: `digest` API rework
|
|
4
|
-
|
|
5
|
-
### What changed
|
|
6
|
-
|
|
7
|
-
The exported `digest()` function signature changed from:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
// OLD (≤ 0.13.x)
|
|
11
|
-
digest(data: string | Uint8Array, algorithm?, inputEncoding?, outputEncoding?)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
to:
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
// NEW (≥ 0.14.0)
|
|
18
|
-
digest(fields: readonly DigestField[], algorithm?, encoding?)
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Key differences:
|
|
22
|
-
|
|
23
|
-
| | Old | New |
|
|
24
|
-
|---|---|---|
|
|
25
|
-
| First argument | A single scalar value (`string` \| `Uint8Array`) | An **array** of values |
|
|
26
|
-
| `inputEncoding` | 3rd positional arg | **Removed** (the new API frames values by type, no string-decoding step) |
|
|
27
|
-
| Output encoding | 4th positional arg | 2nd `encoding` arg (shifted left by one) |
|
|
28
|
-
| Algorithm | 2nd positional arg | 2nd `algorithm` arg (unchanged position) |
|
|
29
|
-
| Result | Bare hash of the decoded bytes | **Framed** injective digest — `digest(['hello'])` ≠ `sha256("hello")` |
|
|
30
|
-
| Algorithm + encoding | Per-call | **Bound at plugin load time** for the SQL function |
|
|
31
|
-
|
|
32
|
-
### Migration: JS/TypeScript callers
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
// OLD
|
|
36
|
-
const hashBytes = digest(payload, 'sha256', 'utf8', 'bytes') as Uint8Array;
|
|
37
|
-
const payloadDigest = digest(payload, 'sha256', 'utf8', 'base64url') as string;
|
|
38
|
-
|
|
39
|
-
// NEW — wrap the value in an array, drop inputEncoding
|
|
40
|
-
const hashBytes = digest([payload], 'sha256', 'bytes') as Uint8Array;
|
|
41
|
-
const payloadDigest = digest([payload], 'sha256', 'base64url') as string;
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
> **The result value changes.** The new digest is *framed* (version byte + type tag +
|
|
45
|
-
> length-prefixed payload per field), so `digest(['hello'])` is **not** the same bytes
|
|
46
|
-
> as `sha256(utf8("hello"))`. If you need to match an externally-computed bare hash,
|
|
47
|
-
> see the open question below.
|
|
48
|
-
|
|
49
|
-
### Migration: SQL callers
|
|
50
|
-
|
|
51
|
-
The SQL `digest(field1, field2, ...)` function is **variadic over data fields** — every
|
|
52
|
-
argument is a field to hash, not a config option — so the signature is unchanged from the
|
|
53
|
-
SQL perspective.
|
|
54
|
-
|
|
55
|
-
However, if you were passing extra positional arguments to mimic `algo`/`inputEncoding`/`outputEncoding`
|
|
56
|
-
(e.g. `digest(data, 'sha256', 'utf8', 'bytes')`), those are now treated as **additional
|
|
57
|
-
data fields** and hashed into the result silently rather than interpreted as config. There
|
|
58
|
-
is **no error** on the SQL path for this; it just hashes more fields. Check any SQL call
|
|
59
|
-
sites that pass more than pure data arguments.
|
|
60
|
-
|
|
61
|
-
Algorithm and encoding are now set via the plugin config at load time (see the
|
|
62
|
-
[Digest configuration](README.md#digest-configuration) section of the README).
|
|
63
|
-
|
|
64
|
-
### Why no compatibility shim?
|
|
65
|
-
|
|
66
|
-
The old and new calling conventions cannot be cleanly disambiguated: the new first argument
|
|
67
|
-
is always an array; the old's was always a scalar. Adding a scalar → old-API detection
|
|
68
|
-
shim would silently re-enable the broken `inputEncoding` positional footgun and make the
|
|
69
|
-
result value unpredictable. The clean break stays; instead, old-style JS calls now throw
|
|
70
|
-
a clear, actionable error message naming this migration note.
|
|
71
|
-
|
|
72
|
-
### Error message for old-style calls
|
|
73
|
-
|
|
74
|
-
If you pass a non-array as the first argument to `digest()` in JS/TypeScript, you will now
|
|
75
|
-
see:
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
digest(fields, algorithm?, encoding?): 'fields' must be an array of values.
|
|
79
|
-
The digest API changed in v0.14: it is now variadic/injective over fields,
|
|
80
|
-
the per-call inputEncoding was removed, and algorithm + output encoding are bound at plugin load time.
|
|
81
|
-
Migrate digest(value, algo, inputEncoding, outputEncoding) → digest([value], algo, outputEncoding) —
|
|
82
|
-
note the result is now a *framed* digest, not a bare hash of the bytes.
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Open question: bare hash helper
|
|
86
|
-
|
|
87
|
-
The new `digest` has no function that returns a bare (un-framed) hash of a single value's
|
|
88
|
-
bytes in a chosen encoding — what the old `digest(x, algo, inEnc, outEnc)` did. If you
|
|
89
|
-
need bare-hash semantics (e.g. to match a hash stored before v0.14, or computed by an
|
|
90
|
-
external system), there is currently no drop-in. File a separate issue/ticket if a
|
|
91
|
-
`hash(data, algorithm, inputEncoding, outputEncoding)` helper is needed.
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.14.0 — BREAKING: `digest` API rework
|
|
4
|
+
|
|
5
|
+
### What changed
|
|
6
|
+
|
|
7
|
+
The exported `digest()` function signature changed from:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// OLD (≤ 0.13.x)
|
|
11
|
+
digest(data: string | Uint8Array, algorithm?, inputEncoding?, outputEncoding?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
to:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// NEW (≥ 0.14.0)
|
|
18
|
+
digest(fields: readonly DigestField[], algorithm?, encoding?)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Key differences:
|
|
22
|
+
|
|
23
|
+
| | Old | New |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| First argument | A single scalar value (`string` \| `Uint8Array`) | An **array** of values |
|
|
26
|
+
| `inputEncoding` | 3rd positional arg | **Removed** (the new API frames values by type, no string-decoding step) |
|
|
27
|
+
| Output encoding | 4th positional arg | 2nd `encoding` arg (shifted left by one) |
|
|
28
|
+
| Algorithm | 2nd positional arg | 2nd `algorithm` arg (unchanged position) |
|
|
29
|
+
| Result | Bare hash of the decoded bytes | **Framed** injective digest — `digest(['hello'])` ≠ `sha256("hello")` |
|
|
30
|
+
| Algorithm + encoding | Per-call | **Bound at plugin load time** for the SQL function |
|
|
31
|
+
|
|
32
|
+
### Migration: JS/TypeScript callers
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// OLD
|
|
36
|
+
const hashBytes = digest(payload, 'sha256', 'utf8', 'bytes') as Uint8Array;
|
|
37
|
+
const payloadDigest = digest(payload, 'sha256', 'utf8', 'base64url') as string;
|
|
38
|
+
|
|
39
|
+
// NEW — wrap the value in an array, drop inputEncoding
|
|
40
|
+
const hashBytes = digest([payload], 'sha256', 'bytes') as Uint8Array;
|
|
41
|
+
const payloadDigest = digest([payload], 'sha256', 'base64url') as string;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **The result value changes.** The new digest is *framed* (version byte + type tag +
|
|
45
|
+
> length-prefixed payload per field), so `digest(['hello'])` is **not** the same bytes
|
|
46
|
+
> as `sha256(utf8("hello"))`. If you need to match an externally-computed bare hash,
|
|
47
|
+
> see the open question below.
|
|
48
|
+
|
|
49
|
+
### Migration: SQL callers
|
|
50
|
+
|
|
51
|
+
The SQL `digest(field1, field2, ...)` function is **variadic over data fields** — every
|
|
52
|
+
argument is a field to hash, not a config option — so the signature is unchanged from the
|
|
53
|
+
SQL perspective.
|
|
54
|
+
|
|
55
|
+
However, if you were passing extra positional arguments to mimic `algo`/`inputEncoding`/`outputEncoding`
|
|
56
|
+
(e.g. `digest(data, 'sha256', 'utf8', 'bytes')`), those are now treated as **additional
|
|
57
|
+
data fields** and hashed into the result silently rather than interpreted as config. There
|
|
58
|
+
is **no error** on the SQL path for this; it just hashes more fields. Check any SQL call
|
|
59
|
+
sites that pass more than pure data arguments.
|
|
60
|
+
|
|
61
|
+
Algorithm and encoding are now set via the plugin config at load time (see the
|
|
62
|
+
[Digest configuration](README.md#digest-configuration) section of the README).
|
|
63
|
+
|
|
64
|
+
### Why no compatibility shim?
|
|
65
|
+
|
|
66
|
+
The old and new calling conventions cannot be cleanly disambiguated: the new first argument
|
|
67
|
+
is always an array; the old's was always a scalar. Adding a scalar → old-API detection
|
|
68
|
+
shim would silently re-enable the broken `inputEncoding` positional footgun and make the
|
|
69
|
+
result value unpredictable. The clean break stays; instead, old-style JS calls now throw
|
|
70
|
+
a clear, actionable error message naming this migration note.
|
|
71
|
+
|
|
72
|
+
### Error message for old-style calls
|
|
73
|
+
|
|
74
|
+
If you pass a non-array as the first argument to `digest()` in JS/TypeScript, you will now
|
|
75
|
+
see:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
digest(fields, algorithm?, encoding?): 'fields' must be an array of values.
|
|
79
|
+
The digest API changed in v0.14: it is now variadic/injective over fields,
|
|
80
|
+
the per-call inputEncoding was removed, and algorithm + output encoding are bound at plugin load time.
|
|
81
|
+
Migrate digest(value, algo, inputEncoding, outputEncoding) → digest([value], algo, outputEncoding) —
|
|
82
|
+
note the result is now a *framed* digest, not a bare hash of the bytes.
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Open question: bare hash helper
|
|
86
|
+
|
|
87
|
+
The new `digest` has no function that returns a bare (un-framed) hash of a single value's
|
|
88
|
+
bytes in a chosen encoding — what the old `digest(x, algo, inEnc, outEnc)` did. If you
|
|
89
|
+
need bare-hash semantics (e.g. to match a hash stored before v0.14, or computed by an
|
|
90
|
+
external system), there is currently no drop-in. File a separate issue/ticket if a
|
|
91
|
+
`hash(data, algorithm, inputEncoding, outputEncoding)` helper is needed.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crypto.ts","../src/cid.ts","../src/sd.ts"],"names":["uint8ArrayFromString","uint8ArrayToString","nobleRandomBytes","digest"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Cryptographic Functions for Quereus\n *\n * Idiomatic ES module exports with base64url as default encoding.\n * All functions accept and return base64url strings by default for SQL compatibility.\n */\n\nimport { sha256, sha512 } from '@noble/hashes/sha2.js';\nimport { blake3 } from '@noble/hashes/blake3.js';\nimport { randomBytes as nobleRandomBytes, utf8ToBytes, concatBytes } from '@noble/hashes/utils.js';\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { p256 } from '@noble/curves/nist.js';\nimport { ed25519 } from '@noble/curves/ed25519.js';\nimport { hexToBytes, bytesToHex } from '@noble/curves/utils.js';\nimport { toString as uint8ArrayToString, fromString as uint8ArrayFromString } from 'uint8arrays';\n\n// Type definitions\nexport type HashAlgorithm = 'sha256' | 'sha512' | 'blake3';\nexport type CurveType = 'secp256k1' | 'p256' | 'ed25519';\nexport type Encoding = 'base64url' | 'base64' | 'hex' | 'utf8' | 'bytes';\n\n/** Encodings valid for hash *output* (no 'utf8' — a digest is not UTF-8 text). */\nexport type OutputEncoding = 'base64url' | 'base64' | 'hex' | 'bytes';\n\n/** A single value in a multi-field digest. Mirrors the SQL value space. */\nexport type DigestField =\n\t| string\n\t| number\n\t| bigint\n\t| boolean\n\t| Uint8Array\n\t| null\n\t| undefined\n\t| { readonly [key: string]: unknown }\n\t| readonly unknown[];\n\n/** A resolved hash function: raw bytes in, digest bytes out. */\nexport type DigestHasher = (input: Uint8Array) => Uint8Array;\n\n/** A resolved output encoder: digest bytes in, encoded form out. */\nexport type OutputEncoder = (bytes: Uint8Array) => string | Uint8Array;\n\n/**\n * Convert input to Uint8Array, handling various encodings\n */\nfunction toBytes(input: string | Uint8Array | null | undefined, encoding: Encoding = 'base64url'): Uint8Array {\n\tif (input === null || input === undefined) {\n\t\treturn new Uint8Array(0);\n\t}\n\n\tif (input instanceof Uint8Array) {\n\t\treturn input;\n\t}\n\n\tif (typeof input === 'string') {\n\t\tswitch (encoding) {\n\t\t\tcase 'base64url':\n\t\t\t\treturn uint8ArrayFromString(input, 'base64url');\n\t\t\tcase 'base64':\n\t\t\t\treturn uint8ArrayFromString(input, 'base64');\n\t\t\tcase 'hex':\n\t\t\t\treturn hexToBytes(input);\n\t\t\tcase 'utf8':\n\t\t\t\treturn utf8ToBytes(input);\n\t\t\tdefault:\n\t\t\t\treturn uint8ArrayFromString(input, 'base64url');\n\t\t}\n\t}\n\n\tthrow new Error('Invalid input type');\n}\n\n/**\n * Convert Uint8Array to string in specified encoding\n */\nfunction fromBytes(bytes: Uint8Array, encoding: Encoding = 'base64url'): string | Uint8Array {\n\tswitch (encoding) {\n\t\tcase 'base64url':\n\t\t\treturn uint8ArrayToString(bytes, 'base64url');\n\t\tcase 'base64':\n\t\t\treturn uint8ArrayToString(bytes, 'base64');\n\t\tcase 'hex':\n\t\t\treturn bytesToHex(bytes);\n\t\tcase 'utf8':\n\t\t\treturn uint8ArrayToString(bytes, 'utf8');\n\t\tcase 'bytes':\n\t\t\treturn bytes;\n\t\tdefault:\n\t\t\treturn uint8ArrayToString(bytes, 'base64url');\n\t}\n}\n\n// --- Algorithm / encoding resolution (done once, no per-call switching) --- //\n\n/** Hash algorithm → noble hasher. Keyed lookup so the digest hot path never branches. */\nconst HASHERS: Record<HashAlgorithm, DigestHasher> = {\n\tsha256,\n\tsha512,\n\tblake3,\n};\n\n/** Output encoding → encoder closure. */\nconst OUTPUT_ENCODERS: Record<OutputEncoding, OutputEncoder> = {\n\tbase64url: (bytes) => uint8ArrayToString(bytes, 'base64url'),\n\tbase64: (bytes) => uint8ArrayToString(bytes, 'base64'),\n\thex: (bytes) => bytesToHex(bytes),\n\tbytes: (bytes) => bytes,\n};\n\n/**\n * Resolve a hash algorithm name to its hasher. Throws on unknown algorithm.\n * Call once (e.g. at plugin registration) and capture the result so the digest\n * hot path performs no per-call algorithm branching.\n */\nexport function resolveHasher(algorithm: HashAlgorithm): DigestHasher {\n\tconst hasher = HASHERS[algorithm];\n\tif (!hasher) {\n\t\tthrow new Error(`Unsupported hash algorithm: ${algorithm}`);\n\t}\n\treturn hasher;\n}\n\n/**\n * Resolve an output encoding name to its encoder. Throws on unknown encoding.\n */\nexport function resolveOutputEncoder(encoding: OutputEncoding): OutputEncoder {\n\tconst encoder = OUTPUT_ENCODERS[encoding];\n\tif (!encoder) {\n\t\tthrow new Error(`Unsupported output encoding: ${encoding}`);\n\t}\n\treturn encoder;\n}\n\n// --- Canonical, injective multi-field encoding --- //\n\n/**\n * Format version for {@link encodeFields}. Prepended to every encoding so the\n * framing can evolve, and so a framed digest is domain-separated from a bare\n * hash of the same bytes. Bump only with a deliberate, breaking format change.\n */\nconst DIGEST_FORMAT_V1 = 0x01;\n\n// Per-field type tags. Distinct tags keep distinct SQL types from colliding\n// (e.g. INTEGER 123 vs TEXT '123' vs BOOL true).\n//\n// Note on INT vs REAL: the tag is derived from the JS value, not from SQL\n// affinity (a scalar function does not receive affinity). An integer-VALUED\n// number — including a REAL like 2.0, which reaches JS as the number 2 — is\n// encoded as INTEGER. So INTEGER 2 and REAL 2.0 produce the same digest. This is\n// replicable (every peer sees the same JS value) but not int/real-distinguishing.\nconst TAG_NULL = 0x00; // bare tag, no length/payload\nconst TAG_INT = 0x01; // payload: canonical decimal string (number-integer & bigint unified via BigInt)\nconst TAG_REAL = 0x02; // payload: ECMAScript Number::toString (non-integer numbers only)\nconst TAG_TEXT = 0x03; // payload: UTF-8 bytes\nconst TAG_BOOL = 0x04; // payload: single 0x00/0x01 byte\nconst TAG_BLOB = 0x05; // payload: raw bytes\nconst TAG_JSON = 0x06; // payload: UTF-8 of key-sorted canonical JSON\n\n/** Append an unsigned LEB128 varint (safe for lengths up to MAX_SAFE_INTEGER). */\nfunction writeVarint(out: number[], value: number): void {\n\tif (!Number.isInteger(value) || value < 0) {\n\t\tthrow new Error(`varint expects a non-negative integer, got ${value}`);\n\t}\n\tlet v = value;\n\twhile (v >= 0x80) {\n\t\tout.push((v & 0x7f) | 0x80);\n\t\tv = Math.floor(v / 128);\n\t}\n\tout.push(v);\n}\n\n/** tag ‖ varint(len) ‖ payload */\nfunction framed(tag: number, payload: Uint8Array): Uint8Array {\n\tconst header: number[] = [tag];\n\twriteVarint(header, payload.length);\n\treturn concatBytes(Uint8Array.from(header), payload);\n}\n\n/**\n * Strict, deterministic JSON canonicalization for a native object/array field:\n * object keys recursively sorted, no incidental whitespace. Unlike `JSON.stringify`,\n * it THROWS rather than silently collapsing non-JSON inputs (`undefined`, non-finite\n * numbers, `bigint`, non-plain objects like `Date`/`Map`) — silent collapse would\n * break injectivity (`{a:undefined}` vs `{}`, `NaN` vs `null`, `new Date(0)` vs `{}`).\n */\nfunction canonicalJson(value: unknown): string {\n\tif (value === null) return 'null';\n\tconst t = typeof value;\n\tif (t === 'string') return JSON.stringify(value);\n\tif (t === 'boolean') return value ? 'true' : 'false';\n\tif (t === 'number') {\n\t\tif (!Number.isFinite(value)) {\n\t\t\tthrow new Error('digest: cannot encode a non-finite number inside a JSON field');\n\t\t}\n\t\treturn JSON.stringify(value) as string; // deterministic Number::toString\n\t}\n\tif (t === 'bigint') {\n\t\tthrow new Error('digest: bigint is not representable inside a JSON field');\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map((el) => {\n\t\t\tif (el === undefined) {\n\t\t\t\tthrow new Error('digest: undefined / sparse element inside a JSON field');\n\t\t\t}\n\t\t\treturn canonicalJson(el);\n\t\t}).join(',')}]`;\n\t}\n\tif (t === 'object') {\n\t\tconst proto = Object.getPrototypeOf(value);\n\t\tif (proto !== Object.prototype && proto !== null) {\n\t\t\tthrow new Error('digest: only plain objects are allowed inside a JSON field');\n\t\t}\n\t\tconst obj = value as Record<string, unknown>;\n\t\tconst keys = Object.keys(obj).sort();\n\t\treturn `{${keys.map((k) => {\n\t\t\tif (obj[k] === undefined) {\n\t\t\t\tthrow new Error(`digest: undefined value for JSON key '${k}'`);\n\t\t\t}\n\t\t\treturn `${JSON.stringify(k)}:${canonicalJson(obj[k])}`;\n\t\t}).join(',')}}`;\n\t}\n\tthrow new Error(`digest: unsupported value of type '${t}' inside a JSON field`);\n}\n\n/** Encode one field as tag (‖ length ‖ payload). NULL/undefined is a bare tag. */\nfunction encodeField(field: DigestField): Uint8Array {\n\tif (field === null || field === undefined) {\n\t\treturn Uint8Array.of(TAG_NULL);\n\t}\n\tswitch (typeof field) {\n\t\tcase 'boolean':\n\t\t\treturn Uint8Array.of(TAG_BOOL, field ? 1 : 0);\n\t\tcase 'bigint':\n\t\t\treturn framed(TAG_INT, utf8ToBytes(field.toString()));\n\t\tcase 'number':\n\t\t\tif (!Number.isFinite(field)) {\n\t\t\t\tthrow new Error('digest: cannot encode a non-finite number');\n\t\t\t}\n\t\t\t// Integer-valued numbers go through BigInt so they encode identically to\n\t\t\t// the equal-valued bigint (e.g. 1e21 → full digits, not \"1e+21\").\n\t\t\treturn Number.isInteger(field)\n\t\t\t\t? framed(TAG_INT, utf8ToBytes(BigInt(field).toString()))\n\t\t\t\t: framed(TAG_REAL, utf8ToBytes(field.toString()));\n\t\tcase 'string':\n\t\t\treturn framed(TAG_TEXT, utf8ToBytes(field));\n\t\tcase 'object':\n\t\t\tif (field instanceof Uint8Array) {\n\t\t\t\treturn framed(TAG_BLOB, field);\n\t\t\t}\n\t\t\treturn framed(TAG_JSON, utf8ToBytes(canonicalJson(field)));\n\t\tdefault:\n\t\t\tthrow new Error(`digest: unsupported field type '${typeof field}'`);\n\t}\n}\n\n/**\n * Canonically encode an ordered tuple of fields into bytes such that distinct\n * tuples never collide (injective framing).\n *\n * Layout: `version ‖ field*` where each field is `tag ‖ varint(len) ‖ payload`\n * (NULL is a bare tag). Properties:\n * - order-preserving and arity-safe (self-delimiting fields → uniquely decodable),\n * - NULL distinguishable from empty string,\n * - type distinguishable (INTEGER 123 ≠ TEXT '123' ≠ BOOL true ≠ BLOB),\n * - delimiter-safe (a separator inside a string is just payload under its length).\n *\n * Replicability notes:\n * - Integer `number` and `bigint` of equal value encode identically (both via\n * `BigInt(...).toString()`); a non-integer REAL uses ECMAScript `Number::toString`\n * (deterministic across JS engines, but not guaranteed across other languages).\n * - INT vs REAL is derived from the JS value, not SQL affinity: an integer-valued\n * REAL (e.g. 2.0 → number 2) encodes as INTEGER, so INTEGER 2 and REAL 2.0 collide.\n * - A native JSON object/array field must contain only valid JSON (no `undefined`,\n * non-finite numbers, `bigint`, or non-plain objects) — otherwise it throws.\n */\nexport function encodeFields(fields: readonly DigestField[]): Uint8Array {\n\tconst chunks: Uint8Array[] = [Uint8Array.of(DIGEST_FORMAT_V1)];\n\tfor (const field of fields) {\n\t\tchunks.push(encodeField(field));\n\t}\n\treturn concatBytes(...chunks);\n}\n\n/**\n * Low-level multi-field digest: canonically encode the fields, then hash and\n * encode with the supplied (pre-resolved) hasher/encoder. No per-call branching\n * on algorithm or encoding — resolve once via {@link resolveHasher} /\n * {@link resolveOutputEncoder} and reuse.\n */\nexport function digestFields(\n\tfields: readonly DigestField[],\n\thasher: DigestHasher,\n\tencode: OutputEncoder\n): string | Uint8Array {\n\treturn encode(hasher(encodeFields(fields)));\n}\n\n/**\n * Compute an injective digest over an ordered tuple of fields.\n *\n * @param fields - Ordered tuple of values to hash (any SQL value type)\n * @param algorithm - Hash algorithm (default: 'sha256')\n * @param encoding - Output encoding (default: 'base64url')\n * @returns Hash digest in the specified encoding\n *\n * @example\n * ```typescript\n * // Hash a tuple of fields — distinct tuples never collide\n * const h = digest(['alice', 42, null, true]);\n *\n * // Pick algorithm / output encoding\n * const h512 = digest(['a', 'b'], 'sha512', 'hex');\n * ```\n *\n * Note: this is a *framed* digest, not a bare hash of raw bytes —\n * `digest(['hello'])` is not `sha256(\"hello\")`. Use `hashMod` for sharding a\n * single value.\n */\nexport function digest(\n\tfields: readonly DigestField[],\n\talgorithm: HashAlgorithm = 'sha256',\n\tencoding: OutputEncoding = 'base64url'\n): string | Uint8Array {\n\tif (!Array.isArray(fields)) {\n\t\tthrow new Error(\n\t\t\t`digest(fields, algorithm?, encoding?): 'fields' must be an array of values. ` +\n\t\t\t`The digest API changed in v0.14: it is now variadic/injective over fields, ` +\n\t\t\t`the per-call inputEncoding was removed, and algorithm + output encoding are bound at plugin load time. ` +\n\t\t\t`Migrate digest(value, algo, inputEncoding, outputEncoding) → digest([value], algo, outputEncoding) — ` +\n\t\t\t`note the result is now a *framed* digest, not a bare hash of the bytes.`\n\t\t);\n\t}\n\treturn digestFields(fields, resolveHasher(algorithm), resolveOutputEncoder(encoding));\n}\n\n/**\n * Hash data and return modulo of specified bit length\n * Useful for generating fixed-size hash values (e.g., 16-bit, 32-bit)\n *\n * @param data - Data to hash\n * @param bits - Number of bits for the result (e.g., 16 for 16-bit hash)\n * @param algorithm - Hash algorithm (default: 'sha256')\n * @param inputEncoding - Encoding of input string (default: 'base64url')\n * @returns Integer hash value modulo 2^bits\n *\n * @example\n * ```typescript\n * // Get 16-bit hash (0-65535)\n * const hash16 = hashMod('hello', 16, 'sha256', 'utf8');\n *\n * // Get 32-bit hash\n * const hash32 = hashMod('world', 32, 'sha256', 'utf8');\n * ```\n */\nexport function hashMod(\n\tdata: string | Uint8Array,\n\tbits: number,\n\talgorithm: HashAlgorithm = 'sha256',\n\tinputEncoding: Encoding = 'base64url'\n): number {\n\tif (bits <= 0 || bits > 53) {\n\t\tthrow new Error('Bits must be between 1 and 53 (JavaScript safe integer limit)');\n\t}\n\n\t// Single-blob hash for sharding (not the field-framed digest).\n\tconst hashBytes = resolveHasher(algorithm)(toBytes(data, inputEncoding));\n\n\t// Take first 8 bytes and convert to number\n\tconst view = new DataView(hashBytes.buffer, hashBytes.byteOffset, Math.min(8, hashBytes.length));\n\tconst fullHash = view.getBigUint64(0, false); // big-endian\n\n\t// Modulo by 2^bits\n\tconst modulus = BigInt(2) ** BigInt(bits);\n\tconst result = fullHash % modulus;\n\n\treturn Number(result);\n}\n\n/**\n * Sign data with a private key\n *\n * @param data - Data to sign (typically a hash)\n * @param privateKey - Private key (base64url string or Uint8Array)\n * @param curve - Elliptic curve (default: 'secp256k1')\n * @param inputEncoding - Encoding of data input (default: 'base64url')\n * @param keyEncoding - Encoding of private key (default: 'base64url')\n * @param outputEncoding - Encoding of signature output (default: 'base64url')\n * @returns Signature in specified encoding\n *\n * @example\n * ```typescript\n * // Sign a hash with secp256k1\n * const sig = sign(hashData, privateKey);\n *\n * // Sign with Ed25519\n * const sig2 = sign(hashData, privateKey, 'ed25519');\n * ```\n */\nexport function sign(\n\tdata: string | Uint8Array,\n\tprivateKey: string | Uint8Array,\n\tcurve: CurveType = 'secp256k1',\n\tinputEncoding: Encoding = 'base64url',\n\tkeyEncoding: Encoding = 'base64url',\n\toutputEncoding: Encoding = 'base64url'\n): string | Uint8Array {\n\tconst dataBytes = toBytes(data, inputEncoding);\n\tconst keyBytes = toBytes(privateKey, keyEncoding);\n\n\tlet sigBytes: Uint8Array;\n\n\tswitch (curve) {\n\t\tcase 'secp256k1':\n\t\t\tsigBytes = secp256k1.sign(dataBytes, keyBytes, { lowS: true });\n\t\t\tbreak;\n\t\tcase 'p256':\n\t\t\tsigBytes = p256.sign(dataBytes, keyBytes, { lowS: true });\n\t\t\tbreak;\n\t\tcase 'ed25519':\n\t\t\tsigBytes = ed25519.sign(dataBytes, keyBytes);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\n\t}\n\n\treturn fromBytes(sigBytes, outputEncoding);\n}\n\n/**\n * Verify a signature\n *\n * @param data - Data that was signed\n * @param signature - Signature to verify\n * @param publicKey - Public key\n * @param curve - Elliptic curve (default: 'secp256k1')\n * @param inputEncoding - Encoding of data input (default: 'base64url')\n * @param sigEncoding - Encoding of signature (default: 'base64url')\n * @param keyEncoding - Encoding of public key (default: 'base64url')\n * @returns true if signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * // Verify a signature\n * const isValid = verify(hashData, signature, publicKey);\n *\n * // Verify with Ed25519\n * const isValid2 = verify(hashData, signature, publicKey, 'ed25519');\n * ```\n */\nexport function verify(\n\tdata: string | Uint8Array,\n\tsignature: string | Uint8Array,\n\tpublicKey: string | Uint8Array,\n\tcurve: CurveType = 'secp256k1',\n\tinputEncoding: Encoding = 'base64url',\n\tsigEncoding: Encoding = 'base64url',\n\tkeyEncoding: Encoding = 'base64url'\n): boolean {\n\ttry {\n\t\tconst dataBytes = toBytes(data, inputEncoding);\n\t\tconst sigBytes = toBytes(signature, sigEncoding);\n\t\tconst keyBytes = toBytes(publicKey, keyEncoding);\n\n\t\tswitch (curve) {\n\t\t\tcase 'secp256k1': {\n\t\t\t\treturn secp256k1.verify(sigBytes, dataBytes, keyBytes);\n\t\t\t}\n\t\t\tcase 'p256': {\n\t\t\t\treturn p256.verify(sigBytes, dataBytes, keyBytes);\n\t\t\t}\n\t\t\tcase 'ed25519': {\n\t\t\t\treturn ed25519.verify(sigBytes, dataBytes, keyBytes);\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\n\t\t}\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Generate cryptographically secure random bytes\n *\n * @param bits - Number of bits to generate (default: 256)\n * @param encoding - Output encoding (default: 'base64url')\n * @returns Random bytes in the specified encoding\n */\nexport function randomBytes(bits: number = 256, encoding: Encoding = 'base64url'): string | Uint8Array {\n\tconst bytes = Math.ceil(bits / 8);\n\tconst randomBytesArray = nobleRandomBytes(bytes);\n\treturn fromBytes(randomBytesArray, encoding);\n}\n\n/**\n * Generate a random private key\n */\nexport function generatePrivateKey(curve: CurveType = 'secp256k1', encoding: Encoding = 'base64url'): string | Uint8Array {\n\tlet keyBytes: Uint8Array;\n\n\tswitch (curve) {\n\t\tcase 'secp256k1':\n\t\t\tkeyBytes = secp256k1.utils.randomSecretKey();\n\t\t\tbreak;\n\t\tcase 'p256':\n\t\t\tkeyBytes = p256.utils.randomSecretKey();\n\t\t\tbreak;\n\t\tcase 'ed25519':\n\t\t\tkeyBytes = ed25519.utils.randomSecretKey();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\n\t}\n\n\treturn fromBytes(keyBytes, encoding);\n}\n\n/**\n * Get public key from private key\n */\nexport function getPublicKey(\n\tprivateKey: string | Uint8Array,\n\tcurve: CurveType = 'secp256k1',\n\tkeyEncoding: Encoding = 'base64url',\n\toutputEncoding: Encoding = 'base64url'\n): string | Uint8Array {\n\tconst keyBytes = toBytes(privateKey, keyEncoding);\n\n\tlet pubBytes: Uint8Array;\n\n\tswitch (curve) {\n\t\tcase 'secp256k1':\n\t\t\tpubBytes = secp256k1.getPublicKey(keyBytes);\n\t\t\tbreak;\n\t\tcase 'p256':\n\t\t\tpubBytes = p256.getPublicKey(keyBytes);\n\t\t\tbreak;\n\t\tcase 'ed25519':\n\t\t\tpubBytes = ed25519.getPublicKey(keyBytes);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\n\t}\n\n\treturn fromBytes(pubBytes, outputEncoding);\n}\n\n","/**\n * Self-describing content identifiers (CIDv1) for Quereus.\n *\n * Where {@link ./crypto.ts | digest} emits a *bare* hash (raw digest bytes in\n * some text encoding), this module emits an interoperable, self-describing\n * CIDv1:\n *\n * ```\n * CIDv1 = multibase( version ‖ multicodec(content-type) ‖ multihash )\n * multihash = hashFnCode ‖ digestLength ‖ digestBytes\n * ```\n *\n * The value carries its own multibase, multicodec (content type), and multihash\n * (hash algorithm + length), so a consumer can decode it without out-of-band\n * knowledge, and an algorithm migration (e.g. sha2-256 → another hash) is\n * unambiguous because the hash code is recorded *in the value*.\n *\n * All framing/parsing comes from the audited `multiformats` library — there is\n * no bespoke byte-pushing here. The actual hashing reuses the same synchronous,\n * cross-platform `@noble/hashes` functions the rest of the plugin uses (via\n * {@link resolveHasher}), so the output is byte-identical to the CID an external\n * content-addressed store (IPFS/IPLD) computes for the same bytes:\n * `cid(utf8('hello world'))` === `bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e`.\n */\n\nimport { CID } from 'multiformats/cid';\nimport * as Digest from 'multiformats/hashes/digest';\nimport { base32 } from 'multiformats/bases/base32';\nimport { base58btc } from 'multiformats/bases/base58';\nimport { base64url } from 'multiformats/bases/base64';\nimport { base16 } from 'multiformats/bases/base16';\nimport type { MultibaseEncoder, MultibaseDecoder } from 'multiformats/bases/interface';\nimport { resolveHasher, type HashAlgorithm } from './crypto.js';\n\n/** Content-type multicodec selectable for the CID. Extensible. */\nexport type Multicodec = 'raw' | 'dag-cbor';\n/** Hash-algorithm multihash code selectable for the CID. */\nexport type MultihashCode = 'sha2-256' | 'sha2-512' | 'blake3';\n/** Multibase the CID string is rendered in. */\nexport type Multibase = 'base32' | 'base58btc' | 'base64url' | 'base16';\n\n/** Parsed parts of a CIDv1 (or CIDv0), as returned by {@link cidDecode}. */\nexport interface CidParts {\n\t/** CID version (1 for the values this module produces; 0 for legacy CIDv0). */\n\treadonly version: number;\n\t/** Content-type codec name when recognized, else the raw multicodec number. */\n\treadonly codec: Multicodec | number;\n\t/** Hash-algorithm code name when recognized, else the raw multihash number. */\n\treadonly hashCode: MultihashCode | number;\n\t/** Raw digest bytes (without the multihash code/length prefix). */\n\treadonly digest: Uint8Array;\n}\n\n// --- Multiformats code tables (see multiformats/multicodec table.csv) --- //\n\n/** Content-type name → multicodec code. */\nconst MULTICODEC_CODES: Record<Multicodec, number> = {\n\t'raw': 0x55,\n\t'dag-cbor': 0x71,\n};\n\n/** Hash name → multihash code. */\nconst MULTIHASH_CODES: Record<MultihashCode, number> = {\n\t'sha2-256': 0x12,\n\t'sha2-512': 0x13,\n\t'blake3': 0x1e,\n};\n\n/** Multihash code → the synchronous `@noble/hashes` algorithm that produces it. */\nconst MULTIHASH_TO_ALGORITHM: Record<MultihashCode, HashAlgorithm> = {\n\t'sha2-256': 'sha256',\n\t'sha2-512': 'sha512',\n\t'blake3': 'blake3',\n};\n\n/**\n * Multihash code → exact digest length in bytes. A CID is replicable only if its\n * digest length is fixed, so blake3 (which is variable-length in general) is\n * pinned to 32 bytes here, matching the plugin's blake3 output and sha2-256.\n */\nconst MULTIHASH_DIGEST_LENGTHS: Record<MultihashCode, number> = {\n\t'sha2-256': 32,\n\t'sha2-512': 64,\n\t'blake3': 32,\n};\n\n/** Reverse lookups for {@link cidDecode}: code number → friendly name. */\nconst MULTICODEC_NAMES: ReadonlyMap<number, Multicodec> = new Map(\n\t(Object.entries(MULTICODEC_CODES) as [Multicodec, number][]).map(([name, code]) => [code, name])\n);\nconst MULTIHASH_NAMES: ReadonlyMap<number, MultihashCode> = new Map(\n\t(Object.entries(MULTIHASH_CODES) as [MultihashCode, number][]).map(([name, code]) => [code, name])\n);\n\n/** Multibase name → its multiformats encoder. */\nconst MULTIBASE_ENCODERS: Record<Multibase, MultibaseEncoder<string>> = {\n\t'base32': base32,\n\t'base58btc': base58btc,\n\t'base64url': base64url,\n\t'base16': base16,\n};\n\n/**\n * Combined decoder that dispatches on the multibase prefix character, so\n * {@link cidDecode} accepts a CID in any of the supported bases without the\n * caller having to declare which.\n */\nconst MULTIBASE_DECODER: MultibaseDecoder<string> = base32.decoder\n\t.or(base58btc.decoder)\n\t.or(base64url.decoder)\n\t.or(base16.decoder);\n\nfunction resolveCodecCode(codec: Multicodec): number {\n\tconst code = MULTICODEC_CODES[codec];\n\tif (code === undefined) {\n\t\tthrow new Error(`cid: unsupported multicodec '${codec}' (expected one of ${Object.keys(MULTICODEC_CODES).join(', ')})`);\n\t}\n\treturn code;\n}\n\nfunction resolveBaseEncoder(base: Multibase): MultibaseEncoder<string> {\n\tconst encoder = MULTIBASE_ENCODERS[base];\n\tif (!encoder) {\n\t\tthrow new Error(`cid: unsupported multibase '${base}' (expected one of ${Object.keys(MULTIBASE_ENCODERS).join(', ')})`);\n\t}\n\treturn encoder;\n}\n\n/**\n * Frame an **already-computed** digest as a CIDv1 string. The caller asserts\n * which `hash` produced the digest; the digest length is validated against that\n * hash so a mismatched assertion is rejected rather than silently mis-framed.\n *\n * Use this to turn an existing field-tuple digest into a CID without re-hashing,\n * e.g. `cidV1(digest(fields, 'sha256', 'bytes'), 'sha2-256')`.\n *\n * @param digest - Raw digest bytes (no multihash prefix).\n * @param hash - The multihash code asserting which algorithm produced `digest`.\n * @param codec - Content-type multicodec (default `'raw'`).\n * @param base - Multibase to render in (default `'base32'`, the IPFS canonical).\n */\nexport function cidV1(\n\tdigest: Uint8Array,\n\thash: MultihashCode,\n\tcodec: Multicodec = 'raw',\n\tbase: Multibase = 'base32'\n): string {\n\tconst hashCode = MULTIHASH_CODES[hash];\n\tif (hashCode === undefined) {\n\t\tthrow new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);\n\t}\n\tconst expectedLength = MULTIHASH_DIGEST_LENGTHS[hash];\n\tif (digest.length !== expectedLength) {\n\t\tthrow new Error(`cid: digest length ${digest.length} does not match asserted hash '${hash}' (expected ${expectedLength} bytes)`);\n\t}\n\tconst codecCode = resolveCodecCode(codec);\n\tconst encoder = resolveBaseEncoder(base);\n\tconst multihash = Digest.create(hashCode, digest);\n\treturn CID.createV1(codecCode, multihash).toString(encoder);\n}\n\n/**\n * Hash `data`, wrap the digest as a multihash, frame it as a CIDv1, and encode\n * in `base`. The result is the same interoperable address an IPFS/IPLD store\n * computes for the same bytes (for the matching codec/hash).\n *\n * @param data - The content bytes to address.\n * @param codec - Content-type multicodec (default `'raw'`).\n * @param hash - Hash algorithm (default `'sha2-256'`).\n * @param base - Multibase to render in (default `'base32'`, the IPFS canonical).\n */\nexport function cid(\n\tdata: Uint8Array,\n\tcodec: Multicodec = 'raw',\n\thash: MultihashCode = 'sha2-256',\n\tbase: Multibase = 'base32'\n): string {\n\tconst algorithm = MULTIHASH_TO_ALGORITHM[hash];\n\tif (!algorithm) {\n\t\tthrow new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);\n\t}\n\tconst digest = resolveHasher(algorithm)(data);\n\treturn cidV1(digest, hash, codec, base);\n}\n\n/**\n * Parse a CID string back into its parts, for schema validation and migration.\n * Recognized codec/hash codes are returned as friendly names; unrecognized ones\n * as their raw numbers. Throws cleanly on malformed input (delegated to\n * `multiformats`), never silently mis-framing.\n */\nexport function cidDecode(value: string): CidParts {\n\tconst parsed = CID.parse(value, MULTIBASE_DECODER);\n\treturn {\n\t\tversion: parsed.version,\n\t\tcodec: MULTICODEC_NAMES.get(parsed.code) ?? parsed.code,\n\t\thashCode: MULTIHASH_NAMES.get(parsed.multihash.code) ?? parsed.multihash.code,\n\t\tdigest: parsed.multihash.digest,\n\t};\n}\n","/**\n * Salted-leaf SET COMMITMENT for per-attribute selective disclosure.\n *\n * An authority commits to a whole set of attributes as a single root value (which\n * it signs / persists), then later reveals only a chosen *subset* to a recipient —\n * with a proof that the revealed values are genuinely the committed ones — without\n * leaking the values of the withheld attributes. A flat `digest(whole set)` cannot\n * do this (verifying one field needs the whole pre-image, so it is all-or-nothing);\n * this construction supports *partial opening*.\n *\n * ## Construction (flat salted-leaf set commitment, NOT a Merkle tree)\n *\n * Each disclosable attribute is a salted leaf, and the commitment (root) is the\n * digest of all leaf digests in canonical order:\n *\n * ```\n * leafDigest = digest([SD_LEAF_DOMAIN_V1, name, value, salt]) // raw digest bytes\n * root = digest([SD_SET_DOMAIN_V1, sortedLeaf_0, sortedLeaf_1, ...])\n * ```\n *\n * Both layers compose on the existing canonical {@link encodeFields} framing\n * (injective, type-tagged, length-prefixed, replicable) — the same layering the CID\n * work uses — so a *generic* salted-set primitive is simultaneously reusable and\n * fully DB-enforceable. This is the same shape the IETF SD-JWT standard settled on\n * (flat salted hashes, not a tree); we are NOT wire-compatible with SD-JWT (we reuse\n * Optimystic's own `encodeFields` framing for cross-peer replicability) — SD-JWT is\n * cited only as conceptual precedent that the smaller construction is the right one.\n *\n * Voter selective-disclosure field sets are small (a handful to a few dozen fields),\n * so a tree's only advantage — O(log n) proof size — is marginal, while a tree drags\n * in real footguns we would have to hand-roll and pin (arity, odd-node handling /\n * the CVE-2012-2459 duplicate-leaf forgery class, leaf-vs-internal domain separation,\n * and a separate audit-path proof format). A flat construction avoids all of them.\n *\n * ## Why these specific choices\n *\n * - **`name` is hashed into the leaf** so a disclosed `(value, salt)` proof cannot be\n * replayed against a different attribute slot (e.g. presenting an `over18=true`\n * proof as the `citizen` field). The binding is free given `encodeFields` framing.\n * - **`salt` is per-leaf and mandatory** — low-entropy attributes (DOB, booleans, ZIP)\n * are brute-forceable from a bare hash, and independent salts also defeat cross-\n * registrant equality correlation. Salts come from `random_bytes` (≥128 bits).\n * - **Canonical order is by raw leaf-digest bytes (lexicographic), and this is FORCED,\n * not a preference.** In a disclosure the verifier learns the *names* of only the\n * disclosed leaves; the withheld leaves arrive as opaque digests with no name. So the\n * verifier can re-derive the root only if the ordering key is something it holds for\n * *every* leaf — the leaf digest itself. Sorting by name would be unverifiable for\n * hidden leaves. Do NOT \"tidy\" this into a name sort.\n * - Sort is over **raw digest bytes**, never over encoded strings — an encoding-\n * dependent ordering would break cross-peer agreement. Output encoding applies only\n * to the final root.\n *\n * Because leaf and root reuse `encodeFields`, a future `DIGEST_FORMAT_V1` bump changes\n * `setCommit` output too; this coupling is intentional (one canonical framing).\n */\n\nimport { fromString as uint8ArrayFromString, toString as uint8ArrayToString } from 'uint8arrays';\nimport {\n\tencodeFields,\n\tresolveHasher,\n\tresolveOutputEncoder,\n\ttype DigestField,\n\ttype DigestHasher,\n\ttype OutputEncoder,\n} from './crypto.js';\n\n/**\n * Fixed domain-separation constants — the leading string field of each layer's\n * {@link encodeFields} tuple. They are pinned EXACTLY like `DIGEST_FORMAT_V1`:\n *\n * - the two strings MUST be distinct, so a leaf hash can never equal a root hash;\n * - neither may change without a deliberate, breaking version bump — which would\n * change every committed root and every signature taken over it.\n *\n * Do not \"tidy\" or shorten these.\n */\nconst SD_LEAF_DOMAIN_V1 = 'optimystic/sd-leaf/v1';\nconst SD_SET_DOMAIN_V1 = 'optimystic/sd-set/v1';\n\n/** Hidden leaf digests travel as base64url text — the plugin's canonical text encoding. */\nconst HIDDEN_ENCODING = 'base64url';\n\n/** One disclosable attribute. `value` spans the SQL value space ({@link DigestField}). */\nexport interface SaltedLeaf {\n\treadonly name: string;\n\treadonly value: DigestField;\n\t/** base64url text (e.g. from `random_bytes`) or raw bytes. Mandatory, non-empty. */\n\treadonly salt: string | Uint8Array;\n}\n\n/** A disclosure payload sent to a recipient. */\nexport interface SetDisclosure {\n\t/** The opened `(name, value, salt)` triples. */\n\treadonly disclosed: readonly SaltedLeaf[];\n\t/** Opaque leaf digests (base64url) of the withheld leaves — no name, no value, no salt. */\n\treadonly hidden: readonly string[];\n}\n\n// --- internal helpers --- //\n\n/** Lexicographic compare of two byte arrays (the canonical leaf ordering key). */\nfunction compareBytes(a: Uint8Array, b: Uint8Array): number {\n\tconst len = Math.min(a.length, b.length);\n\tfor (let i = 0; i < len; i++) {\n\t\tconst d = a[i]! - b[i]!;\n\t\tif (d !== 0) return d;\n\t}\n\treturn a.length - b.length;\n}\n\n/** Constant-shape byte equality (length first, then content). */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n\tif (a.length !== b.length) return false;\n\tfor (let i = 0; i < a.length; i++) {\n\t\tif (a[i] !== b[i]) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Normalize a leaf's salt to raw bytes — a base64url string (the form `random_bytes`\n * returns) decodes to bytes, raw bytes pass through — so the two representations of the\n * same salt commit identically. THROWS on a missing or empty salt (unsalted leaves are\n * brute-forceable, an invalid state we make impossible).\n */\nfunction requireSaltBytes(leaf: SaltedLeaf): Uint8Array {\n\tconst { salt } = leaf;\n\tif (salt == null) {\n\t\tthrow new Error(`set commitment: leaf '${leaf.name}' is missing a salt (an unsalted leaf is brute-forceable)`);\n\t}\n\tconst bytes = salt instanceof Uint8Array ? salt : uint8ArrayFromString(salt, HIDDEN_ENCODING);\n\tif (bytes.length === 0) {\n\t\tthrow new Error(`set commitment: leaf '${leaf.name}' has an empty salt (an unsalted leaf is brute-forceable)`);\n\t}\n\treturn bytes;\n}\n\n/**\n * THROW on a duplicate `name`. Two leaves with the same name would let a holder\n * selectively present whichever value suits them; the authority side (which holds all\n * names) is the only place uniqueness can be enforced — the verifier never sees the\n * hidden names — so the primitive must fail-fast.\n */\nfunction assertUniqueNames(leaves: readonly SaltedLeaf[]): void {\n\tconst seen = new Set<string>();\n\tfor (const leaf of leaves) {\n\t\tif (seen.has(leaf.name)) {\n\t\t\tthrow new Error(`set commitment: duplicate leaf name '${leaf.name}'`);\n\t\t}\n\t\tseen.add(leaf.name);\n\t}\n}\n\n// --- public API --- //\n\n/**\n * Raw leaf digest bytes for one salted leaf: `digest([SD_LEAF_DOMAIN_V1, name,\n * value, salt])`. Domain-separated (can never equal a root) and name-bound (a\n * `(value, salt)` proof cannot be replayed under another attribute name). THROWS on\n * a missing/empty salt.\n */\nexport function leafDigest(leaf: SaltedLeaf, hasher: DigestHasher): Uint8Array {\n\tconst saltBytes = requireSaltBytes(leaf);\n\treturn hasher(encodeFields([SD_LEAF_DOMAIN_V1, leaf.name, leaf.value, saltBytes]));\n}\n\n/**\n * Commit to a SET of salted leaves → a single root (the signed/persisted value).\n * Sorts leaves by raw leaf-digest bytes, then digests them under `SD_SET_DOMAIN_V1`.\n * Like `digest`, this emits a BARE digest — apply `cid()` on top for the self-\n * describing column representation (`cid(set_commit(...))`).\n *\n * The empty set is well-defined (the digest of `[SD_SET_DOMAIN_V1]`), not an error.\n * THROWS on a duplicate `name` or a missing/empty `salt` (invalid states made\n * impossible). Resolve `hasher`/`encode` once and reuse — no per-call branching.\n */\nexport function setCommit(\n\tleaves: readonly SaltedLeaf[],\n\thasher: DigestHasher = resolveHasher('sha256'),\n\tencode: OutputEncoder = resolveOutputEncoder('base64url'),\n): string | Uint8Array {\n\tassertUniqueNames(leaves);\n\tconst leafDigests = leaves.map((leaf) => leafDigest(leaf, hasher));\n\tleafDigests.sort(compareBytes);\n\treturn encode(hasher(encodeFields([SD_SET_DOMAIN_V1, ...leafDigests])));\n}\n\n/**\n * Split a leaf set into the revealed `(name, value, salt)` triples plus the opaque\n * leaf digests (base64url) of the rest. Withheld `value`/`salt` never appear in the\n * output. Names in `revealNames` that match no leaf are simply not disclosed.\n * THROWS on a duplicate `name` or a missing/empty salt of a withheld leaf.\n */\nexport function setDisclose(\n\tleaves: readonly SaltedLeaf[],\n\trevealNames: readonly string[],\n\thasher: DigestHasher = resolveHasher('sha256'),\n): SetDisclosure {\n\tassertUniqueNames(leaves);\n\tconst reveal = new Set(revealNames);\n\tconst disclosed: SaltedLeaf[] = [];\n\tconst hidden: string[] = [];\n\tfor (const leaf of leaves) {\n\t\tif (reveal.has(leaf.name)) {\n\t\t\tdisclosed.push(leaf);\n\t\t} else {\n\t\t\thidden.push(uint8ArrayToString(leafDigest(leaf, hasher), HIDDEN_ENCODING));\n\t\t}\n\t}\n\treturn { disclosed, hidden };\n}\n\n/**\n * Verify a disclosure against a signed root. Recomputes the disclosed leaves'\n * digests, unions them with the supplied hidden digests, sorts by bytes, recomputes\n * the root, and compares to `root`. This reconstructs the ENTIRE root, so it proves\n * the disclosed leaves belong to *exactly* this committed set — the holder cannot\n * add, drop, or swap a leaf (the leaf count is bound too).\n *\n * `encode` is how the signed `root` is rendered (so the recomputed root is encoded\n * the same way before comparison); for a `Uint8Array` root the raw bytes are compared\n * directly. Returns `false` on mismatch or malformed input — mirroring `verify`'s\n * forgiving contract rather than throwing.\n */\nexport function setVerify(\n\troot: string | Uint8Array,\n\tdisclosure: SetDisclosure,\n\thasher: DigestHasher = resolveHasher('sha256'),\n\tencode: OutputEncoder = resolveOutputEncoder('base64url'),\n): boolean {\n\ttry {\n\t\tconst { disclosed, hidden } = disclosure;\n\t\tconst digests: Uint8Array[] = [];\n\t\tfor (const leaf of disclosed) {\n\t\t\tdigests.push(leafDigest(leaf, hasher));\n\t\t}\n\t\tfor (const h of hidden) {\n\t\t\tdigests.push(uint8ArrayFromString(h, HIDDEN_ENCODING));\n\t\t}\n\t\tdigests.sort(compareBytes);\n\t\tconst recomputed = hasher(encodeFields([SD_SET_DOMAIN_V1, ...digests]));\n\t\tif (root instanceof Uint8Array) {\n\t\t\treturn bytesEqual(recomputed, root);\n\t\t}\n\t\tconst encoded = encode(recomputed);\n\t\treturn typeof encoded === 'string' && encoded === root;\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/crypto.ts","../src/cid.ts","../src/sd.ts"],"names":["uint8ArrayFromString","uint8ArrayToString","nobleRandomBytes","digest"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Cryptographic Functions for Quereus\r\n *\r\n * Idiomatic ES module exports with base64url as default encoding.\r\n * All functions accept and return base64url strings by default for SQL compatibility.\r\n */\r\n\r\nimport { sha256, sha512 } from '@noble/hashes/sha2.js';\r\nimport { blake3 } from '@noble/hashes/blake3.js';\r\nimport { randomBytes as nobleRandomBytes, utf8ToBytes, concatBytes } from '@noble/hashes/utils.js';\r\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\r\nimport { p256 } from '@noble/curves/nist.js';\r\nimport { ed25519 } from '@noble/curves/ed25519.js';\r\nimport { hexToBytes, bytesToHex } from '@noble/curves/utils.js';\r\nimport { toString as uint8ArrayToString, fromString as uint8ArrayFromString } from 'uint8arrays';\r\n\r\n// Type definitions\r\nexport type HashAlgorithm = 'sha256' | 'sha512' | 'blake3';\r\nexport type CurveType = 'secp256k1' | 'p256' | 'ed25519';\r\nexport type Encoding = 'base64url' | 'base64' | 'hex' | 'utf8' | 'bytes';\r\n\r\n/** Encodings valid for hash *output* (no 'utf8' — a digest is not UTF-8 text). */\r\nexport type OutputEncoding = 'base64url' | 'base64' | 'hex' | 'bytes';\r\n\r\n/** A single value in a multi-field digest. Mirrors the SQL value space. */\r\nexport type DigestField =\r\n\t| string\r\n\t| number\r\n\t| bigint\r\n\t| boolean\r\n\t| Uint8Array\r\n\t| null\r\n\t| undefined\r\n\t| { readonly [key: string]: unknown }\r\n\t| readonly unknown[];\r\n\r\n/** A resolved hash function: raw bytes in, digest bytes out. */\r\nexport type DigestHasher = (input: Uint8Array) => Uint8Array;\r\n\r\n/** A resolved output encoder: digest bytes in, encoded form out. */\r\nexport type OutputEncoder = (bytes: Uint8Array) => string | Uint8Array;\r\n\r\n/**\r\n * Convert input to Uint8Array, handling various encodings\r\n */\r\nfunction toBytes(input: string | Uint8Array | null | undefined, encoding: Encoding = 'base64url'): Uint8Array {\r\n\tif (input === null || input === undefined) {\r\n\t\treturn new Uint8Array(0);\r\n\t}\r\n\r\n\tif (input instanceof Uint8Array) {\r\n\t\treturn input;\r\n\t}\r\n\r\n\tif (typeof input === 'string') {\r\n\t\tswitch (encoding) {\r\n\t\t\tcase 'base64url':\r\n\t\t\t\treturn uint8ArrayFromString(input, 'base64url');\r\n\t\t\tcase 'base64':\r\n\t\t\t\treturn uint8ArrayFromString(input, 'base64');\r\n\t\t\tcase 'hex':\r\n\t\t\t\treturn hexToBytes(input);\r\n\t\t\tcase 'utf8':\r\n\t\t\t\treturn utf8ToBytes(input);\r\n\t\t\tdefault:\r\n\t\t\t\treturn uint8ArrayFromString(input, 'base64url');\r\n\t\t}\r\n\t}\r\n\r\n\tthrow new Error('Invalid input type');\r\n}\r\n\r\n/**\r\n * Convert Uint8Array to string in specified encoding\r\n */\r\nfunction fromBytes(bytes: Uint8Array, encoding: Encoding = 'base64url'): string | Uint8Array {\r\n\tswitch (encoding) {\r\n\t\tcase 'base64url':\r\n\t\t\treturn uint8ArrayToString(bytes, 'base64url');\r\n\t\tcase 'base64':\r\n\t\t\treturn uint8ArrayToString(bytes, 'base64');\r\n\t\tcase 'hex':\r\n\t\t\treturn bytesToHex(bytes);\r\n\t\tcase 'utf8':\r\n\t\t\treturn uint8ArrayToString(bytes, 'utf8');\r\n\t\tcase 'bytes':\r\n\t\t\treturn bytes;\r\n\t\tdefault:\r\n\t\t\treturn uint8ArrayToString(bytes, 'base64url');\r\n\t}\r\n}\r\n\r\n// --- Algorithm / encoding resolution (done once, no per-call switching) --- //\r\n\r\n/** Hash algorithm → noble hasher. Keyed lookup so the digest hot path never branches. */\r\nconst HASHERS: Record<HashAlgorithm, DigestHasher> = {\r\n\tsha256,\r\n\tsha512,\r\n\tblake3,\r\n};\r\n\r\n/** Output encoding → encoder closure. */\r\nconst OUTPUT_ENCODERS: Record<OutputEncoding, OutputEncoder> = {\r\n\tbase64url: (bytes) => uint8ArrayToString(bytes, 'base64url'),\r\n\tbase64: (bytes) => uint8ArrayToString(bytes, 'base64'),\r\n\thex: (bytes) => bytesToHex(bytes),\r\n\tbytes: (bytes) => bytes,\r\n};\r\n\r\n/**\r\n * Resolve a hash algorithm name to its hasher. Throws on unknown algorithm.\r\n * Call once (e.g. at plugin registration) and capture the result so the digest\r\n * hot path performs no per-call algorithm branching.\r\n */\r\nexport function resolveHasher(algorithm: HashAlgorithm): DigestHasher {\r\n\tconst hasher = HASHERS[algorithm];\r\n\tif (!hasher) {\r\n\t\tthrow new Error(`Unsupported hash algorithm: ${algorithm}`);\r\n\t}\r\n\treturn hasher;\r\n}\r\n\r\n/**\r\n * Resolve an output encoding name to its encoder. Throws on unknown encoding.\r\n */\r\nexport function resolveOutputEncoder(encoding: OutputEncoding): OutputEncoder {\r\n\tconst encoder = OUTPUT_ENCODERS[encoding];\r\n\tif (!encoder) {\r\n\t\tthrow new Error(`Unsupported output encoding: ${encoding}`);\r\n\t}\r\n\treturn encoder;\r\n}\r\n\r\n// --- Canonical, injective multi-field encoding --- //\r\n\r\n/**\r\n * Format version for {@link encodeFields}. Prepended to every encoding so the\r\n * framing can evolve, and so a framed digest is domain-separated from a bare\r\n * hash of the same bytes. Bump only with a deliberate, breaking format change.\r\n */\r\nconst DIGEST_FORMAT_V1 = 0x01;\r\n\r\n// Per-field type tags. Distinct tags keep distinct SQL types from colliding\r\n// (e.g. INTEGER 123 vs TEXT '123' vs BOOL true).\r\n//\r\n// Note on INT vs REAL: the tag is derived from the JS value, not from SQL\r\n// affinity (a scalar function does not receive affinity). An integer-VALUED\r\n// number — including a REAL like 2.0, which reaches JS as the number 2 — is\r\n// encoded as INTEGER. So INTEGER 2 and REAL 2.0 produce the same digest. This is\r\n// replicable (every peer sees the same JS value) but not int/real-distinguishing.\r\nconst TAG_NULL = 0x00; // bare tag, no length/payload\r\nconst TAG_INT = 0x01; // payload: canonical decimal string (number-integer & bigint unified via BigInt)\r\nconst TAG_REAL = 0x02; // payload: ECMAScript Number::toString (non-integer numbers only)\r\nconst TAG_TEXT = 0x03; // payload: UTF-8 bytes\r\nconst TAG_BOOL = 0x04; // payload: single 0x00/0x01 byte\r\nconst TAG_BLOB = 0x05; // payload: raw bytes\r\nconst TAG_JSON = 0x06; // payload: UTF-8 of key-sorted canonical JSON\r\n\r\n/** Append an unsigned LEB128 varint (safe for lengths up to MAX_SAFE_INTEGER). */\r\nfunction writeVarint(out: number[], value: number): void {\r\n\tif (!Number.isInteger(value) || value < 0) {\r\n\t\tthrow new Error(`varint expects a non-negative integer, got ${value}`);\r\n\t}\r\n\tlet v = value;\r\n\twhile (v >= 0x80) {\r\n\t\tout.push((v & 0x7f) | 0x80);\r\n\t\tv = Math.floor(v / 128);\r\n\t}\r\n\tout.push(v);\r\n}\r\n\r\n/** tag ‖ varint(len) ‖ payload */\r\nfunction framed(tag: number, payload: Uint8Array): Uint8Array {\r\n\tconst header: number[] = [tag];\r\n\twriteVarint(header, payload.length);\r\n\treturn concatBytes(Uint8Array.from(header), payload);\r\n}\r\n\r\n/**\r\n * Strict, deterministic JSON canonicalization for a native object/array field:\r\n * object keys recursively sorted, no incidental whitespace. Unlike `JSON.stringify`,\r\n * it THROWS rather than silently collapsing non-JSON inputs (`undefined`, non-finite\r\n * numbers, `bigint`, non-plain objects like `Date`/`Map`) — silent collapse would\r\n * break injectivity (`{a:undefined}` vs `{}`, `NaN` vs `null`, `new Date(0)` vs `{}`).\r\n */\r\nfunction canonicalJson(value: unknown): string {\r\n\tif (value === null) return 'null';\r\n\tconst t = typeof value;\r\n\tif (t === 'string') return JSON.stringify(value);\r\n\tif (t === 'boolean') return value ? 'true' : 'false';\r\n\tif (t === 'number') {\r\n\t\tif (!Number.isFinite(value)) {\r\n\t\t\tthrow new Error('digest: cannot encode a non-finite number inside a JSON field');\r\n\t\t}\r\n\t\treturn JSON.stringify(value) as string; // deterministic Number::toString\r\n\t}\r\n\tif (t === 'bigint') {\r\n\t\tthrow new Error('digest: bigint is not representable inside a JSON field');\r\n\t}\r\n\tif (Array.isArray(value)) {\r\n\t\treturn `[${value.map((el) => {\r\n\t\t\tif (el === undefined) {\r\n\t\t\t\tthrow new Error('digest: undefined / sparse element inside a JSON field');\r\n\t\t\t}\r\n\t\t\treturn canonicalJson(el);\r\n\t\t}).join(',')}]`;\r\n\t}\r\n\tif (t === 'object') {\r\n\t\tconst proto = Object.getPrototypeOf(value);\r\n\t\tif (proto !== Object.prototype && proto !== null) {\r\n\t\t\tthrow new Error('digest: only plain objects are allowed inside a JSON field');\r\n\t\t}\r\n\t\tconst obj = value as Record<string, unknown>;\r\n\t\tconst keys = Object.keys(obj).sort();\r\n\t\treturn `{${keys.map((k) => {\r\n\t\t\tif (obj[k] === undefined) {\r\n\t\t\t\tthrow new Error(`digest: undefined value for JSON key '${k}'`);\r\n\t\t\t}\r\n\t\t\treturn `${JSON.stringify(k)}:${canonicalJson(obj[k])}`;\r\n\t\t}).join(',')}}`;\r\n\t}\r\n\tthrow new Error(`digest: unsupported value of type '${t}' inside a JSON field`);\r\n}\r\n\r\n/** Encode one field as tag (‖ length ‖ payload). NULL/undefined is a bare tag. */\r\nfunction encodeField(field: DigestField): Uint8Array {\r\n\tif (field === null || field === undefined) {\r\n\t\treturn Uint8Array.of(TAG_NULL);\r\n\t}\r\n\tswitch (typeof field) {\r\n\t\tcase 'boolean':\r\n\t\t\treturn Uint8Array.of(TAG_BOOL, field ? 1 : 0);\r\n\t\tcase 'bigint':\r\n\t\t\treturn framed(TAG_INT, utf8ToBytes(field.toString()));\r\n\t\tcase 'number':\r\n\t\t\tif (!Number.isFinite(field)) {\r\n\t\t\t\tthrow new Error('digest: cannot encode a non-finite number');\r\n\t\t\t}\r\n\t\t\t// Integer-valued numbers go through BigInt so they encode identically to\r\n\t\t\t// the equal-valued bigint (e.g. 1e21 → full digits, not \"1e+21\").\r\n\t\t\treturn Number.isInteger(field)\r\n\t\t\t\t? framed(TAG_INT, utf8ToBytes(BigInt(field).toString()))\r\n\t\t\t\t: framed(TAG_REAL, utf8ToBytes(field.toString()));\r\n\t\tcase 'string':\r\n\t\t\treturn framed(TAG_TEXT, utf8ToBytes(field));\r\n\t\tcase 'object':\r\n\t\t\tif (field instanceof Uint8Array) {\r\n\t\t\t\treturn framed(TAG_BLOB, field);\r\n\t\t\t}\r\n\t\t\treturn framed(TAG_JSON, utf8ToBytes(canonicalJson(field)));\r\n\t\tdefault:\r\n\t\t\tthrow new Error(`digest: unsupported field type '${typeof field}'`);\r\n\t}\r\n}\r\n\r\n/**\r\n * Canonically encode an ordered tuple of fields into bytes such that distinct\r\n * tuples never collide (injective framing).\r\n *\r\n * Layout: `version ‖ field*` where each field is `tag ‖ varint(len) ‖ payload`\r\n * (NULL is a bare tag). Properties:\r\n * - order-preserving and arity-safe (self-delimiting fields → uniquely decodable),\r\n * - NULL distinguishable from empty string,\r\n * - type distinguishable (INTEGER 123 ≠ TEXT '123' ≠ BOOL true ≠ BLOB),\r\n * - delimiter-safe (a separator inside a string is just payload under its length).\r\n *\r\n * Replicability notes:\r\n * - Integer `number` and `bigint` of equal value encode identically (both via\r\n * `BigInt(...).toString()`); a non-integer REAL uses ECMAScript `Number::toString`\r\n * (deterministic across JS engines, but not guaranteed across other languages).\r\n * - INT vs REAL is derived from the JS value, not SQL affinity: an integer-valued\r\n * REAL (e.g. 2.0 → number 2) encodes as INTEGER, so INTEGER 2 and REAL 2.0 collide.\r\n * - A native JSON object/array field must contain only valid JSON (no `undefined`,\r\n * non-finite numbers, `bigint`, or non-plain objects) — otherwise it throws.\r\n */\r\nexport function encodeFields(fields: readonly DigestField[]): Uint8Array {\r\n\tconst chunks: Uint8Array[] = [Uint8Array.of(DIGEST_FORMAT_V1)];\r\n\tfor (const field of fields) {\r\n\t\tchunks.push(encodeField(field));\r\n\t}\r\n\treturn concatBytes(...chunks);\r\n}\r\n\r\n/**\r\n * Low-level multi-field digest: canonically encode the fields, then hash and\r\n * encode with the supplied (pre-resolved) hasher/encoder. No per-call branching\r\n * on algorithm or encoding — resolve once via {@link resolveHasher} /\r\n * {@link resolveOutputEncoder} and reuse.\r\n */\r\nexport function digestFields(\r\n\tfields: readonly DigestField[],\r\n\thasher: DigestHasher,\r\n\tencode: OutputEncoder\r\n): string | Uint8Array {\r\n\treturn encode(hasher(encodeFields(fields)));\r\n}\r\n\r\n/**\r\n * Compute an injective digest over an ordered tuple of fields.\r\n *\r\n * @param fields - Ordered tuple of values to hash (any SQL value type)\r\n * @param algorithm - Hash algorithm (default: 'sha256')\r\n * @param encoding - Output encoding (default: 'base64url')\r\n * @returns Hash digest in the specified encoding\r\n *\r\n * @example\r\n * ```typescript\r\n * // Hash a tuple of fields — distinct tuples never collide\r\n * const h = digest(['alice', 42, null, true]);\r\n *\r\n * // Pick algorithm / output encoding\r\n * const h512 = digest(['a', 'b'], 'sha512', 'hex');\r\n * ```\r\n *\r\n * Note: this is a *framed* digest, not a bare hash of raw bytes —\r\n * `digest(['hello'])` is not `sha256(\"hello\")`. Use `hashMod` for sharding a\r\n * single value.\r\n */\r\nexport function digest(\r\n\tfields: readonly DigestField[],\r\n\talgorithm: HashAlgorithm = 'sha256',\r\n\tencoding: OutputEncoding = 'base64url'\r\n): string | Uint8Array {\r\n\tif (!Array.isArray(fields)) {\r\n\t\tthrow new Error(\r\n\t\t\t`digest(fields, algorithm?, encoding?): 'fields' must be an array of values. ` +\r\n\t\t\t`The digest API changed in v0.14: it is now variadic/injective over fields, ` +\r\n\t\t\t`the per-call inputEncoding was removed, and algorithm + output encoding are bound at plugin load time. ` +\r\n\t\t\t`Migrate digest(value, algo, inputEncoding, outputEncoding) → digest([value], algo, outputEncoding) — ` +\r\n\t\t\t`note the result is now a *framed* digest, not a bare hash of the bytes.`\r\n\t\t);\r\n\t}\r\n\treturn digestFields(fields, resolveHasher(algorithm), resolveOutputEncoder(encoding));\r\n}\r\n\r\n/**\r\n * Hash data and return modulo of specified bit length\r\n * Useful for generating fixed-size hash values (e.g., 16-bit, 32-bit)\r\n *\r\n * @param data - Data to hash\r\n * @param bits - Number of bits for the result (e.g., 16 for 16-bit hash)\r\n * @param algorithm - Hash algorithm (default: 'sha256')\r\n * @param inputEncoding - Encoding of input string (default: 'base64url')\r\n * @returns Integer hash value modulo 2^bits\r\n *\r\n * @example\r\n * ```typescript\r\n * // Get 16-bit hash (0-65535)\r\n * const hash16 = hashMod('hello', 16, 'sha256', 'utf8');\r\n *\r\n * // Get 32-bit hash\r\n * const hash32 = hashMod('world', 32, 'sha256', 'utf8');\r\n * ```\r\n */\r\nexport function hashMod(\r\n\tdata: string | Uint8Array,\r\n\tbits: number,\r\n\talgorithm: HashAlgorithm = 'sha256',\r\n\tinputEncoding: Encoding = 'base64url'\r\n): number {\r\n\tif (bits <= 0 || bits > 53) {\r\n\t\tthrow new Error('Bits must be between 1 and 53 (JavaScript safe integer limit)');\r\n\t}\r\n\r\n\t// Single-blob hash for sharding (not the field-framed digest).\r\n\tconst hashBytes = resolveHasher(algorithm)(toBytes(data, inputEncoding));\r\n\r\n\t// Take first 8 bytes and convert to number\r\n\tconst view = new DataView(hashBytes.buffer, hashBytes.byteOffset, Math.min(8, hashBytes.length));\r\n\tconst fullHash = view.getBigUint64(0, false); // big-endian\r\n\r\n\t// Modulo by 2^bits\r\n\tconst modulus = BigInt(2) ** BigInt(bits);\r\n\tconst result = fullHash % modulus;\r\n\r\n\treturn Number(result);\r\n}\r\n\r\n/**\r\n * Sign data with a private key\r\n *\r\n * @param data - Data to sign (typically a hash)\r\n * @param privateKey - Private key (base64url string or Uint8Array)\r\n * @param curve - Elliptic curve (default: 'secp256k1')\r\n * @param inputEncoding - Encoding of data input (default: 'base64url')\r\n * @param keyEncoding - Encoding of private key (default: 'base64url')\r\n * @param outputEncoding - Encoding of signature output (default: 'base64url')\r\n * @returns Signature in specified encoding\r\n *\r\n * @example\r\n * ```typescript\r\n * // Sign a hash with secp256k1\r\n * const sig = sign(hashData, privateKey);\r\n *\r\n * // Sign with Ed25519\r\n * const sig2 = sign(hashData, privateKey, 'ed25519');\r\n * ```\r\n */\r\nexport function sign(\r\n\tdata: string | Uint8Array,\r\n\tprivateKey: string | Uint8Array,\r\n\tcurve: CurveType = 'secp256k1',\r\n\tinputEncoding: Encoding = 'base64url',\r\n\tkeyEncoding: Encoding = 'base64url',\r\n\toutputEncoding: Encoding = 'base64url'\r\n): string | Uint8Array {\r\n\tconst dataBytes = toBytes(data, inputEncoding);\r\n\tconst keyBytes = toBytes(privateKey, keyEncoding);\r\n\r\n\tlet sigBytes: Uint8Array;\r\n\r\n\tswitch (curve) {\r\n\t\tcase 'secp256k1':\r\n\t\t\tsigBytes = secp256k1.sign(dataBytes, keyBytes, { lowS: true });\r\n\t\t\tbreak;\r\n\t\tcase 'p256':\r\n\t\t\tsigBytes = p256.sign(dataBytes, keyBytes, { lowS: true });\r\n\t\t\tbreak;\r\n\t\tcase 'ed25519':\r\n\t\t\tsigBytes = ed25519.sign(dataBytes, keyBytes);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\r\n\t}\r\n\r\n\treturn fromBytes(sigBytes, outputEncoding);\r\n}\r\n\r\n/**\r\n * Verify a signature\r\n *\r\n * @param data - Data that was signed\r\n * @param signature - Signature to verify\r\n * @param publicKey - Public key\r\n * @param curve - Elliptic curve (default: 'secp256k1')\r\n * @param inputEncoding - Encoding of data input (default: 'base64url')\r\n * @param sigEncoding - Encoding of signature (default: 'base64url')\r\n * @param keyEncoding - Encoding of public key (default: 'base64url')\r\n * @returns true if signature is valid, false otherwise\r\n *\r\n * @example\r\n * ```typescript\r\n * // Verify a signature\r\n * const isValid = verify(hashData, signature, publicKey);\r\n *\r\n * // Verify with Ed25519\r\n * const isValid2 = verify(hashData, signature, publicKey, 'ed25519');\r\n * ```\r\n */\r\nexport function verify(\r\n\tdata: string | Uint8Array,\r\n\tsignature: string | Uint8Array,\r\n\tpublicKey: string | Uint8Array,\r\n\tcurve: CurveType = 'secp256k1',\r\n\tinputEncoding: Encoding = 'base64url',\r\n\tsigEncoding: Encoding = 'base64url',\r\n\tkeyEncoding: Encoding = 'base64url'\r\n): boolean {\r\n\ttry {\r\n\t\tconst dataBytes = toBytes(data, inputEncoding);\r\n\t\tconst sigBytes = toBytes(signature, sigEncoding);\r\n\t\tconst keyBytes = toBytes(publicKey, keyEncoding);\r\n\r\n\t\tswitch (curve) {\r\n\t\t\tcase 'secp256k1': {\r\n\t\t\t\treturn secp256k1.verify(sigBytes, dataBytes, keyBytes);\r\n\t\t\t}\r\n\t\t\tcase 'p256': {\r\n\t\t\t\treturn p256.verify(sigBytes, dataBytes, keyBytes);\r\n\t\t\t}\r\n\t\t\tcase 'ed25519': {\r\n\t\t\t\treturn ed25519.verify(sigBytes, dataBytes, keyBytes);\r\n\t\t\t}\r\n\t\t\tdefault:\r\n\t\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\r\n\t\t}\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/**\r\n * Generate cryptographically secure random bytes\r\n *\r\n * @param bits - Number of bits to generate (default: 256)\r\n * @param encoding - Output encoding (default: 'base64url')\r\n * @returns Random bytes in the specified encoding\r\n */\r\nexport function randomBytes(bits: number = 256, encoding: Encoding = 'base64url'): string | Uint8Array {\r\n\tconst bytes = Math.ceil(bits / 8);\r\n\tconst randomBytesArray = nobleRandomBytes(bytes);\r\n\treturn fromBytes(randomBytesArray, encoding);\r\n}\r\n\r\n/**\r\n * Generate a random private key\r\n */\r\nexport function generatePrivateKey(curve: CurveType = 'secp256k1', encoding: Encoding = 'base64url'): string | Uint8Array {\r\n\tlet keyBytes: Uint8Array;\r\n\r\n\tswitch (curve) {\r\n\t\tcase 'secp256k1':\r\n\t\t\tkeyBytes = secp256k1.utils.randomSecretKey();\r\n\t\t\tbreak;\r\n\t\tcase 'p256':\r\n\t\t\tkeyBytes = p256.utils.randomSecretKey();\r\n\t\t\tbreak;\r\n\t\tcase 'ed25519':\r\n\t\t\tkeyBytes = ed25519.utils.randomSecretKey();\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\r\n\t}\r\n\r\n\treturn fromBytes(keyBytes, encoding);\r\n}\r\n\r\n/**\r\n * Get public key from private key\r\n */\r\nexport function getPublicKey(\r\n\tprivateKey: string | Uint8Array,\r\n\tcurve: CurveType = 'secp256k1',\r\n\tkeyEncoding: Encoding = 'base64url',\r\n\toutputEncoding: Encoding = 'base64url'\r\n): string | Uint8Array {\r\n\tconst keyBytes = toBytes(privateKey, keyEncoding);\r\n\r\n\tlet pubBytes: Uint8Array;\r\n\r\n\tswitch (curve) {\r\n\t\tcase 'secp256k1':\r\n\t\t\tpubBytes = secp256k1.getPublicKey(keyBytes);\r\n\t\t\tbreak;\r\n\t\tcase 'p256':\r\n\t\t\tpubBytes = p256.getPublicKey(keyBytes);\r\n\t\t\tbreak;\r\n\t\tcase 'ed25519':\r\n\t\t\tpubBytes = ed25519.getPublicKey(keyBytes);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tthrow new Error(`Unsupported curve: ${curve}`);\r\n\t}\r\n\r\n\treturn fromBytes(pubBytes, outputEncoding);\r\n}\r\n\r\n","/**\r\n * Self-describing content identifiers (CIDv1) for Quereus.\r\n *\r\n * Where {@link ./crypto.ts | digest} emits a *bare* hash (raw digest bytes in\r\n * some text encoding), this module emits an interoperable, self-describing\r\n * CIDv1:\r\n *\r\n * ```\r\n * CIDv1 = multibase( version ‖ multicodec(content-type) ‖ multihash )\r\n * multihash = hashFnCode ‖ digestLength ‖ digestBytes\r\n * ```\r\n *\r\n * The value carries its own multibase, multicodec (content type), and multihash\r\n * (hash algorithm + length), so a consumer can decode it without out-of-band\r\n * knowledge, and an algorithm migration (e.g. sha2-256 → another hash) is\r\n * unambiguous because the hash code is recorded *in the value*.\r\n *\r\n * All framing/parsing comes from the audited `multiformats` library — there is\r\n * no bespoke byte-pushing here. The actual hashing reuses the same synchronous,\r\n * cross-platform `@noble/hashes` functions the rest of the plugin uses (via\r\n * {@link resolveHasher}), so the output is byte-identical to the CID an external\r\n * content-addressed store (IPFS/IPLD) computes for the same bytes:\r\n * `cid(utf8('hello world'))` === `bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e`.\r\n */\r\n\r\nimport { CID } from 'multiformats/cid';\r\nimport * as Digest from 'multiformats/hashes/digest';\r\nimport { base32 } from 'multiformats/bases/base32';\r\nimport { base58btc } from 'multiformats/bases/base58';\r\nimport { base64url } from 'multiformats/bases/base64';\r\nimport { base16 } from 'multiformats/bases/base16';\r\nimport type { MultibaseEncoder, MultibaseDecoder } from 'multiformats/bases/interface';\r\nimport { resolveHasher, type HashAlgorithm } from './crypto.js';\r\n\r\n/** Content-type multicodec selectable for the CID. Extensible. */\r\nexport type Multicodec = 'raw' | 'dag-cbor';\r\n/** Hash-algorithm multihash code selectable for the CID. */\r\nexport type MultihashCode = 'sha2-256' | 'sha2-512' | 'blake3';\r\n/** Multibase the CID string is rendered in. */\r\nexport type Multibase = 'base32' | 'base58btc' | 'base64url' | 'base16';\r\n\r\n/** Parsed parts of a CIDv1 (or CIDv0), as returned by {@link cidDecode}. */\r\nexport interface CidParts {\r\n\t/** CID version (1 for the values this module produces; 0 for legacy CIDv0). */\r\n\treadonly version: number;\r\n\t/** Content-type codec name when recognized, else the raw multicodec number. */\r\n\treadonly codec: Multicodec | number;\r\n\t/** Hash-algorithm code name when recognized, else the raw multihash number. */\r\n\treadonly hashCode: MultihashCode | number;\r\n\t/** Raw digest bytes (without the multihash code/length prefix). */\r\n\treadonly digest: Uint8Array;\r\n}\r\n\r\n// --- Multiformats code tables (see multiformats/multicodec table.csv) --- //\r\n\r\n/** Content-type name → multicodec code. */\r\nconst MULTICODEC_CODES: Record<Multicodec, number> = {\r\n\t'raw': 0x55,\r\n\t'dag-cbor': 0x71,\r\n};\r\n\r\n/** Hash name → multihash code. */\r\nconst MULTIHASH_CODES: Record<MultihashCode, number> = {\r\n\t'sha2-256': 0x12,\r\n\t'sha2-512': 0x13,\r\n\t'blake3': 0x1e,\r\n};\r\n\r\n/** Multihash code → the synchronous `@noble/hashes` algorithm that produces it. */\r\nconst MULTIHASH_TO_ALGORITHM: Record<MultihashCode, HashAlgorithm> = {\r\n\t'sha2-256': 'sha256',\r\n\t'sha2-512': 'sha512',\r\n\t'blake3': 'blake3',\r\n};\r\n\r\n/**\r\n * Multihash code → exact digest length in bytes. A CID is replicable only if its\r\n * digest length is fixed, so blake3 (which is variable-length in general) is\r\n * pinned to 32 bytes here, matching the plugin's blake3 output and sha2-256.\r\n */\r\nconst MULTIHASH_DIGEST_LENGTHS: Record<MultihashCode, number> = {\r\n\t'sha2-256': 32,\r\n\t'sha2-512': 64,\r\n\t'blake3': 32,\r\n};\r\n\r\n/** Reverse lookups for {@link cidDecode}: code number → friendly name. */\r\nconst MULTICODEC_NAMES: ReadonlyMap<number, Multicodec> = new Map(\r\n\t(Object.entries(MULTICODEC_CODES) as [Multicodec, number][]).map(([name, code]) => [code, name])\r\n);\r\nconst MULTIHASH_NAMES: ReadonlyMap<number, MultihashCode> = new Map(\r\n\t(Object.entries(MULTIHASH_CODES) as [MultihashCode, number][]).map(([name, code]) => [code, name])\r\n);\r\n\r\n/** Multibase name → its multiformats encoder. */\r\nconst MULTIBASE_ENCODERS: Record<Multibase, MultibaseEncoder<string>> = {\r\n\t'base32': base32,\r\n\t'base58btc': base58btc,\r\n\t'base64url': base64url,\r\n\t'base16': base16,\r\n};\r\n\r\n/**\r\n * Combined decoder that dispatches on the multibase prefix character, so\r\n * {@link cidDecode} accepts a CID in any of the supported bases without the\r\n * caller having to declare which.\r\n */\r\nconst MULTIBASE_DECODER: MultibaseDecoder<string> = base32.decoder\r\n\t.or(base58btc.decoder)\r\n\t.or(base64url.decoder)\r\n\t.or(base16.decoder);\r\n\r\nfunction resolveCodecCode(codec: Multicodec): number {\r\n\tconst code = MULTICODEC_CODES[codec];\r\n\tif (code === undefined) {\r\n\t\tthrow new Error(`cid: unsupported multicodec '${codec}' (expected one of ${Object.keys(MULTICODEC_CODES).join(', ')})`);\r\n\t}\r\n\treturn code;\r\n}\r\n\r\nfunction resolveBaseEncoder(base: Multibase): MultibaseEncoder<string> {\r\n\tconst encoder = MULTIBASE_ENCODERS[base];\r\n\tif (!encoder) {\r\n\t\tthrow new Error(`cid: unsupported multibase '${base}' (expected one of ${Object.keys(MULTIBASE_ENCODERS).join(', ')})`);\r\n\t}\r\n\treturn encoder;\r\n}\r\n\r\n/**\r\n * Frame an **already-computed** digest as a CIDv1 string. The caller asserts\r\n * which `hash` produced the digest; the digest length is validated against that\r\n * hash so a mismatched assertion is rejected rather than silently mis-framed.\r\n *\r\n * Use this to turn an existing field-tuple digest into a CID without re-hashing,\r\n * e.g. `cidV1(digest(fields, 'sha256', 'bytes'), 'sha2-256')`.\r\n *\r\n * @param digest - Raw digest bytes (no multihash prefix).\r\n * @param hash - The multihash code asserting which algorithm produced `digest`.\r\n * @param codec - Content-type multicodec (default `'raw'`).\r\n * @param base - Multibase to render in (default `'base32'`, the IPFS canonical).\r\n */\r\nexport function cidV1(\r\n\tdigest: Uint8Array,\r\n\thash: MultihashCode,\r\n\tcodec: Multicodec = 'raw',\r\n\tbase: Multibase = 'base32'\r\n): string {\r\n\tconst hashCode = MULTIHASH_CODES[hash];\r\n\tif (hashCode === undefined) {\r\n\t\tthrow new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);\r\n\t}\r\n\tconst expectedLength = MULTIHASH_DIGEST_LENGTHS[hash];\r\n\tif (digest.length !== expectedLength) {\r\n\t\tthrow new Error(`cid: digest length ${digest.length} does not match asserted hash '${hash}' (expected ${expectedLength} bytes)`);\r\n\t}\r\n\tconst codecCode = resolveCodecCode(codec);\r\n\tconst encoder = resolveBaseEncoder(base);\r\n\tconst multihash = Digest.create(hashCode, digest);\r\n\treturn CID.createV1(codecCode, multihash).toString(encoder);\r\n}\r\n\r\n/**\r\n * Hash `data`, wrap the digest as a multihash, frame it as a CIDv1, and encode\r\n * in `base`. The result is the same interoperable address an IPFS/IPLD store\r\n * computes for the same bytes (for the matching codec/hash).\r\n *\r\n * @param data - The content bytes to address.\r\n * @param codec - Content-type multicodec (default `'raw'`).\r\n * @param hash - Hash algorithm (default `'sha2-256'`).\r\n * @param base - Multibase to render in (default `'base32'`, the IPFS canonical).\r\n */\r\nexport function cid(\r\n\tdata: Uint8Array,\r\n\tcodec: Multicodec = 'raw',\r\n\thash: MultihashCode = 'sha2-256',\r\n\tbase: Multibase = 'base32'\r\n): string {\r\n\tconst algorithm = MULTIHASH_TO_ALGORITHM[hash];\r\n\tif (!algorithm) {\r\n\t\tthrow new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);\r\n\t}\r\n\tconst digest = resolveHasher(algorithm)(data);\r\n\treturn cidV1(digest, hash, codec, base);\r\n}\r\n\r\n/**\r\n * Parse a CID string back into its parts, for schema validation and migration.\r\n * Recognized codec/hash codes are returned as friendly names; unrecognized ones\r\n * as their raw numbers. Throws cleanly on malformed input (delegated to\r\n * `multiformats`), never silently mis-framing.\r\n */\r\nexport function cidDecode(value: string): CidParts {\r\n\tconst parsed = CID.parse(value, MULTIBASE_DECODER);\r\n\treturn {\r\n\t\tversion: parsed.version,\r\n\t\tcodec: MULTICODEC_NAMES.get(parsed.code) ?? parsed.code,\r\n\t\thashCode: MULTIHASH_NAMES.get(parsed.multihash.code) ?? parsed.multihash.code,\r\n\t\tdigest: parsed.multihash.digest,\r\n\t};\r\n}\r\n","/**\r\n * Salted-leaf SET COMMITMENT for per-attribute selective disclosure.\r\n *\r\n * An authority commits to a whole set of attributes as a single root value (which\r\n * it signs / persists), then later reveals only a chosen *subset* to a recipient —\r\n * with a proof that the revealed values are genuinely the committed ones — without\r\n * leaking the values of the withheld attributes. A flat `digest(whole set)` cannot\r\n * do this (verifying one field needs the whole pre-image, so it is all-or-nothing);\r\n * this construction supports *partial opening*.\r\n *\r\n * ## Construction (flat salted-leaf set commitment, NOT a Merkle tree)\r\n *\r\n * Each disclosable attribute is a salted leaf, and the commitment (root) is the\r\n * digest of all leaf digests in canonical order:\r\n *\r\n * ```\r\n * leafDigest = digest([SD_LEAF_DOMAIN_V1, name, value, salt]) // raw digest bytes\r\n * root = digest([SD_SET_DOMAIN_V1, sortedLeaf_0, sortedLeaf_1, ...])\r\n * ```\r\n *\r\n * Both layers compose on the existing canonical {@link encodeFields} framing\r\n * (injective, type-tagged, length-prefixed, replicable) — the same layering the CID\r\n * work uses — so a *generic* salted-set primitive is simultaneously reusable and\r\n * fully DB-enforceable. This is the same shape the IETF SD-JWT standard settled on\r\n * (flat salted hashes, not a tree); we are NOT wire-compatible with SD-JWT (we reuse\r\n * Optimystic's own `encodeFields` framing for cross-peer replicability) — SD-JWT is\r\n * cited only as conceptual precedent that the smaller construction is the right one.\r\n *\r\n * Voter selective-disclosure field sets are small (a handful to a few dozen fields),\r\n * so a tree's only advantage — O(log n) proof size — is marginal, while a tree drags\r\n * in real footguns we would have to hand-roll and pin (arity, odd-node handling /\r\n * the CVE-2012-2459 duplicate-leaf forgery class, leaf-vs-internal domain separation,\r\n * and a separate audit-path proof format). A flat construction avoids all of them.\r\n *\r\n * ## Why these specific choices\r\n *\r\n * - **`name` is hashed into the leaf** so a disclosed `(value, salt)` proof cannot be\r\n * replayed against a different attribute slot (e.g. presenting an `over18=true`\r\n * proof as the `citizen` field). The binding is free given `encodeFields` framing.\r\n * - **`salt` is per-leaf and mandatory** — low-entropy attributes (DOB, booleans, ZIP)\r\n * are brute-forceable from a bare hash, and independent salts also defeat cross-\r\n * registrant equality correlation. Salts come from `random_bytes` (≥128 bits).\r\n * - **Canonical order is by raw leaf-digest bytes (lexicographic), and this is FORCED,\r\n * not a preference.** In a disclosure the verifier learns the *names* of only the\r\n * disclosed leaves; the withheld leaves arrive as opaque digests with no name. So the\r\n * verifier can re-derive the root only if the ordering key is something it holds for\r\n * *every* leaf — the leaf digest itself. Sorting by name would be unverifiable for\r\n * hidden leaves. Do NOT \"tidy\" this into a name sort.\r\n * - Sort is over **raw digest bytes**, never over encoded strings — an encoding-\r\n * dependent ordering would break cross-peer agreement. Output encoding applies only\r\n * to the final root.\r\n *\r\n * Because leaf and root reuse `encodeFields`, a future `DIGEST_FORMAT_V1` bump changes\r\n * `setCommit` output too; this coupling is intentional (one canonical framing).\r\n */\r\n\r\nimport { fromString as uint8ArrayFromString, toString as uint8ArrayToString } from 'uint8arrays';\r\nimport {\r\n\tencodeFields,\r\n\tresolveHasher,\r\n\tresolveOutputEncoder,\r\n\ttype DigestField,\r\n\ttype DigestHasher,\r\n\ttype OutputEncoder,\r\n} from './crypto.js';\r\n\r\n/**\r\n * Fixed domain-separation constants — the leading string field of each layer's\r\n * {@link encodeFields} tuple. They are pinned EXACTLY like `DIGEST_FORMAT_V1`:\r\n *\r\n * - the two strings MUST be distinct, so a leaf hash can never equal a root hash;\r\n * - neither may change without a deliberate, breaking version bump — which would\r\n * change every committed root and every signature taken over it.\r\n *\r\n * Do not \"tidy\" or shorten these.\r\n */\r\nconst SD_LEAF_DOMAIN_V1 = 'optimystic/sd-leaf/v1';\r\nconst SD_SET_DOMAIN_V1 = 'optimystic/sd-set/v1';\r\n\r\n/** Hidden leaf digests travel as base64url text — the plugin's canonical text encoding. */\r\nconst HIDDEN_ENCODING = 'base64url';\r\n\r\n/** One disclosable attribute. `value` spans the SQL value space ({@link DigestField}). */\r\nexport interface SaltedLeaf {\r\n\treadonly name: string;\r\n\treadonly value: DigestField;\r\n\t/** base64url text (e.g. from `random_bytes`) or raw bytes. Mandatory, non-empty. */\r\n\treadonly salt: string | Uint8Array;\r\n}\r\n\r\n/** A disclosure payload sent to a recipient. */\r\nexport interface SetDisclosure {\r\n\t/** The opened `(name, value, salt)` triples. */\r\n\treadonly disclosed: readonly SaltedLeaf[];\r\n\t/** Opaque leaf digests (base64url) of the withheld leaves — no name, no value, no salt. */\r\n\treadonly hidden: readonly string[];\r\n}\r\n\r\n// --- internal helpers --- //\r\n\r\n/** Lexicographic compare of two byte arrays (the canonical leaf ordering key). */\r\nfunction compareBytes(a: Uint8Array, b: Uint8Array): number {\r\n\tconst len = Math.min(a.length, b.length);\r\n\tfor (let i = 0; i < len; i++) {\r\n\t\tconst d = a[i]! - b[i]!;\r\n\t\tif (d !== 0) return d;\r\n\t}\r\n\treturn a.length - b.length;\r\n}\r\n\r\n/** Constant-shape byte equality (length first, then content). */\r\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\r\n\tif (a.length !== b.length) return false;\r\n\tfor (let i = 0; i < a.length; i++) {\r\n\t\tif (a[i] !== b[i]) return false;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Normalize a leaf's salt to raw bytes — a base64url string (the form `random_bytes`\r\n * returns) decodes to bytes, raw bytes pass through — so the two representations of the\r\n * same salt commit identically. THROWS on a missing or empty salt (unsalted leaves are\r\n * brute-forceable, an invalid state we make impossible).\r\n */\r\nfunction requireSaltBytes(leaf: SaltedLeaf): Uint8Array {\r\n\tconst { salt } = leaf;\r\n\tif (salt == null) {\r\n\t\tthrow new Error(`set commitment: leaf '${leaf.name}' is missing a salt (an unsalted leaf is brute-forceable)`);\r\n\t}\r\n\tconst bytes = salt instanceof Uint8Array ? salt : uint8ArrayFromString(salt, HIDDEN_ENCODING);\r\n\tif (bytes.length === 0) {\r\n\t\tthrow new Error(`set commitment: leaf '${leaf.name}' has an empty salt (an unsalted leaf is brute-forceable)`);\r\n\t}\r\n\treturn bytes;\r\n}\r\n\r\n/**\r\n * THROW on a duplicate `name`. Two leaves with the same name would let a holder\r\n * selectively present whichever value suits them; the authority side (which holds all\r\n * names) is the only place uniqueness can be enforced — the verifier never sees the\r\n * hidden names — so the primitive must fail-fast.\r\n */\r\nfunction assertUniqueNames(leaves: readonly SaltedLeaf[]): void {\r\n\tconst seen = new Set<string>();\r\n\tfor (const leaf of leaves) {\r\n\t\tif (seen.has(leaf.name)) {\r\n\t\t\tthrow new Error(`set commitment: duplicate leaf name '${leaf.name}'`);\r\n\t\t}\r\n\t\tseen.add(leaf.name);\r\n\t}\r\n}\r\n\r\n// --- public API --- //\r\n\r\n/**\r\n * Raw leaf digest bytes for one salted leaf: `digest([SD_LEAF_DOMAIN_V1, name,\r\n * value, salt])`. Domain-separated (can never equal a root) and name-bound (a\r\n * `(value, salt)` proof cannot be replayed under another attribute name). THROWS on\r\n * a missing/empty salt.\r\n */\r\nexport function leafDigest(leaf: SaltedLeaf, hasher: DigestHasher): Uint8Array {\r\n\tconst saltBytes = requireSaltBytes(leaf);\r\n\treturn hasher(encodeFields([SD_LEAF_DOMAIN_V1, leaf.name, leaf.value, saltBytes]));\r\n}\r\n\r\n/**\r\n * Commit to a SET of salted leaves → a single root (the signed/persisted value).\r\n * Sorts leaves by raw leaf-digest bytes, then digests them under `SD_SET_DOMAIN_V1`.\r\n * Like `digest`, this emits a BARE digest — apply `cid()` on top for the self-\r\n * describing column representation (`cid(set_commit(...))`).\r\n *\r\n * The empty set is well-defined (the digest of `[SD_SET_DOMAIN_V1]`), not an error.\r\n * THROWS on a duplicate `name` or a missing/empty `salt` (invalid states made\r\n * impossible). Resolve `hasher`/`encode` once and reuse — no per-call branching.\r\n */\r\nexport function setCommit(\r\n\tleaves: readonly SaltedLeaf[],\r\n\thasher: DigestHasher = resolveHasher('sha256'),\r\n\tencode: OutputEncoder = resolveOutputEncoder('base64url'),\r\n): string | Uint8Array {\r\n\tassertUniqueNames(leaves);\r\n\tconst leafDigests = leaves.map((leaf) => leafDigest(leaf, hasher));\r\n\tleafDigests.sort(compareBytes);\r\n\treturn encode(hasher(encodeFields([SD_SET_DOMAIN_V1, ...leafDigests])));\r\n}\r\n\r\n/**\r\n * Split a leaf set into the revealed `(name, value, salt)` triples plus the opaque\r\n * leaf digests (base64url) of the rest. Withheld `value`/`salt` never appear in the\r\n * output. Names in `revealNames` that match no leaf are simply not disclosed.\r\n * THROWS on a duplicate `name` or a missing/empty salt of a withheld leaf.\r\n */\r\nexport function setDisclose(\r\n\tleaves: readonly SaltedLeaf[],\r\n\trevealNames: readonly string[],\r\n\thasher: DigestHasher = resolveHasher('sha256'),\r\n): SetDisclosure {\r\n\tassertUniqueNames(leaves);\r\n\tconst reveal = new Set(revealNames);\r\n\tconst disclosed: SaltedLeaf[] = [];\r\n\tconst hidden: string[] = [];\r\n\tfor (const leaf of leaves) {\r\n\t\tif (reveal.has(leaf.name)) {\r\n\t\t\tdisclosed.push(leaf);\r\n\t\t} else {\r\n\t\t\thidden.push(uint8ArrayToString(leafDigest(leaf, hasher), HIDDEN_ENCODING));\r\n\t\t}\r\n\t}\r\n\treturn { disclosed, hidden };\r\n}\r\n\r\n/**\r\n * Verify a disclosure against a signed root. Recomputes the disclosed leaves'\r\n * digests, unions them with the supplied hidden digests, sorts by bytes, recomputes\r\n * the root, and compares to `root`. This reconstructs the ENTIRE root, so it proves\r\n * the disclosed leaves belong to *exactly* this committed set — the holder cannot\r\n * add, drop, or swap a leaf (the leaf count is bound too).\r\n *\r\n * `encode` is how the signed `root` is rendered (so the recomputed root is encoded\r\n * the same way before comparison); for a `Uint8Array` root the raw bytes are compared\r\n * directly. Returns `false` on mismatch or malformed input — mirroring `verify`'s\r\n * forgiving contract rather than throwing.\r\n */\r\nexport function setVerify(\r\n\troot: string | Uint8Array,\r\n\tdisclosure: SetDisclosure,\r\n\thasher: DigestHasher = resolveHasher('sha256'),\r\n\tencode: OutputEncoder = resolveOutputEncoder('base64url'),\r\n): boolean {\r\n\ttry {\r\n\t\tconst { disclosed, hidden } = disclosure;\r\n\t\tconst digests: Uint8Array[] = [];\r\n\t\tfor (const leaf of disclosed) {\r\n\t\t\tdigests.push(leafDigest(leaf, hasher));\r\n\t\t}\r\n\t\tfor (const h of hidden) {\r\n\t\t\tdigests.push(uint8ArrayFromString(h, HIDDEN_ENCODING));\r\n\t\t}\r\n\t\tdigests.sort(compareBytes);\r\n\t\tconst recomputed = hasher(encodeFields([SD_SET_DOMAIN_V1, ...digests]));\r\n\t\tif (root instanceof Uint8Array) {\r\n\t\t\treturn bytesEqual(recomputed, root);\r\n\t\t}\r\n\t\tconst encoded = encode(recomputed);\r\n\t\treturn typeof encoded === 'string' && encoded === root;\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n"]}
|