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.
Files changed (204) hide show
  1. package/esm/src/adapters/async-iterable.js +8 -0
  2. package/esm/src/adapters/effection.js +6 -0
  3. package/esm/src/annotate/annotator.js +12 -0
  4. package/esm/src/annotate/cli.js +12 -0
  5. package/esm/src/annotate/comments.js +5 -1
  6. package/esm/src/annotate/denot.js +6 -0
  7. package/esm/src/annotate/render.js +15 -3
  8. package/esm/src/core/bytes.js +9 -0
  9. package/esm/src/core/errors.js +3 -0
  10. package/esm/src/core/parser-engine.js +1 -1
  11. package/esm/src/core/parser-frame-parser.js +22 -18
  12. package/esm/src/core/vocabulary.js +47 -0
  13. package/esm/src/index.js +5 -0
  14. package/esm/src/parser/attachment-parser.js +4 -0
  15. package/esm/src/parser/cold-start.js +6 -0
  16. package/esm/src/parser/group-dispatch.js +2 -2
  17. package/esm/src/primitives/aggor.js +374 -30
  18. package/esm/src/primitives/bexter.js +2 -3
  19. package/esm/src/primitives/blinder.js +6 -0
  20. package/esm/src/primitives/byte-like.js +19 -0
  21. package/esm/src/primitives/cigar.js +17 -3
  22. package/esm/src/primitives/cipher.js +91 -6
  23. package/esm/src/primitives/codex.js +39 -6
  24. package/esm/src/primitives/compactor.js +307 -3
  25. package/esm/src/primitives/counter.js +6 -2
  26. package/esm/src/primitives/dater.js +1 -2
  27. package/esm/src/primitives/decimer.js +7 -11
  28. package/esm/src/primitives/decrypter.js +123 -7
  29. package/esm/src/primitives/diger.js +57 -3
  30. package/esm/src/primitives/disclosure.js +300 -0
  31. package/esm/src/primitives/encrypter.js +130 -7
  32. package/esm/src/primitives/hydrate.js +49 -0
  33. package/esm/src/primitives/ilker.js +2 -3
  34. package/esm/src/primitives/indexer.js +21 -4
  35. package/esm/src/primitives/labeler.js +1 -2
  36. package/esm/src/primitives/mapper.js +612 -35
  37. package/esm/src/primitives/matter.js +133 -3
  38. package/esm/src/primitives/mediar.js +6 -0
  39. package/esm/src/primitives/noncer.js +2 -3
  40. package/esm/src/primitives/number.js +1 -2
  41. package/esm/src/primitives/pather.js +102 -5
  42. package/esm/src/primitives/prefixer.js +1 -2
  43. package/esm/src/primitives/primitive.js +14 -0
  44. package/esm/src/primitives/registry.js +2 -0
  45. package/esm/src/primitives/saider.js +80 -3
  46. package/esm/src/primitives/salter.js +96 -2
  47. package/esm/src/primitives/sealed-box.js +41 -0
  48. package/esm/src/primitives/seqner.js +1 -2
  49. package/esm/src/primitives/siger.js +58 -3
  50. package/esm/src/primitives/signer.js +243 -9
  51. package/esm/src/primitives/streamer.js +28 -0
  52. package/esm/src/primitives/structing.js +727 -0
  53. package/esm/src/primitives/structor.js +20 -0
  54. package/esm/src/primitives/tagger.js +1 -2
  55. package/esm/src/primitives/texter.js +1 -2
  56. package/esm/src/primitives/tholder.js +552 -10
  57. package/esm/src/primitives/traitor.js +2 -3
  58. package/esm/src/primitives/verfer.js +50 -6
  59. package/esm/src/primitives/verser.js +16 -6
  60. package/esm/src/router/router-stub.js +1 -0
  61. package/esm/src/serder/native.js +1326 -0
  62. package/esm/src/serder/serder.js +1558 -59
  63. package/esm/src/serder/serdery.js +92 -7
  64. package/esm/src/serder/smell.js +26 -1
  65. package/esm/src/tables/codex-utils.js +2 -0
  66. package/esm/src/tables/counter-codex.js +2 -0
  67. package/esm/src/tables/counter-groups.js +6 -0
  68. package/esm/src/tables/versions.js +4 -0
  69. package/esm/src/version.js +5 -2
  70. package/package.json +5 -2
  71. package/types/src/adapters/async-iterable.d.ts +8 -0
  72. package/types/src/adapters/async-iterable.d.ts.map +1 -1
  73. package/types/src/adapters/effection.d.ts +7 -0
  74. package/types/src/adapters/effection.d.ts.map +1 -1
  75. package/types/src/annotate/annotator.d.ts +12 -0
  76. package/types/src/annotate/annotator.d.ts.map +1 -1
  77. package/types/src/annotate/cli.d.ts +13 -0
  78. package/types/src/annotate/cli.d.ts.map +1 -1
  79. package/types/src/annotate/comments.d.ts +4 -0
  80. package/types/src/annotate/comments.d.ts.map +1 -1
  81. package/types/src/annotate/denot.d.ts +6 -0
  82. package/types/src/annotate/denot.d.ts.map +1 -1
  83. package/types/src/annotate/render.d.ts.map +1 -1
  84. package/types/src/annotate/types.d.ts +2 -0
  85. package/types/src/annotate/types.d.ts.map +1 -1
  86. package/types/src/bench/parser-benchmark.d.ts +3 -0
  87. package/types/src/bench/parser-benchmark.d.ts.map +1 -1
  88. package/types/src/core/bytes.d.ts +9 -0
  89. package/types/src/core/bytes.d.ts.map +1 -1
  90. package/types/src/core/errors.d.ts +3 -0
  91. package/types/src/core/errors.d.ts.map +1 -1
  92. package/types/src/core/parser-engine.d.ts +4 -0
  93. package/types/src/core/parser-engine.d.ts.map +1 -1
  94. package/types/src/core/parser-frame-parser.d.ts.map +1 -1
  95. package/types/src/core/types.d.ts +2 -0
  96. package/types/src/core/types.d.ts.map +1 -1
  97. package/types/src/core/vocabulary.d.ts +48 -0
  98. package/types/src/core/vocabulary.d.ts.map +1 -0
  99. package/types/src/index.d.ts +5 -0
  100. package/types/src/index.d.ts.map +1 -1
  101. package/types/src/parser/attachment-parser.d.ts +4 -0
  102. package/types/src/parser/attachment-parser.d.ts.map +1 -1
  103. package/types/src/parser/cold-start.d.ts +6 -0
  104. package/types/src/parser/cold-start.d.ts.map +1 -1
  105. package/types/src/parser/group-dispatch.d.ts.map +1 -1
  106. package/types/src/primitives/aggor.d.ts +100 -13
  107. package/types/src/primitives/aggor.d.ts.map +1 -1
  108. package/types/src/primitives/bexter.d.ts.map +1 -1
  109. package/types/src/primitives/blinder.d.ts +6 -0
  110. package/types/src/primitives/blinder.d.ts.map +1 -1
  111. package/types/src/primitives/byte-like.d.ts +7 -0
  112. package/types/src/primitives/byte-like.d.ts.map +1 -0
  113. package/types/src/primitives/cigar.d.ts +11 -1
  114. package/types/src/primitives/cigar.d.ts.map +1 -1
  115. package/types/src/primitives/cipher.d.ts +73 -2
  116. package/types/src/primitives/cipher.d.ts.map +1 -1
  117. package/types/src/primitives/codex.d.ts +34 -4
  118. package/types/src/primitives/codex.d.ts.map +1 -1
  119. package/types/src/primitives/compactor.d.ts +94 -2
  120. package/types/src/primitives/compactor.d.ts.map +1 -1
  121. package/types/src/primitives/counter.d.ts.map +1 -1
  122. package/types/src/primitives/dater.d.ts.map +1 -1
  123. package/types/src/primitives/decimer.d.ts.map +1 -1
  124. package/types/src/primitives/decrypter.d.ts +64 -3
  125. package/types/src/primitives/decrypter.d.ts.map +1 -1
  126. package/types/src/primitives/diger.d.ts +16 -0
  127. package/types/src/primitives/diger.d.ts.map +1 -1
  128. package/types/src/primitives/disclosure.d.ts +201 -0
  129. package/types/src/primitives/disclosure.d.ts.map +1 -0
  130. package/types/src/primitives/encrypter.d.ts +66 -4
  131. package/types/src/primitives/encrypter.d.ts.map +1 -1
  132. package/types/src/primitives/hydrate.d.ts +20 -0
  133. package/types/src/primitives/hydrate.d.ts.map +1 -0
  134. package/types/src/primitives/ilker.d.ts.map +1 -1
  135. package/types/src/primitives/indexer.d.ts.map +1 -1
  136. package/types/src/primitives/labeler.d.ts.map +1 -1
  137. package/types/src/primitives/mapper.d.ts +132 -11
  138. package/types/src/primitives/mapper.d.ts.map +1 -1
  139. package/types/src/primitives/matter.d.ts +33 -1
  140. package/types/src/primitives/matter.d.ts.map +1 -1
  141. package/types/src/primitives/mediar.d.ts +6 -0
  142. package/types/src/primitives/mediar.d.ts.map +1 -1
  143. package/types/src/primitives/noncer.d.ts.map +1 -1
  144. package/types/src/primitives/number.d.ts.map +1 -1
  145. package/types/src/primitives/pather.d.ts +21 -0
  146. package/types/src/primitives/pather.d.ts.map +1 -1
  147. package/types/src/primitives/prefixer.d.ts.map +1 -1
  148. package/types/src/primitives/primitive.d.ts +26 -0
  149. package/types/src/primitives/primitive.d.ts.map +1 -1
  150. package/types/src/primitives/registry.d.ts +3 -0
  151. package/types/src/primitives/registry.d.ts.map +1 -1
  152. package/types/src/primitives/saider.d.ts +41 -0
  153. package/types/src/primitives/saider.d.ts.map +1 -1
  154. package/types/src/primitives/salter.d.ts +56 -1
  155. package/types/src/primitives/salter.d.ts.map +1 -1
  156. package/types/src/primitives/sealed-box.d.ts +29 -0
  157. package/types/src/primitives/sealed-box.d.ts.map +1 -0
  158. package/types/src/primitives/seqner.d.ts.map +1 -1
  159. package/types/src/primitives/siger.d.ts +14 -0
  160. package/types/src/primitives/siger.d.ts.map +1 -1
  161. package/types/src/primitives/signer.d.ts +80 -4
  162. package/types/src/primitives/signer.d.ts.map +1 -1
  163. package/types/src/primitives/streamer.d.ts +31 -0
  164. package/types/src/primitives/streamer.d.ts.map +1 -0
  165. package/types/src/primitives/structing.d.ts +4114 -0
  166. package/types/src/primitives/structing.d.ts.map +1 -0
  167. package/types/src/primitives/structor.d.ts +6 -0
  168. package/types/src/primitives/structor.d.ts.map +1 -1
  169. package/types/src/primitives/tagger.d.ts.map +1 -1
  170. package/types/src/primitives/texter.d.ts.map +1 -1
  171. package/types/src/primitives/tholder.d.ts +150 -5
  172. package/types/src/primitives/tholder.d.ts.map +1 -1
  173. package/types/src/primitives/traitor.d.ts.map +1 -1
  174. package/types/src/primitives/unknown.d.ts +1 -0
  175. package/types/src/primitives/unknown.d.ts.map +1 -1
  176. package/types/src/primitives/verfer.d.ts +12 -0
  177. package/types/src/primitives/verfer.d.ts.map +1 -1
  178. package/types/src/primitives/verser.d.ts +14 -3
  179. package/types/src/primitives/verser.d.ts.map +1 -1
  180. package/types/src/router/router-stub.d.ts +3 -0
  181. package/types/src/router/router-stub.d.ts.map +1 -1
  182. package/types/src/serder/native.d.ts +115 -0
  183. package/types/src/serder/native.d.ts.map +1 -0
  184. package/types/src/serder/serder.d.ts +187 -9
  185. package/types/src/serder/serder.d.ts.map +1 -1
  186. package/types/src/serder/serdery.d.ts +42 -0
  187. package/types/src/serder/serdery.d.ts.map +1 -1
  188. package/types/src/serder/smell.d.ts +22 -0
  189. package/types/src/serder/smell.d.ts.map +1 -1
  190. package/types/src/tables/codex-utils.d.ts +3 -0
  191. package/types/src/tables/codex-utils.d.ts.map +1 -1
  192. package/types/src/tables/counter-codex.d.ts +4 -0
  193. package/types/src/tables/counter-codex.d.ts.map +1 -1
  194. package/types/src/tables/counter-groups.d.ts +6 -0
  195. package/types/src/tables/counter-groups.d.ts.map +1 -1
  196. package/types/src/tables/counter.tables.generated.d.ts.map +1 -1
  197. package/types/src/tables/indexer.codex.generated.d.ts.map +1 -1
  198. package/types/src/tables/matter.codex.generated.d.ts.map +1 -1
  199. package/types/src/tables/table-types.d.ts +6 -0
  200. package/types/src/tables/table-types.d.ts.map +1 -1
  201. package/types/src/tables/versions.d.ts +6 -0
  202. package/types/src/tables/versions.d.ts.map +1 -1
  203. package/types/src/version.d.ts +5 -2
  204. 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
- const matter = init instanceof Matter ? init : new Matter(init);
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 { UnknownCodeError } from "../core/errors.js";
2
- import { ENCRYPTER_CODES } from "./codex.js";
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 uses public key material (typically X25519,
8
- * optionally derived from verifier keys) to produce ciphertext envelopes.
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
- const matter = init instanceof Matter ? init : new Matter(init);
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 { Matter, parseMatter } from "./matter.js";
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
- const matter = init instanceof Matter ? init : new Matter(init);
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
  }