cesr-ts 0.4.0 → 0.6.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/esm/src/adapters/async-iterable.js +8 -0
- package/esm/src/adapters/effection.js +6 -0
- package/esm/src/annotate/annotator.js +12 -0
- package/esm/src/annotate/cli.js +12 -0
- package/esm/src/annotate/comments.js +5 -1
- package/esm/src/annotate/denot.js +6 -0
- package/esm/src/annotate/render.js +15 -3
- package/esm/src/core/bytes.js +9 -0
- package/esm/src/core/errors.js +3 -0
- package/esm/src/core/parser-engine.js +1 -1
- package/esm/src/core/parser-frame-parser.js +22 -18
- package/esm/src/core/vocabulary.js +47 -0
- package/esm/src/index.js +5 -0
- package/esm/src/parser/attachment-parser.js +4 -0
- package/esm/src/parser/cold-start.js +6 -0
- package/esm/src/parser/group-dispatch.js +2 -2
- package/esm/src/primitives/aggor.js +374 -30
- package/esm/src/primitives/bexter.js +2 -3
- package/esm/src/primitives/blinder.js +6 -0
- package/esm/src/primitives/byte-like.js +19 -0
- package/esm/src/primitives/cigar.js +17 -3
- package/esm/src/primitives/cipher.js +91 -6
- package/esm/src/primitives/codex.js +39 -6
- package/esm/src/primitives/compactor.js +307 -3
- package/esm/src/primitives/counter.js +6 -2
- package/esm/src/primitives/dater.js +1 -2
- package/esm/src/primitives/decimer.js +7 -11
- package/esm/src/primitives/decrypter.js +123 -7
- package/esm/src/primitives/diger.js +57 -3
- package/esm/src/primitives/disclosure.js +300 -0
- package/esm/src/primitives/encrypter.js +130 -7
- package/esm/src/primitives/hydrate.js +49 -0
- package/esm/src/primitives/ilker.js +2 -3
- package/esm/src/primitives/indexer.js +21 -4
- package/esm/src/primitives/labeler.js +1 -2
- package/esm/src/primitives/mapper.js +612 -35
- package/esm/src/primitives/matter.js +133 -3
- package/esm/src/primitives/mediar.js +6 -0
- package/esm/src/primitives/noncer.js +2 -3
- package/esm/src/primitives/number.js +1 -2
- package/esm/src/primitives/pather.js +102 -5
- package/esm/src/primitives/prefixer.js +1 -2
- package/esm/src/primitives/primitive.js +14 -0
- package/esm/src/primitives/registry.js +2 -0
- package/esm/src/primitives/saider.js +80 -3
- package/esm/src/primitives/salter.js +96 -2
- package/esm/src/primitives/sealed-box.js +41 -0
- package/esm/src/primitives/seqner.js +1 -2
- package/esm/src/primitives/siger.js +58 -3
- package/esm/src/primitives/signer.js +243 -9
- package/esm/src/primitives/streamer.js +28 -0
- package/esm/src/primitives/structing.js +727 -0
- package/esm/src/primitives/structor.js +20 -0
- package/esm/src/primitives/tagger.js +1 -2
- package/esm/src/primitives/texter.js +1 -2
- package/esm/src/primitives/tholder.js +552 -10
- package/esm/src/primitives/traitor.js +2 -3
- package/esm/src/primitives/verfer.js +50 -6
- package/esm/src/primitives/verser.js +16 -6
- package/esm/src/router/router-stub.js +1 -0
- package/esm/src/serder/native.js +1326 -0
- package/esm/src/serder/serder.js +1558 -59
- package/esm/src/serder/serdery.js +92 -7
- package/esm/src/serder/smell.js +26 -1
- package/esm/src/tables/codex-utils.js +2 -0
- package/esm/src/tables/counter-codex.js +2 -0
- package/esm/src/tables/counter-groups.js +6 -0
- package/esm/src/tables/versions.js +4 -0
- package/esm/src/version.js +5 -2
- package/package.json +5 -2
- package/types/src/adapters/async-iterable.d.ts +8 -0
- package/types/src/adapters/async-iterable.d.ts.map +1 -1
- package/types/src/adapters/effection.d.ts +7 -0
- package/types/src/adapters/effection.d.ts.map +1 -1
- package/types/src/annotate/annotator.d.ts +12 -0
- package/types/src/annotate/annotator.d.ts.map +1 -1
- package/types/src/annotate/cli.d.ts +13 -0
- package/types/src/annotate/cli.d.ts.map +1 -1
- package/types/src/annotate/comments.d.ts +4 -0
- package/types/src/annotate/comments.d.ts.map +1 -1
- package/types/src/annotate/denot.d.ts +6 -0
- package/types/src/annotate/denot.d.ts.map +1 -1
- package/types/src/annotate/render.d.ts.map +1 -1
- package/types/src/annotate/types.d.ts +2 -0
- package/types/src/annotate/types.d.ts.map +1 -1
- package/types/src/bench/parser-benchmark.d.ts +3 -0
- package/types/src/bench/parser-benchmark.d.ts.map +1 -1
- package/types/src/core/bytes.d.ts +9 -0
- package/types/src/core/bytes.d.ts.map +1 -1
- package/types/src/core/errors.d.ts +3 -0
- package/types/src/core/errors.d.ts.map +1 -1
- package/types/src/core/parser-engine.d.ts +4 -0
- package/types/src/core/parser-engine.d.ts.map +1 -1
- package/types/src/core/parser-frame-parser.d.ts.map +1 -1
- package/types/src/core/types.d.ts +2 -0
- package/types/src/core/types.d.ts.map +1 -1
- package/types/src/core/vocabulary.d.ts +48 -0
- package/types/src/core/vocabulary.d.ts.map +1 -0
- package/types/src/index.d.ts +5 -0
- package/types/src/index.d.ts.map +1 -1
- package/types/src/parser/attachment-parser.d.ts +4 -0
- package/types/src/parser/attachment-parser.d.ts.map +1 -1
- package/types/src/parser/cold-start.d.ts +6 -0
- package/types/src/parser/cold-start.d.ts.map +1 -1
- package/types/src/parser/group-dispatch.d.ts.map +1 -1
- package/types/src/primitives/aggor.d.ts +100 -13
- package/types/src/primitives/aggor.d.ts.map +1 -1
- package/types/src/primitives/bexter.d.ts.map +1 -1
- package/types/src/primitives/blinder.d.ts +6 -0
- package/types/src/primitives/blinder.d.ts.map +1 -1
- package/types/src/primitives/byte-like.d.ts +7 -0
- package/types/src/primitives/byte-like.d.ts.map +1 -0
- package/types/src/primitives/cigar.d.ts +11 -1
- package/types/src/primitives/cigar.d.ts.map +1 -1
- package/types/src/primitives/cipher.d.ts +73 -2
- package/types/src/primitives/cipher.d.ts.map +1 -1
- package/types/src/primitives/codex.d.ts +34 -4
- package/types/src/primitives/codex.d.ts.map +1 -1
- package/types/src/primitives/compactor.d.ts +94 -2
- package/types/src/primitives/compactor.d.ts.map +1 -1
- package/types/src/primitives/counter.d.ts.map +1 -1
- package/types/src/primitives/dater.d.ts.map +1 -1
- package/types/src/primitives/decimer.d.ts.map +1 -1
- package/types/src/primitives/decrypter.d.ts +64 -3
- package/types/src/primitives/decrypter.d.ts.map +1 -1
- package/types/src/primitives/diger.d.ts +16 -0
- package/types/src/primitives/diger.d.ts.map +1 -1
- package/types/src/primitives/disclosure.d.ts +201 -0
- package/types/src/primitives/disclosure.d.ts.map +1 -0
- package/types/src/primitives/encrypter.d.ts +66 -4
- package/types/src/primitives/encrypter.d.ts.map +1 -1
- package/types/src/primitives/hydrate.d.ts +20 -0
- package/types/src/primitives/hydrate.d.ts.map +1 -0
- package/types/src/primitives/ilker.d.ts.map +1 -1
- package/types/src/primitives/indexer.d.ts.map +1 -1
- package/types/src/primitives/labeler.d.ts.map +1 -1
- package/types/src/primitives/mapper.d.ts +132 -11
- package/types/src/primitives/mapper.d.ts.map +1 -1
- package/types/src/primitives/matter.d.ts +33 -1
- package/types/src/primitives/matter.d.ts.map +1 -1
- package/types/src/primitives/mediar.d.ts +6 -0
- package/types/src/primitives/mediar.d.ts.map +1 -1
- package/types/src/primitives/noncer.d.ts.map +1 -1
- package/types/src/primitives/number.d.ts.map +1 -1
- package/types/src/primitives/pather.d.ts +21 -0
- package/types/src/primitives/pather.d.ts.map +1 -1
- package/types/src/primitives/prefixer.d.ts.map +1 -1
- package/types/src/primitives/primitive.d.ts +26 -0
- package/types/src/primitives/primitive.d.ts.map +1 -1
- package/types/src/primitives/registry.d.ts +3 -0
- package/types/src/primitives/registry.d.ts.map +1 -1
- package/types/src/primitives/saider.d.ts +41 -0
- package/types/src/primitives/saider.d.ts.map +1 -1
- package/types/src/primitives/salter.d.ts +56 -1
- package/types/src/primitives/salter.d.ts.map +1 -1
- package/types/src/primitives/sealed-box.d.ts +29 -0
- package/types/src/primitives/sealed-box.d.ts.map +1 -0
- package/types/src/primitives/seqner.d.ts.map +1 -1
- package/types/src/primitives/siger.d.ts +14 -0
- package/types/src/primitives/siger.d.ts.map +1 -1
- package/types/src/primitives/signer.d.ts +80 -4
- package/types/src/primitives/signer.d.ts.map +1 -1
- package/types/src/primitives/streamer.d.ts +31 -0
- package/types/src/primitives/streamer.d.ts.map +1 -0
- package/types/src/primitives/structing.d.ts +4114 -0
- package/types/src/primitives/structing.d.ts.map +1 -0
- package/types/src/primitives/structor.d.ts +6 -0
- package/types/src/primitives/structor.d.ts.map +1 -1
- package/types/src/primitives/tagger.d.ts.map +1 -1
- package/types/src/primitives/texter.d.ts.map +1 -1
- package/types/src/primitives/tholder.d.ts +150 -5
- package/types/src/primitives/tholder.d.ts.map +1 -1
- package/types/src/primitives/traitor.d.ts.map +1 -1
- package/types/src/primitives/unknown.d.ts +1 -0
- package/types/src/primitives/unknown.d.ts.map +1 -1
- package/types/src/primitives/verfer.d.ts +12 -0
- package/types/src/primitives/verfer.d.ts.map +1 -1
- package/types/src/primitives/verser.d.ts +14 -3
- package/types/src/primitives/verser.d.ts.map +1 -1
- package/types/src/router/router-stub.d.ts +3 -0
- package/types/src/router/router-stub.d.ts.map +1 -1
- package/types/src/serder/native.d.ts +115 -0
- package/types/src/serder/native.d.ts.map +1 -0
- package/types/src/serder/serder.d.ts +187 -9
- package/types/src/serder/serder.d.ts.map +1 -1
- package/types/src/serder/serdery.d.ts +42 -0
- package/types/src/serder/serdery.d.ts.map +1 -1
- package/types/src/serder/smell.d.ts +22 -0
- package/types/src/serder/smell.d.ts.map +1 -1
- package/types/src/tables/codex-utils.d.ts +3 -0
- package/types/src/tables/codex-utils.d.ts.map +1 -1
- package/types/src/tables/counter-codex.d.ts +4 -0
- package/types/src/tables/counter-codex.d.ts.map +1 -1
- package/types/src/tables/counter-groups.d.ts +6 -0
- package/types/src/tables/counter-groups.d.ts.map +1 -1
- package/types/src/tables/counter.tables.generated.d.ts.map +1 -1
- package/types/src/tables/indexer.codex.generated.d.ts.map +1 -1
- package/types/src/tables/matter.codex.generated.d.ts.map +1 -1
- package/types/src/tables/table-types.d.ts +6 -0
- package/types/src/tables/table-types.d.ts.map +1 -1
- package/types/src/tables/versions.d.ts +6 -0
- package/types/src/tables/versions.d.ts.map +1 -1
- package/types/src/version.d.ts +5 -2
- package/types/src/version.d.ts.map +1 -1
|
@@ -1,16 +1,25 @@
|
|
|
1
|
+
import { blake2b, blake2s } from "@noble/hashes/blake2";
|
|
2
|
+
import { blake3 } from "@noble/hashes/blake3";
|
|
3
|
+
import { sha256, sha512 } from "@noble/hashes/sha2";
|
|
4
|
+
import { sha3_256, sha3_512 } from "@noble/hashes/sha3";
|
|
1
5
|
import { UnknownCodeError } from "../core/errors.js";
|
|
2
|
-
import { DIGEST_CODES, matterCodexName } from "./codex.js";
|
|
6
|
+
import { DigDex, DIGEST_CODES, matterCodexName } from "./codex.js";
|
|
3
7
|
import { Matter, parseMatter } from "./matter.js";
|
|
4
8
|
/**
|
|
5
9
|
* Digest primitive family.
|
|
6
10
|
*
|
|
7
11
|
* KERIpy substance: `Diger` encapsulates self-addressing digest material and
|
|
8
12
|
* optionally constrains codes to digest codex membership (`strict=true`).
|
|
13
|
+
*
|
|
14
|
+
* Maintainer note:
|
|
15
|
+
* `DigDex` remains the canonical namespace of supported digest codes, but this
|
|
16
|
+
* class is the runtime authority for "code -> hash implementation" dispatch.
|
|
17
|
+
* Keep new digest algorithms centralized here so `Saider`, `Serder`, and app
|
|
18
|
+
* flows do not grow their own private digest switches.
|
|
9
19
|
*/
|
|
10
20
|
export class Diger extends Matter {
|
|
11
21
|
constructor(init, options = {}) {
|
|
12
|
-
|
|
13
|
-
super(matter);
|
|
22
|
+
super(init);
|
|
14
23
|
if ((options.strict ?? true) && !DIGEST_CODES.has(this.code)) {
|
|
15
24
|
throw new UnknownCodeError(`Expected digest code, got ${this.code}`);
|
|
16
25
|
}
|
|
@@ -21,7 +30,52 @@ export class Diger extends Matter {
|
|
|
21
30
|
get algorithm() {
|
|
22
31
|
return matterCodexName(this.code) ?? "UnknownDigest";
|
|
23
32
|
}
|
|
33
|
+
/** Compute raw digest bytes for `ser` using the digest family selected by `code`. */
|
|
34
|
+
static digest(ser, code) {
|
|
35
|
+
const digest = Diger.Digests.get(code);
|
|
36
|
+
if (!digest) {
|
|
37
|
+
throw new UnknownCodeError(`Unsupported digest code ${code}`);
|
|
38
|
+
}
|
|
39
|
+
return digest(ser);
|
|
40
|
+
}
|
|
41
|
+
verify(ser) {
|
|
42
|
+
return Diger.compare(ser, this.code, this.raw);
|
|
43
|
+
}
|
|
44
|
+
/** Compare `ser` against either the instance digest or an override digest. */
|
|
45
|
+
compare(ser, dig) {
|
|
46
|
+
return Diger.compare(ser, this.code, dig ?? this.raw);
|
|
47
|
+
}
|
|
48
|
+
/** Constant-shape byte comparison helper used by higher-level SAID/serder verification. */
|
|
49
|
+
static compare(ser, code, dig) {
|
|
50
|
+
const actual = Diger.digest(ser, code);
|
|
51
|
+
if (actual.length !== dig.length) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
for (let idx = 0; idx < actual.length; idx++) {
|
|
55
|
+
if (actual[idx] !== dig[idx]) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
24
61
|
}
|
|
62
|
+
/** Shared KERIpy-style digest registry keyed by canonical `DigDex` values. */
|
|
63
|
+
Object.defineProperty(Diger, "Digests", {
|
|
64
|
+
enumerable: true,
|
|
65
|
+
configurable: true,
|
|
66
|
+
writable: true,
|
|
67
|
+
value: new Map([
|
|
68
|
+
[DigDex.Blake3_256, (ser) => blake3(ser)],
|
|
69
|
+
[DigDex.Blake3_512, (ser) => blake3(ser, { dkLen: 64 })],
|
|
70
|
+
[DigDex.Blake2b_256, (ser) => blake2b(ser, { dkLen: 32 })],
|
|
71
|
+
[DigDex.Blake2b_512, (ser) => blake2b(ser, { dkLen: 64 })],
|
|
72
|
+
[DigDex.Blake2s_256, (ser) => blake2s(ser, { dkLen: 32 })],
|
|
73
|
+
[DigDex.SHA2_256, (ser) => sha256(ser)],
|
|
74
|
+
[DigDex.SHA2_512, (ser) => sha512(ser)],
|
|
75
|
+
[DigDex.SHA3_256, (ser) => sha3_256(ser)],
|
|
76
|
+
[DigDex.SHA3_512, (ser) => sha3_512(ser)],
|
|
77
|
+
])
|
|
78
|
+
});
|
|
25
79
|
/** Parse and hydrate `Diger` from txt/qb2 bytes. */
|
|
26
80
|
export function parseDiger(input, cold) {
|
|
27
81
|
return new Diger(parseMatter(input, cold));
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-field graduated-disclosure workflow helpers.
|
|
3
|
+
*
|
|
4
|
+
* This module is the verb layer for the fixed-field blinded disclosure records
|
|
5
|
+
* defined in `structing.ts`.
|
|
6
|
+
*
|
|
7
|
+
* Keep the boundary clear:
|
|
8
|
+
* - `structing.ts` owns the nouns and representation conversions
|
|
9
|
+
* - this file owns deterministic UUID derivation, blind/bound/media
|
|
10
|
+
* commitment recomputation, and candidate unblinding search
|
|
11
|
+
* - `blinder.ts` / `mediar.ts` own counted-group transport framing only
|
|
12
|
+
*
|
|
13
|
+
* This is intentionally different from KERIpy's richer `Blinder` /
|
|
14
|
+
* `Mediar` classes. `keri-ts` keeps semantic records as plain data and puts
|
|
15
|
+
* workflow verbs in a dedicated module so schema and workflow can evolve
|
|
16
|
+
* independently.
|
|
17
|
+
*/
|
|
18
|
+
import { b } from "../core/bytes.js";
|
|
19
|
+
import { DigDex, DIGEST_CODES, NonceDex } from "./codex.js";
|
|
20
|
+
import { Diger } from "./diger.js";
|
|
21
|
+
import { Labeler } from "./labeler.js";
|
|
22
|
+
import { Noncer } from "./noncer.js";
|
|
23
|
+
import { NumberPrimitive } from "./number.js";
|
|
24
|
+
import { Salter } from "./salter.js";
|
|
25
|
+
import { BlindState, BoundState, TypeMedia, } from "./structing.js";
|
|
26
|
+
import { Texter } from "./texter.js";
|
|
27
|
+
/** Normalize `Number`-like input into the KERIpy `numh` hex string projection. */
|
|
28
|
+
function coerceNumh(value) {
|
|
29
|
+
if (value instanceof NumberPrimitive) {
|
|
30
|
+
return value.numh;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "string") {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "number") {
|
|
36
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
37
|
+
throw new TypeError(`Expected non-negative integer number, got ${value}`);
|
|
38
|
+
}
|
|
39
|
+
return BigInt(value).toString(16);
|
|
40
|
+
}
|
|
41
|
+
if (value < 0n) {
|
|
42
|
+
throw new TypeError(`Expected non-negative bigint, got ${value}`);
|
|
43
|
+
}
|
|
44
|
+
return value.toString(16);
|
|
45
|
+
}
|
|
46
|
+
/** Normalize nonce-like input into crew/SAD text while preserving empty placeholders. */
|
|
47
|
+
function coerceNonceText(value) {
|
|
48
|
+
if (value instanceof Noncer) {
|
|
49
|
+
return value.nonce;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "string") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
/** Normalize label-like input into label text while preserving empty placeholders. */
|
|
57
|
+
function coerceLabelText(value) {
|
|
58
|
+
if (value instanceof Labeler) {
|
|
59
|
+
return value.text;
|
|
60
|
+
}
|
|
61
|
+
return value ?? "";
|
|
62
|
+
}
|
|
63
|
+
/** Normalize text-like input into plain text while preserving empty placeholders. */
|
|
64
|
+
function coerceTexterText(value) {
|
|
65
|
+
if (value instanceof Texter) {
|
|
66
|
+
return value.text;
|
|
67
|
+
}
|
|
68
|
+
return value ?? "";
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Require a non-empty disclosure UUID, deriving one when omitted.
|
|
72
|
+
*
|
|
73
|
+
* Maintainer rule:
|
|
74
|
+
* - placeholder semantics apply to blinded record fields
|
|
75
|
+
* - they do not apply to the disclosure UUID itself, because the UUID scopes
|
|
76
|
+
* the blind/unblind computation
|
|
77
|
+
*/
|
|
78
|
+
function ensurePresentUuid(value, options) {
|
|
79
|
+
if (value instanceof Noncer) {
|
|
80
|
+
if (value.code === NonceDex.Empty) {
|
|
81
|
+
throw new TypeError("Disclosure UUID may not be empty.");
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === "string") {
|
|
86
|
+
const nonce = new Noncer({ qb64: value });
|
|
87
|
+
if (nonce.code === NonceDex.Empty) {
|
|
88
|
+
throw new TypeError("Disclosure UUID may not be empty.");
|
|
89
|
+
}
|
|
90
|
+
return nonce;
|
|
91
|
+
}
|
|
92
|
+
return makeBlindUuid(options);
|
|
93
|
+
}
|
|
94
|
+
/** Choose the digest/noncer code used for the recomputed commitment field `d`. */
|
|
95
|
+
function effectiveDisclosureCode(current, code) {
|
|
96
|
+
if (code !== undefined) {
|
|
97
|
+
return code;
|
|
98
|
+
}
|
|
99
|
+
return DIGEST_CODES.has(current.code) ? current.code : DigDex.Blake3_256;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Recompute the committed `d` field for one fixed-field disclosure record.
|
|
103
|
+
*
|
|
104
|
+
* Parity rule:
|
|
105
|
+
* - the commitment is computed from tuple `qb64` field serializations with a
|
|
106
|
+
* dummied `d` slot
|
|
107
|
+
* - it is not computed from crew/SAD strings
|
|
108
|
+
*
|
|
109
|
+
* This mirrors the saidive/makify behavior KERIpy gets through `Structor`.
|
|
110
|
+
*/
|
|
111
|
+
function computeDisclosureNonce(descriptor, value, code) {
|
|
112
|
+
const effectiveCode = effectiveDisclosureCode(value.d, code);
|
|
113
|
+
const tuple = descriptor.toTuple(value);
|
|
114
|
+
// Fixed-field disclosure commitments work over canonical primitive tuple
|
|
115
|
+
// bytes. Crew/SAD projections are for readability and object transport, not
|
|
116
|
+
// for commitment material.
|
|
117
|
+
const ser = descriptor.fields.map((field, index) => field === "d"
|
|
118
|
+
? "#".repeat(Noncer.fullSizeForCode(effectiveCode))
|
|
119
|
+
: tuple[index].qb64).join("");
|
|
120
|
+
return new Noncer({
|
|
121
|
+
code: effectiveCode,
|
|
122
|
+
raw: Diger.digest(b(ser), effectiveCode),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** Rebuild the record with a freshly committed `d` field. */
|
|
126
|
+
function saidifyDisclosureRecord(descriptor, value, code) {
|
|
127
|
+
const d = computeDisclosureNonce(descriptor, value, code);
|
|
128
|
+
return descriptor.fromSad({
|
|
129
|
+
...descriptor.toSad(value),
|
|
130
|
+
d: d.nonce,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Derive the deterministic disclosure UUID used by blinded/bound/media
|
|
135
|
+
* commitment records.
|
|
136
|
+
*
|
|
137
|
+
* KERIpy correspondence:
|
|
138
|
+
* - mirrors `Blinder.makeUUID(...)`
|
|
139
|
+
* - sequence numbers feed the salty path through `numh`, not decimal text
|
|
140
|
+
*/
|
|
141
|
+
export function makeBlindUuid({ raw, salt, sn = 1, tier, } = {}) {
|
|
142
|
+
const salter = salt !== undefined || raw !== undefined || tier !== undefined
|
|
143
|
+
? new Salter({ qb64: salt, raw, tier })
|
|
144
|
+
: new Salter({});
|
|
145
|
+
return new Noncer({
|
|
146
|
+
code: NonceDex.Salt_256,
|
|
147
|
+
raw: salter.stretch({ path: coerceNumh(sn), tier }),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Recompute the committed `d` field for one `BlindState`.
|
|
152
|
+
*
|
|
153
|
+
* KERIpy correspondence:
|
|
154
|
+
* - same semantic role as building `Blinder(..., makify=True)` for the
|
|
155
|
+
* blind-state quadruple without instantiating a transport wrapper
|
|
156
|
+
*/
|
|
157
|
+
export function commitBlindState(value, code) {
|
|
158
|
+
return saidifyDisclosureRecord(BlindState, value, code);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Recompute the committed `d` field for one `BoundState`.
|
|
162
|
+
*
|
|
163
|
+
* KERIpy correspondence:
|
|
164
|
+
* - same semantic role as the bound-state branch of `Blinder.blind(...)`
|
|
165
|
+
*/
|
|
166
|
+
export function commitBoundState(value, code) {
|
|
167
|
+
return saidifyDisclosureRecord(BoundState, value, code);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Recompute the committed `d` field for one `TypeMedia`.
|
|
171
|
+
*
|
|
172
|
+
* KERIpy correspondence:
|
|
173
|
+
* - same semantic role as `Mediar(..., makify=True)` for typed-media records
|
|
174
|
+
*/
|
|
175
|
+
export function commitTypeMedia(value, code) {
|
|
176
|
+
return saidifyDisclosureRecord(TypeMedia, value, code);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Build one blinded disclosure-state record and compute its `d` commitment.
|
|
180
|
+
*
|
|
181
|
+
* KERIpy correspondence:
|
|
182
|
+
* - mirrors `Blinder.blind(..., bound=False)`
|
|
183
|
+
*/
|
|
184
|
+
export function makeBlindState({ uuid, raw, salt, sn = 1, tier, acdc = "", state = "", code, } = {}) {
|
|
185
|
+
return commitBlindState(BlindState.fromSad({
|
|
186
|
+
d: "",
|
|
187
|
+
u: ensurePresentUuid(uuid, { raw, salt, sn, tier }).nonce,
|
|
188
|
+
td: coerceNonceText(acdc),
|
|
189
|
+
ts: coerceLabelText(state),
|
|
190
|
+
}), code);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Build one bound blinded-state record and compute its `d` commitment.
|
|
194
|
+
*
|
|
195
|
+
* KERIpy correspondence:
|
|
196
|
+
* - mirrors `Blinder.blind(..., bound=True)`
|
|
197
|
+
*/
|
|
198
|
+
export function makeBoundState({ uuid, raw, salt, sn = 1, tier, acdc = "", state = "", bsn = 0, bd = "", code, } = {}) {
|
|
199
|
+
return commitBoundState(BoundState.fromSad({
|
|
200
|
+
d: "",
|
|
201
|
+
u: ensurePresentUuid(uuid, { raw, salt, sn, tier }).nonce,
|
|
202
|
+
td: coerceNonceText(acdc),
|
|
203
|
+
ts: coerceLabelText(state),
|
|
204
|
+
bn: coerceNumh(bsn),
|
|
205
|
+
bd: coerceNonceText(bd),
|
|
206
|
+
}), code);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Build one typed-media disclosure record and compute its `d` commitment.
|
|
210
|
+
*
|
|
211
|
+
* KERIpy correspondence:
|
|
212
|
+
* - matches `Mediar(..., makify=True)` without recreating a wrapper object
|
|
213
|
+
*/
|
|
214
|
+
export function makeTypeMedia({ uuid, raw, salt, sn = 1, tier, mt = "", mv = "", code, } = {}) {
|
|
215
|
+
return commitTypeMedia(TypeMedia.fromSad({
|
|
216
|
+
d: "",
|
|
217
|
+
u: ensurePresentUuid(uuid, { raw, salt, sn, tier }).nonce,
|
|
218
|
+
mt: coerceLabelText(mt),
|
|
219
|
+
mv: coerceTexterText(mv),
|
|
220
|
+
}), code);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Rebuild the matching blinded-state candidate if one exists.
|
|
224
|
+
*
|
|
225
|
+
* KERIpy correspondence:
|
|
226
|
+
* - mirrors `Blinder.unblind(..., bound=False)`
|
|
227
|
+
* - tries the placeholder combinations too, so callers do not need to add the
|
|
228
|
+
* empty `acdc` / empty `state` cases themselves
|
|
229
|
+
*
|
|
230
|
+
* Search strategy:
|
|
231
|
+
* - derive or validate the UUID
|
|
232
|
+
* - try the candidate `acdc` plus empty placeholder
|
|
233
|
+
* - try each caller-supplied state plus empty placeholder
|
|
234
|
+
* - return the first candidate whose recomputed `d` matches `said`
|
|
235
|
+
*/
|
|
236
|
+
export function unblindBlindState({ said, uuid, raw, salt, sn = 1, tier, acdc = "", states = [], code, }) {
|
|
237
|
+
const resolvedUuid = ensurePresentUuid(uuid, { raw, salt, sn, tier });
|
|
238
|
+
const acdcs = [...new Set([coerceNonceText(acdc), ""])];
|
|
239
|
+
const stateTexts = [...new Set(states.map(coerceLabelText).concat(""))];
|
|
240
|
+
// KERIpy search semantics intentionally treat empty ACDC/state placeholders
|
|
241
|
+
// as real candidates, because a disclosure may reveal them later or keep
|
|
242
|
+
// them compact for the current step.
|
|
243
|
+
for (const td of acdcs) {
|
|
244
|
+
for (const ts of stateTexts) {
|
|
245
|
+
const candidate = makeBlindState({
|
|
246
|
+
uuid: resolvedUuid,
|
|
247
|
+
acdc: td,
|
|
248
|
+
state: ts,
|
|
249
|
+
code,
|
|
250
|
+
});
|
|
251
|
+
if (candidate.d.nonce === said) {
|
|
252
|
+
return candidate;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Rebuild the matching bound blinded-state candidate if one exists.
|
|
260
|
+
*
|
|
261
|
+
* KERIpy correspondence:
|
|
262
|
+
* - mirrors `Blinder.unblind(..., bound=True)`
|
|
263
|
+
* - tries placeholder bound pairs automatically by including `(0, "")`
|
|
264
|
+
*
|
|
265
|
+
* Search strategy:
|
|
266
|
+
* - same placeholder search as `unblindBlindState(...)`
|
|
267
|
+
* - plus candidate `(bn, bd)` pairs, with placeholder `(0, "")` always added
|
|
268
|
+
*/
|
|
269
|
+
export function unblindBoundState({ said, uuid, raw, salt, sn = 1, tier, acdc = "", states = [], bounds = [], code, }) {
|
|
270
|
+
const resolvedUuid = ensurePresentUuid(uuid, { raw, salt, sn, tier });
|
|
271
|
+
const acdcs = [...new Set([coerceNonceText(acdc), ""])];
|
|
272
|
+
const stateTexts = [...new Set(states.map(coerceLabelText).concat(""))];
|
|
273
|
+
// Placeholder bound pairs matter for parity with KERIpy's partially revealed
|
|
274
|
+
// disclosure states, so the search space always includes `(0, "")`.
|
|
275
|
+
const normalizedBounds = [
|
|
276
|
+
...bounds.map(([bsn, bd]) => [coerceNumh(bsn), coerceNonceText(bd)]),
|
|
277
|
+
["0", ""],
|
|
278
|
+
];
|
|
279
|
+
const uniqueBounds = [
|
|
280
|
+
...new Map(normalizedBounds.map((bound) => [`${bound[0]}:${bound[1]}`, bound])).values(),
|
|
281
|
+
];
|
|
282
|
+
for (const [bn, bd] of uniqueBounds) {
|
|
283
|
+
for (const td of acdcs) {
|
|
284
|
+
for (const ts of stateTexts) {
|
|
285
|
+
const candidate = makeBoundState({
|
|
286
|
+
uuid: resolvedUuid,
|
|
287
|
+
acdc: td,
|
|
288
|
+
state: ts,
|
|
289
|
+
bsn: bn,
|
|
290
|
+
bd,
|
|
291
|
+
code,
|
|
292
|
+
});
|
|
293
|
+
if (candidate.d.nonce === said) {
|
|
294
|
+
return candidate;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
@@ -1,19 +1,142 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { bytesEqual } from "../core/bytes.js";
|
|
2
|
+
import { DeserializeError, UnknownCodeError } from "../core/errors.js";
|
|
3
|
+
import { normalizeByteLike } from "./byte-like.js";
|
|
4
|
+
import { Cipher } from "./cipher.js";
|
|
5
|
+
import { CIPHER_X25519_ALL_QB64_CODES, CIPHER_X25519_QB2_VARIABLE_CODES, CIPHER_X25519_VARIABLE_STREAM_CODES, ENCRYPTER_CODES, MtrDex, } from "./codex.js";
|
|
3
6
|
import { Matter } from "./matter.js";
|
|
7
|
+
import { isQualifiedPrimitive } from "./primitive.js";
|
|
8
|
+
import { Salter } from "./salter.js";
|
|
9
|
+
import { boxKeyPairFromEd25519Seed, boxPublicKeyFromEd25519Verfer, sealBox } from "./sealed-box.js";
|
|
10
|
+
import { Signer } from "./signer.js";
|
|
11
|
+
import { Streamer } from "./streamer.js";
|
|
12
|
+
import { Verfer } from "./verfer.js";
|
|
13
|
+
/** Normalize direct X25519 or derived-Ed25519 constructor forms into `Matter` input. */
|
|
14
|
+
function normalizeMatterInit(init) {
|
|
15
|
+
if (init instanceof Matter) {
|
|
16
|
+
return init;
|
|
17
|
+
}
|
|
18
|
+
const normalized = {
|
|
19
|
+
code: init.code ?? MtrDex.X25519,
|
|
20
|
+
};
|
|
21
|
+
if (init.raw) {
|
|
22
|
+
normalized.raw = normalizeByteLike(init.raw);
|
|
23
|
+
}
|
|
24
|
+
else if (init.verkey) {
|
|
25
|
+
const verfer = new Verfer({ qb64b: normalizeByteLike(init.verkey) });
|
|
26
|
+
if (verfer.code !== MtrDex.Ed25519
|
|
27
|
+
&& verfer.code !== MtrDex.Ed25519N) {
|
|
28
|
+
throw new DeserializeError(`Unsupported verkey derivation code = ${verfer.code}.`);
|
|
29
|
+
}
|
|
30
|
+
normalized.raw = boxPublicKeyFromEd25519Verfer(verfer.raw);
|
|
31
|
+
}
|
|
32
|
+
else if (init.qb64b) {
|
|
33
|
+
normalized.qb64b = normalizeByteLike(init.qb64b);
|
|
34
|
+
}
|
|
35
|
+
else if (init.qb64) {
|
|
36
|
+
const qb64 = normalizeByteLike(init.qb64);
|
|
37
|
+
normalized.qb64b = qb64;
|
|
38
|
+
}
|
|
39
|
+
else if (init.qb2) {
|
|
40
|
+
normalized.qb2 = normalizeByteLike(init.qb2);
|
|
41
|
+
}
|
|
42
|
+
return normalized;
|
|
43
|
+
}
|
|
4
44
|
/**
|
|
5
45
|
* Public-key encryption primitive for asymmetric envelope encryption.
|
|
6
46
|
*
|
|
7
|
-
* KERIpy substance: Encrypter
|
|
8
|
-
*
|
|
9
|
-
* This TS class validates material type; encryption ops live in crypto layers.
|
|
47
|
+
* KERIpy substance: `Encrypter` owns the public X25519 material and can derive
|
|
48
|
+
* it from Ed25519 verifier keys used by non-transferable/basic AIDs.
|
|
10
49
|
*/
|
|
11
50
|
export class Encrypter extends Matter {
|
|
51
|
+
/**
|
|
52
|
+
* Construct one X25519 public-key encrypter.
|
|
53
|
+
*
|
|
54
|
+
* Default code remains the KERIpy public-box family `MtrDex.X25519`.
|
|
55
|
+
*/
|
|
12
56
|
constructor(init) {
|
|
13
|
-
|
|
14
|
-
super(matter);
|
|
57
|
+
super(normalizeMatterInit(init));
|
|
15
58
|
if (!ENCRYPTER_CODES.has(this.code)) {
|
|
16
59
|
throw new UnknownCodeError(`Expected encrypter key code, got ${this.code}`);
|
|
17
60
|
}
|
|
18
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Confirm whether the supplied Ed25519 seed maps to this X25519 public key.
|
|
64
|
+
*
|
|
65
|
+
* Boundary contract:
|
|
66
|
+
* - only Ed25519 signer seeds are accepted here
|
|
67
|
+
* - the check proves that the seed belongs to the AEID/verkey that was used
|
|
68
|
+
* to derive this encrypter's public box key
|
|
69
|
+
*/
|
|
70
|
+
verifySeed(seed) {
|
|
71
|
+
const signer = new Signer({ qb64b: normalizeByteLike(seed) });
|
|
72
|
+
if (signer.code !== MtrDex.Ed25519_Seed) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return bytesEqual(boxKeyPairFromEd25519Seed(signer.raw).publicKey, this.raw);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Encrypt raw or primitive plaintext into one CESR `Cipher`.
|
|
79
|
+
*
|
|
80
|
+
* KERIpy parity:
|
|
81
|
+
* - `ser` wins when supplied
|
|
82
|
+
* - otherwise one primitive is required
|
|
83
|
+
* - omitted `code` defaults to stream-family `L0` only for raw `ser` input
|
|
84
|
+
*
|
|
85
|
+
* Plaintext-family rules:
|
|
86
|
+
* - `Salter` and `Signer` may infer fixed qb64 cipher families when `code`
|
|
87
|
+
* is omitted
|
|
88
|
+
* - other primitives require an explicit family so decrypt-time hydration
|
|
89
|
+
* knows whether the plaintext was stored as qb64, qb2, or a sniffable
|
|
90
|
+
* stream
|
|
91
|
+
*/
|
|
92
|
+
encrypt({ ser, prim, code } = {}) {
|
|
93
|
+
let plaintext = ser
|
|
94
|
+
? normalizeByteLike(ser)
|
|
95
|
+
: undefined;
|
|
96
|
+
let cipherCode = code;
|
|
97
|
+
if (!plaintext) {
|
|
98
|
+
if (!prim) {
|
|
99
|
+
throw new DeserializeError("Neither plaintext serialization nor primitive input was provided.");
|
|
100
|
+
}
|
|
101
|
+
if (!cipherCode) {
|
|
102
|
+
if (prim instanceof Salter) {
|
|
103
|
+
cipherCode = MtrDex.X25519_Cipher_Salt;
|
|
104
|
+
}
|
|
105
|
+
else if (prim instanceof Signer) {
|
|
106
|
+
cipherCode = MtrDex.X25519_Cipher_Seed;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
throw new DeserializeError(`Unsupported primitive ${prim instanceof Streamer ? "streamer" : `with code = ${prim.code}`} when cipher code is missing.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (CIPHER_X25519_ALL_QB64_CODES.has(cipherCode)) {
|
|
113
|
+
if (!isQualifiedPrimitive(prim)) {
|
|
114
|
+
throw new DeserializeError(`Invalid primitive cipher code = ${cipherCode} for stream primitive.`);
|
|
115
|
+
}
|
|
116
|
+
plaintext = prim.qb64b;
|
|
117
|
+
}
|
|
118
|
+
else if (CIPHER_X25519_QB2_VARIABLE_CODES.has(cipherCode)) {
|
|
119
|
+
if (!isQualifiedPrimitive(prim)) {
|
|
120
|
+
throw new DeserializeError(`Invalid primitive cipher code = ${cipherCode} for stream primitive.`);
|
|
121
|
+
}
|
|
122
|
+
plaintext = prim.qb2;
|
|
123
|
+
}
|
|
124
|
+
else if (CIPHER_X25519_VARIABLE_STREAM_CODES.has(cipherCode)) {
|
|
125
|
+
if (!(prim instanceof Streamer)) {
|
|
126
|
+
throw new DeserializeError(`Invalid primitive cipher code = ${cipherCode} for non-stream primitive.`);
|
|
127
|
+
}
|
|
128
|
+
plaintext = prim.stream;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
throw new UnknownCodeError(`Invalid primitive cipher code = ${cipherCode}.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!plaintext) {
|
|
135
|
+
throw new DeserializeError("Missing plaintext serialization for encryption.");
|
|
136
|
+
}
|
|
137
|
+
return new Cipher({
|
|
138
|
+
code: cipherCode ?? MtrDex.X25519_Cipher_L0,
|
|
139
|
+
raw: sealBox(plaintext, this.raw),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
19
142
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Cigar } from "./cigar.js";
|
|
2
|
+
import { CIGAR_CODES, DATER_CODES, DECIMAL_CODES, NONCE_CODES, SIGER_CODES } from "./codex.js";
|
|
3
|
+
import { Dater } from "./dater.js";
|
|
4
|
+
import { Decimer } from "./decimer.js";
|
|
5
|
+
import { Indexer, parseIndexer } from "./indexer.js";
|
|
6
|
+
import { Matter, parseMatter } from "./matter.js";
|
|
7
|
+
import { Noncer } from "./noncer.js";
|
|
8
|
+
import { Siger } from "./siger.js";
|
|
9
|
+
/**
|
|
10
|
+
* Hydrate one low-level `Matter` into the narrowest safe primitive class.
|
|
11
|
+
*
|
|
12
|
+
* Maintainer rule:
|
|
13
|
+
* this is intentionally conservative, not aggressive. Many CESR codes are
|
|
14
|
+
* semantically overloaded across families (`0A`, `B`, `E`, tag/label codes,
|
|
15
|
+
* numeric threshold codes, etc.), so generic callers should only get an
|
|
16
|
+
* automatic narrowing when the code family is genuinely unambiguous.
|
|
17
|
+
*/
|
|
18
|
+
export function hydrateMatter(init) {
|
|
19
|
+
const matter = init instanceof Matter ? init : new Matter(init);
|
|
20
|
+
if (DECIMAL_CODES.has(matter.code)) {
|
|
21
|
+
return new Decimer(matter);
|
|
22
|
+
}
|
|
23
|
+
if (DATER_CODES.has(matter.code)) {
|
|
24
|
+
return new Dater(matter);
|
|
25
|
+
}
|
|
26
|
+
if (CIGAR_CODES.has(matter.code)) {
|
|
27
|
+
return new Cigar(matter);
|
|
28
|
+
}
|
|
29
|
+
if (NONCE_CODES.has(matter.code)) {
|
|
30
|
+
return new Noncer(matter);
|
|
31
|
+
}
|
|
32
|
+
return matter;
|
|
33
|
+
}
|
|
34
|
+
/** Parse one text/binary matter token and hydrate it through `hydrateMatter()`. */
|
|
35
|
+
export function parseQualifiedMatter(input, cold) {
|
|
36
|
+
return hydrateMatter(parseMatter(input, cold));
|
|
37
|
+
}
|
|
38
|
+
/** Hydrate one low-level `Indexer` into the narrowest safe primitive class. */
|
|
39
|
+
export function hydrateIndexer(init) {
|
|
40
|
+
const indexer = init instanceof Indexer ? init : new Indexer(init);
|
|
41
|
+
if (SIGER_CODES.has(indexer.code)) {
|
|
42
|
+
return new Siger(indexer);
|
|
43
|
+
}
|
|
44
|
+
return indexer;
|
|
45
|
+
}
|
|
46
|
+
/** Parse one text/binary indexer token and hydrate it through `hydrateIndexer()`. */
|
|
47
|
+
export function parseQualifiedIndexer(input, cold) {
|
|
48
|
+
return hydrateIndexer(parseIndexer(input, cold));
|
|
49
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UnknownCodeError } from "../core/errors.js";
|
|
2
2
|
import { ILKER_CODES } from "./codex.js";
|
|
3
|
-
import {
|
|
3
|
+
import { parseMatter } from "./matter.js";
|
|
4
4
|
import { Tagger } from "./tagger.js";
|
|
5
5
|
/**
|
|
6
6
|
* Message-type primitive (`ilk`) encoded via Tagger compact tag semantics.
|
|
@@ -10,8 +10,7 @@ import { Tagger } from "./tagger.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export class Ilker extends Tagger {
|
|
12
12
|
constructor(init) {
|
|
13
|
-
|
|
14
|
-
super(matter);
|
|
13
|
+
super(init);
|
|
15
14
|
if (!ILKER_CODES.has(this.code)) {
|
|
16
15
|
throw new UnknownCodeError(`Expected ilker Tag3 code, got ${this.code}`);
|
|
17
16
|
}
|