@real-life/trust-protocol 0.3.1 → 0.4.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 (264) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +187 -78
  3. package/dist/ack.d.ts +84 -0
  4. package/dist/ack.d.ts.map +1 -0
  5. package/dist/ack.js +114 -0
  6. package/dist/ack.js.map +1 -0
  7. package/dist/adapters/vti-mediator.d.ts +154 -0
  8. package/dist/adapters/vti-mediator.d.ts.map +1 -0
  9. package/dist/adapters/vti-mediator.js +621 -0
  10. package/dist/adapters/vti-mediator.js.map +1 -0
  11. package/dist/anchor.d.ts +226 -0
  12. package/dist/anchor.d.ts.map +1 -0
  13. package/dist/anchor.js +347 -0
  14. package/dist/anchor.js.map +1 -0
  15. package/dist/{holder.d.ts → carrier-holder.d.ts} +1 -1
  16. package/dist/carrier-holder.d.ts.map +1 -0
  17. package/dist/{holder.js → carrier-holder.js} +5 -4
  18. package/dist/carrier-holder.js.map +1 -0
  19. package/dist/carrier-identity.js +4 -4
  20. package/dist/carrier-identity.js.map +1 -1
  21. package/dist/carrier.d.ts.map +1 -1
  22. package/dist/carrier.js +4 -3
  23. package/dist/carrier.js.map +1 -1
  24. package/dist/ceremony.d.ts +3 -2
  25. package/dist/ceremony.d.ts.map +1 -1
  26. package/dist/ceremony.js +16 -26
  27. package/dist/ceremony.js.map +1 -1
  28. package/dist/codec.d.ts +46 -0
  29. package/dist/codec.d.ts.map +1 -0
  30. package/dist/codec.js +410 -0
  31. package/dist/codec.js.map +1 -0
  32. package/dist/continuity.d.ts +67 -0
  33. package/dist/continuity.d.ts.map +1 -0
  34. package/dist/continuity.js +110 -0
  35. package/dist/continuity.js.map +1 -0
  36. package/dist/core.d.ts +4 -0
  37. package/dist/core.d.ts.map +1 -1
  38. package/dist/core.js.map +1 -1
  39. package/dist/crypto.d.ts +23 -7
  40. package/dist/crypto.d.ts.map +1 -1
  41. package/dist/crypto.js +54 -20
  42. package/dist/crypto.js.map +1 -1
  43. package/dist/delivery.d.ts +31 -3
  44. package/dist/delivery.d.ts.map +1 -1
  45. package/dist/delivery.js +75 -28
  46. package/dist/delivery.js.map +1 -1
  47. package/dist/encounter/ceremony.d.ts +37 -0
  48. package/dist/encounter/ceremony.d.ts.map +1 -0
  49. package/dist/encounter/ceremony.js +662 -0
  50. package/dist/encounter/ceremony.js.map +1 -0
  51. package/dist/encounter.d.ts +91 -1
  52. package/dist/encounter.d.ts.map +1 -1
  53. package/dist/encounter.js +191 -12
  54. package/dist/encounter.js.map +1 -1
  55. package/dist/engine.d.ts +291 -0
  56. package/dist/engine.d.ts.map +1 -0
  57. package/dist/engine.js +1395 -0
  58. package/dist/engine.js.map +1 -0
  59. package/dist/entropy.d.ts +28 -0
  60. package/dist/entropy.d.ts.map +1 -0
  61. package/dist/entropy.js +96 -0
  62. package/dist/entropy.js.map +1 -0
  63. package/dist/events.d.ts +177 -0
  64. package/dist/events.d.ts.map +1 -0
  65. package/dist/events.js +2 -0
  66. package/dist/events.js.map +1 -0
  67. package/dist/fixtures.d.ts +30 -0
  68. package/dist/fixtures.d.ts.map +1 -0
  69. package/dist/fixtures.js +155 -0
  70. package/dist/fixtures.js.map +1 -0
  71. package/dist/forge.d.ts +47 -0
  72. package/dist/forge.d.ts.map +1 -0
  73. package/dist/forge.js +20 -0
  74. package/dist/forge.js.map +1 -0
  75. package/dist/group-star.d.ts +251 -0
  76. package/dist/group-star.d.ts.map +1 -0
  77. package/dist/group-star.js +348 -0
  78. package/dist/group-star.js.map +1 -0
  79. package/dist/holder-api.d.ts +137 -0
  80. package/dist/holder-api.d.ts.map +1 -0
  81. package/dist/holder-api.js +132 -0
  82. package/dist/holder-api.js.map +1 -0
  83. package/dist/holder-state-v1.d.ts +801 -0
  84. package/dist/holder-state-v1.d.ts.map +1 -0
  85. package/dist/holder-state-v1.js +2 -0
  86. package/dist/holder-state-v1.js.map +1 -0
  87. package/dist/identity-state.d.ts +43 -0
  88. package/dist/identity-state.d.ts.map +1 -0
  89. package/dist/identity-state.js +345 -0
  90. package/dist/identity-state.js.map +1 -0
  91. package/dist/identity.d.ts +24 -2
  92. package/dist/identity.d.ts.map +1 -1
  93. package/dist/identity.js +70 -11
  94. package/dist/identity.js.map +1 -1
  95. package/dist/index.d.ts +26 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +77 -29
  98. package/dist/index.js.map +1 -1
  99. package/dist/internal.d.ts +36 -0
  100. package/dist/internal.d.ts.map +1 -0
  101. package/dist/internal.js +22 -0
  102. package/dist/internal.js.map +1 -0
  103. package/dist/introduction/holder.d.ts +18 -0
  104. package/dist/introduction/holder.d.ts.map +1 -0
  105. package/dist/introduction/holder.js +757 -0
  106. package/dist/introduction/holder.js.map +1 -0
  107. package/dist/introduction.d.ts +101 -0
  108. package/dist/introduction.d.ts.map +1 -0
  109. package/dist/introduction.js +132 -0
  110. package/dist/introduction.js.map +1 -0
  111. package/dist/invariants.d.ts +43 -0
  112. package/dist/invariants.d.ts.map +1 -0
  113. package/dist/invariants.js +883 -0
  114. package/dist/invariants.js.map +1 -0
  115. package/dist/keys-seed.d.ts +7 -0
  116. package/dist/keys-seed.d.ts.map +1 -0
  117. package/dist/keys-seed.js +133 -0
  118. package/dist/keys-seed.js.map +1 -0
  119. package/dist/log-memory.d.ts +12 -0
  120. package/dist/log-memory.d.ts.map +1 -0
  121. package/dist/log-memory.js +101 -0
  122. package/dist/log-memory.js.map +1 -0
  123. package/dist/membership/holder.d.ts +35 -0
  124. package/dist/membership/holder.d.ts.map +1 -0
  125. package/dist/membership/holder.js +852 -0
  126. package/dist/membership/holder.js.map +1 -0
  127. package/dist/membership.d.ts +92 -0
  128. package/dist/membership.d.ts.map +1 -0
  129. package/dist/membership.js +157 -0
  130. package/dist/membership.js.map +1 -0
  131. package/dist/ports.d.ts +237 -0
  132. package/dist/ports.d.ts.map +1 -0
  133. package/dist/ports.js +2 -0
  134. package/dist/ports.js.map +1 -0
  135. package/dist/profile.d.ts +56 -0
  136. package/dist/profile.d.ts.map +1 -0
  137. package/dist/profile.js +193 -0
  138. package/dist/profile.js.map +1 -0
  139. package/dist/prompts.d.ts +14 -0
  140. package/dist/prompts.d.ts.map +1 -0
  141. package/dist/prompts.js +69 -0
  142. package/dist/prompts.js.map +1 -0
  143. package/dist/receive.d.ts +133 -0
  144. package/dist/receive.d.ts.map +1 -0
  145. package/dist/receive.js +298 -0
  146. package/dist/receive.js.map +1 -0
  147. package/dist/results.d.ts +158 -0
  148. package/dist/results.d.ts.map +1 -0
  149. package/dist/results.js +21 -0
  150. package/dist/results.js.map +1 -0
  151. package/dist/schemas.d.ts.map +1 -1
  152. package/dist/schemas.js +42 -38
  153. package/dist/schemas.js.map +1 -1
  154. package/dist/sha256.d.ts +3 -0
  155. package/dist/sha256.d.ts.map +1 -0
  156. package/dist/sha256.js +69 -0
  157. package/dist/sha256.js.map +1 -0
  158. package/dist/star.d.ts +31 -0
  159. package/dist/star.d.ts.map +1 -0
  160. package/dist/star.js +56 -0
  161. package/dist/star.js.map +1 -0
  162. package/dist/state-builder.d.ts +42 -0
  163. package/dist/state-builder.d.ts.map +1 -0
  164. package/dist/state-builder.js +100 -0
  165. package/dist/state-builder.js.map +1 -0
  166. package/dist/state.d.ts +853 -0
  167. package/dist/state.d.ts.map +1 -0
  168. package/dist/state.js +36 -0
  169. package/dist/state.js.map +1 -0
  170. package/dist/store-contract.d.ts +11 -0
  171. package/dist/store-contract.d.ts.map +1 -0
  172. package/dist/store-contract.js +189 -0
  173. package/dist/store-contract.js.map +1 -0
  174. package/dist/store-memory.d.ts +8 -0
  175. package/dist/store-memory.d.ts.map +1 -0
  176. package/dist/store-memory.js +17 -0
  177. package/dist/store-memory.js.map +1 -0
  178. package/dist/testing.d.ts +22 -0
  179. package/dist/testing.d.ts.map +1 -0
  180. package/dist/testing.js +21 -0
  181. package/dist/testing.js.map +1 -0
  182. package/dist/trust/continuity.d.ts +71 -0
  183. package/dist/trust/continuity.d.ts.map +1 -0
  184. package/dist/trust/continuity.js +1216 -0
  185. package/dist/trust/continuity.js.map +1 -0
  186. package/dist/trust/duties.d.ts +67 -0
  187. package/dist/trust/duties.d.ts.map +1 -0
  188. package/dist/trust/duties.js +179 -0
  189. package/dist/trust/duties.js.map +1 -0
  190. package/dist/trust/entries.d.ts +139 -0
  191. package/dist/trust/entries.d.ts.map +1 -0
  192. package/dist/trust/entries.js +694 -0
  193. package/dist/trust/entries.js.map +1 -0
  194. package/dist/trust/group-star.d.ts +43 -0
  195. package/dist/trust/group-star.d.ts.map +1 -0
  196. package/dist/trust/group-star.js +188 -0
  197. package/dist/trust/group-star.js.map +1 -0
  198. package/dist/trust/mapping.d.ts +56 -0
  199. package/dist/trust/mapping.d.ts.map +1 -0
  200. package/dist/trust/mapping.js +584 -0
  201. package/dist/trust/mapping.js.map +1 -0
  202. package/dist/trust/profile.d.ts +25 -0
  203. package/dist/trust/profile.d.ts.map +1 -0
  204. package/dist/trust/profile.js +249 -0
  205. package/dist/trust/profile.js.map +1 -0
  206. package/dist/trust/star.d.ts +208 -0
  207. package/dist/trust/star.d.ts.map +1 -0
  208. package/dist/trust/star.js +588 -0
  209. package/dist/trust/star.js.map +1 -0
  210. package/dist/trust/state.d.ts +83 -0
  211. package/dist/trust/state.d.ts.map +1 -0
  212. package/dist/trust/state.js +154 -0
  213. package/dist/trust/state.js.map +1 -0
  214. package/dist/util-freeze.d.ts +3 -0
  215. package/dist/util-freeze.d.ts.map +1 -0
  216. package/dist/util-freeze.js +11 -0
  217. package/dist/util-freeze.js.map +1 -0
  218. package/dist/views.d.ts +147 -0
  219. package/dist/views.d.ts.map +1 -0
  220. package/dist/views.js +268 -0
  221. package/dist/views.js.map +1 -0
  222. package/dist/visibility.d.ts +5 -5
  223. package/dist/visibility.d.ts.map +1 -1
  224. package/dist/visibility.js +22 -28
  225. package/dist/visibility.js.map +1 -1
  226. package/dist/wire.d.ts +178 -84
  227. package/dist/wire.d.ts.map +1 -1
  228. package/dist/wire.js.map +1 -1
  229. package/package.json +58 -11
  230. package/schemas/holder-state-v1.schema.json +3880 -0
  231. package/dist/holder.d.ts.map +0 -1
  232. package/dist/holder.js.map +0 -1
  233. package/dist/probe/acks.d.ts +0 -27
  234. package/dist/probe/acks.d.ts.map +0 -1
  235. package/dist/probe/acks.js +0 -152
  236. package/dist/probe/acks.js.map +0 -1
  237. package/dist/probe/continuity.d.ts +0 -20
  238. package/dist/probe/continuity.d.ts.map +0 -1
  239. package/dist/probe/continuity.js +0 -805
  240. package/dist/probe/continuity.js.map +0 -1
  241. package/dist/probe/deps.d.ts +0 -61
  242. package/dist/probe/deps.d.ts.map +0 -1
  243. package/dist/probe/deps.js +0 -54
  244. package/dist/probe/deps.js.map +0 -1
  245. package/dist/probe/encounter.d.ts +0 -26
  246. package/dist/probe/encounter.d.ts.map +0 -1
  247. package/dist/probe/encounter.js +0 -938
  248. package/dist/probe/encounter.js.map +0 -1
  249. package/dist/probe/introduce.d.ts +0 -70
  250. package/dist/probe/introduce.d.ts.map +0 -1
  251. package/dist/probe/introduce.js +0 -295
  252. package/dist/probe/introduce.js.map +0 -1
  253. package/dist/probe/membership.d.ts +0 -90
  254. package/dist/probe/membership.d.ts.map +0 -1
  255. package/dist/probe/membership.js +0 -450
  256. package/dist/probe/membership.js.map +0 -1
  257. package/dist/probe/trust.d.ts +0 -156
  258. package/dist/probe/trust.d.ts.map +0 -1
  259. package/dist/probe/trust.js +0 -1233
  260. package/dist/probe/trust.js.map +0 -1
  261. package/dist/probe.d.ts +0 -7
  262. package/dist/probe.d.ts.map +0 -1
  263. package/dist/probe.js +0 -42
  264. package/dist/probe.js.map +0 -1
@@ -0,0 +1,251 @@
1
+ import type { Context } from './identity.js';
2
+ /** Entries are padded to the next positive multiple of this (§5.2b). */
3
+ export declare const GROUP_STAR_PAD = 16;
4
+ /**
5
+ * Chunks hold at most 64 entries (§5.2b): a real entry is about 570
6
+ * bytes JCS, so a full chunk stays well inside the Delivery Contract's
7
+ * 65 536-byte plaintext bound at the longest salt.
8
+ */
9
+ export declare const GROUP_STAR_CHUNK = 64;
10
+ /** The kinds of randomness a group star draws: AEAD nonces of sealed
11
+ * pairs, and filler (d and c of filler entries, c of unsealed ones). */
12
+ export type GroupStarRandKind = 'nonce' | 'filler';
13
+ /** The caller's entropy: n fresh bytes of one kind. */
14
+ export type GroupStarDraw = (n: number, kind: GroupStarRandKind) => Uint8Array;
15
+ /** k_g of one direction from the pair secret: `sender` and `recipient` are the two pair anchors, salt in its wire string form. */
16
+ export declare function groupStarKeyOf(shared: Uint8Array, sender: string, recipient: string, salt: string, subtle?: SubtleCrypto): Promise<Uint8Array>;
17
+ /** k_e(G) from the pair secret: the sealing key of one group's entry; G is the canonical u form of the genesis digest. */
18
+ export declare function groupSealKeyOf(shared: Uint8Array, sender: string, recipient: string, salt: string, group: string, subtle?: SubtleCrypto): Promise<Uint8Array>;
19
+ export interface GroupPairFields {
20
+ /** the genesis digest G, canonical u form */
21
+ group: string;
22
+ /** the sender's member anchor in G (for a founder: its founding pair anchor) */
23
+ member: string;
24
+ /** oid: of the canonical admission of member, or of the genesis where the sender founded G */
25
+ memberOp: string;
26
+ to: string;
27
+ salt: string;
28
+ }
29
+ /** The opened body of a sealed entry; `memberShared` = X25519(memberX_sender, pairX_recipient). */
30
+ export declare function buildGroupPairWith(i: GroupPairFields, memberShared: Uint8Array, subtle?: SubtleCrypto): Promise<{
31
+ proof: string;
32
+ type: string;
33
+ group: string;
34
+ member: string;
35
+ memberOp: string;
36
+ to: string;
37
+ salt: string;
38
+ }>;
39
+ export interface GroupStarEntryInput {
40
+ group: string;
41
+ /** a real sealed pair; otherwise c is filler */
42
+ sealed: boolean;
43
+ /** the sender's member anchor in G (sealed only) */
44
+ member?: string;
45
+ memberOp?: string;
46
+ /** X25519(memberX_sender, pairX_recipient), asked for only when sealed */
47
+ memberShared?: () => Promise<Uint8Array>;
48
+ }
49
+ export interface GroupStarWithInput {
50
+ /** X25519(pairX_sender, pairX_recipient) */
51
+ shared: Uint8Array;
52
+ /** the sender's pair anchor */
53
+ sender: string;
54
+ /** the recipient's pair anchor */
55
+ to: string;
56
+ salt: string;
57
+ /** every group the sender lists toward this recipient */
58
+ groups: GroupStarEntryInput[];
59
+ draw: GroupStarDraw;
60
+ subtle?: SubtleCrypto;
61
+ }
62
+ export type GroupStarChunk = {
63
+ body: {
64
+ type: 'group-star@1';
65
+ salt: string;
66
+ seq: string;
67
+ last: boolean;
68
+ groups: {
69
+ d: string;
70
+ c: string;
71
+ }[];
72
+ };
73
+ proof: {
74
+ mac: string;
75
+ };
76
+ };
77
+ /** The chunks of one group-star delivery toward one recipient. */
78
+ export declare function buildGroupStarWith(i: GroupStarWithInput): Promise<{
79
+ chunks: GroupStarChunk[];
80
+ }>;
81
+ /** Is this a schema-valid group-star@1 chunk (visibility-group-star.schema.json)? */
82
+ export declare const groupStarChunkOk: (chunk: unknown) => boolean;
83
+ /** The §5.2b checks of a completed union: a positive multiple of 16
84
+ * entries, strictly ascending by d, every c of the same length. */
85
+ export declare function groupStarUnionOk(entries: readonly {
86
+ d: string;
87
+ c: string;
88
+ }[]): 'order' | 'size' | 'c length' | null;
89
+ /**
90
+ * One delivery's chunks → its entry union (5.2a under the group star's
91
+ * own salt): every chunk schema-valid and MAC-valid under k_g of the
92
+ * arrival tuple, one salt, seq 1..n each once, exactly the n-th last,
93
+ * the union strictly ascending by d — and the §5.2b reception rules on
94
+ * the completed assembly: a positive multiple of 16 entries, every c of
95
+ * the same length. A failing assembly rejects the delivery.
96
+ */
97
+ export declare function assembleGroupStarWith(i: {
98
+ shared: () => Promise<Uint8Array>;
99
+ from: string;
100
+ own: string;
101
+ chunks: any[];
102
+ subtle?: SubtleCrypto;
103
+ }): Promise<{
104
+ ok: true;
105
+ salt: string;
106
+ entries: {
107
+ d: string;
108
+ c: string;
109
+ }[];
110
+ } | {
111
+ ok: false;
112
+ reason: string;
113
+ }>;
114
+ /** A group the recipient is a CURRENT member of, with its materialized state. */
115
+ export interface GroupMembership {
116
+ group: string;
117
+ /** is this anchor a current member of the recipient's materialized state of the group? */
118
+ isMember: (anchor: string) => boolean;
119
+ /** the canonical admission (or the genesis) an oid: names: its subject (genesis: body.members[0]) and its card's key-agreement key */
120
+ resolve: (oid: string) => {
121
+ subject: string;
122
+ keyAgreement: string;
123
+ } | null;
124
+ }
125
+ export interface GroupStarResult {
126
+ group: string;
127
+ hit: boolean;
128
+ opened: boolean;
129
+ accepted: boolean;
130
+ member?: string;
131
+ memberOp?: string;
132
+ reason?: string;
133
+ }
134
+ /**
135
+ * Reception (§5.2b): for each group the recipient is a current member
136
+ * of, compute d and test the union; on a hit derive k_e(G) and try to
137
+ * open c — a c that does not open is a miss, not an error. An opened
138
+ * pair is accepted only if, in order: schema · group = G · to = the
139
+ * own pair anchor and salt = the assembly's · memberOp names a
140
+ * canonical admission of member (or the genesis) and member is current
141
+ * · the MAC verifies under that operation's card key, non-zero ECDH
142
+ * (`agreeMember(cardKeyAgreement)` = X25519(own pairX, that key), null
143
+ * where it cannot agree). An accepted pair is class V: merged
144
+ * holder-locally, never republished.
145
+ */
146
+ export declare function openGroupStarWith(i: {
147
+ shared: Uint8Array;
148
+ from: string;
149
+ own: string;
150
+ salt: string;
151
+ entries: {
152
+ d: string;
153
+ c: string;
154
+ }[];
155
+ memberships: GroupMembership[];
156
+ agreeMember: (keyAgreement: string) => Promise<Uint8Array | null>;
157
+ subtle?: SubtleCrypto;
158
+ }): Promise<GroupStarResult[]>;
159
+ /** k_g of one direction: `sender` and `recipient` are the two pair anchors, salt in its wire string form. */
160
+ export declare function groupStarKey(own: Context, peerKeyAgreement: string, sender: string, recipient: string, salt: string): Promise<Uint8Array>;
161
+ /** k_e(G): the sealing key of one group's entry; G is the canonical u form of the genesis digest. */
162
+ export declare function groupSealKey(own: Context, peerKeyAgreement: string, sender: string, recipient: string, salt: string, group: string): Promise<Uint8Array>;
163
+ export interface GroupPairInput {
164
+ /** the genesis digest G, canonical u form */
165
+ group: string;
166
+ /** the sender's member context in G (for a founder: its founding pair context) */
167
+ member: Context;
168
+ /** oid: of the canonical admission of member, or of the genesis where the sender founded G */
169
+ memberOp: string;
170
+ to: string;
171
+ toKeyAgreement: string;
172
+ salt: string;
173
+ }
174
+ /** The opened body of a sealed entry; proof under memberX × pairX of the recipient. */
175
+ export declare function buildGroupPair(i: GroupPairInput): Promise<{
176
+ proof: string;
177
+ type: string;
178
+ group: string;
179
+ member: string;
180
+ memberOp: string;
181
+ to: string;
182
+ salt: string;
183
+ }>;
184
+ export interface GroupStarGroup {
185
+ group: string;
186
+ /** a real sealed pair (the recipient is trusted, or this group is switched on for it); otherwise c is filler */
187
+ sealed: boolean;
188
+ member?: Context;
189
+ memberOp?: string;
190
+ }
191
+ export interface GroupStarInput {
192
+ /** the sender's pair context of the relationship */
193
+ own: Context;
194
+ to: string;
195
+ toKeyAgreement: string;
196
+ salt: string;
197
+ /** every group the sender lists toward this recipient (its current groups not switched off) */
198
+ groups: GroupStarGroup[];
199
+ /** the entropy source (nonces, filler): the caller's, so vectors replay */
200
+ rand: (n: number) => Uint8Array;
201
+ }
202
+ /** The chunks of one group-star delivery toward one recipient. */
203
+ export declare function buildGroupStar(i: GroupStarInput): Promise<{
204
+ chunks: GroupStarChunk[];
205
+ }>;
206
+ /**
207
+ * One delivery's chunks → its entry union (5.2a under the group star's
208
+ * own salt): every chunk schema-valid and MAC-valid under k_g of the
209
+ * arrival tuple, one salt, seq 1..n each once, exactly the n-th last,
210
+ * the union strictly ascending by d — and the §5.2b reception rules on
211
+ * the completed assembly: a positive multiple of 16 entries, every c of
212
+ * the same length. A failing assembly rejects the delivery.
213
+ */
214
+ export declare function assembleGroupStar(i: {
215
+ own: Context;
216
+ from: string;
217
+ fromKeyAgreement: string;
218
+ chunks: any[];
219
+ }): Promise<{
220
+ ok: true;
221
+ salt: string;
222
+ entries: {
223
+ d: string;
224
+ c: string;
225
+ }[];
226
+ } | {
227
+ ok: false;
228
+ reason: string;
229
+ }>;
230
+ /**
231
+ * Reception (§5.2b): for each group the recipient is a current member
232
+ * of, compute d and test the union; on a hit derive k_e(G) and try to
233
+ * open c — a c that does not open is a miss, not an error. An opened
234
+ * pair is accepted only if, in order: schema · group = G · to = the
235
+ * own pair anchor and salt = the assembly's · memberOp names a
236
+ * canonical admission of member (or the genesis) and member is current
237
+ * · the MAC verifies under that operation's card key, non-zero ECDH.
238
+ * An accepted pair is class V: merged holder-locally, never republished.
239
+ */
240
+ export declare function openGroupStar(i: {
241
+ own: Context;
242
+ from: string;
243
+ fromKeyAgreement: string;
244
+ salt: string;
245
+ entries: {
246
+ d: string;
247
+ c: string;
248
+ }[];
249
+ memberships: GroupMembership[];
250
+ }): Promise<GroupStarResult[]>;
251
+ //# sourceMappingURL=group-star.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-star.d.ts","sourceRoot":"","sources":["../src/group-star.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,348 @@
1
+ // group-star — group-star@1, the sender's groups blinded to every
2
+ // contact and sealed to the chosen ones (Trust Layer 0.31 §5.2b, task
3
+ // group-star/0.1): the pure wire constructions over raw X25519 shared
4
+ // secrets (lib Stage 2, PR 4b).
5
+ //
6
+ // Byte-identical with vectors/group-star.json (check:generators:lib). The
7
+ // holder's trust module (trust/group-star.ts) builds on these functions
8
+ // with secrets from its KeyProvider.
9
+ //
10
+ // k_g = HKDF(X25519(pairX_sender, pairX_recipient),
11
+ // "rltp/visibility/blind/group-star/" || senderPair || "/" || recipientPair || "/" || salt)
12
+ // k_e(G) = HKDF(same ikm,
13
+ // "rltp/visibility/seal/group-star/" || senderPair || "/" || recipientPair || "/" || salt || "/" || G)
14
+ // entry = { d: HMAC(k_g, UTF-8 of G), c: AES-256-GCM(k_e(G), nonce, AAD = bytes of d, JCS(group-pair@1)) }
15
+ // proof = { mac: HMAC(k_g, JCS(body)) }
16
+ // group-pair@1 = { type, group, member, memberOp, to, salt, proof },
17
+ // proof = HMAC(HKDF(ECDH(memberX_sender, pairX_recipient), "rltp/visibility/mac/group-pair"), JCS(pair without proof))
18
+ //
19
+ // A `c` that is not a real pair is FILLER: random bytes of exactly the
20
+ // length a real c has in this star. The list is padded with filler
21
+ // entries to the next multiple of 16 (an empty list to 16: the all-
22
+ // filler star), sorted by d once, and sliced in order into chunks of at
23
+ // most 64 (5.2a). Entropy comes from the caller's `draw`, by kind, so
24
+ // vectors replay; this module draws none itself.
25
+ import { jcs, makeValidator } from './core.js';
26
+ import { SCHEMAS } from './schemas.js';
27
+ import { b64uOf, ecdh, fromB64u, hkdf, platformSubtle, xRawOfMk } from './crypto.js';
28
+ import { macU } from './anchor.js';
29
+ const te = new TextEncoder();
30
+ const td = new TextDecoder('utf-8', { fatal: true });
31
+ const VAL = makeValidator(SCHEMAS);
32
+ const schemaOk = (file, data) => VAL.validate(data, SCHEMAS[file], SCHEMAS[file]).length === 0;
33
+ /** Entries are padded to the next positive multiple of this (§5.2b). */
34
+ export const GROUP_STAR_PAD = 16;
35
+ /**
36
+ * Chunks hold at most 64 entries (§5.2b): a real entry is about 570
37
+ * bytes JCS, so a full chunk stays well inside the Delivery Contract's
38
+ * 65 536-byte plaintext bound at the longest salt.
39
+ */
40
+ export const GROUP_STAR_CHUNK = 64;
41
+ const NONCE = 12;
42
+ const TAG = 16;
43
+ const isZero = (b) => b.every((x) => x === 0);
44
+ /** k_g of one direction from the pair secret: `sender` and `recipient` are the two pair anchors, salt in its wire string form. */
45
+ export async function groupStarKeyOf(shared, sender, recipient, salt, subtle = platformSubtle()) {
46
+ if (isZero(shared))
47
+ throw new Error('all-zero shared secret (Trust §2.1)');
48
+ return hkdf(shared, `rltp/visibility/blind/group-star/${sender}/${recipient}/${salt}`, 32, subtle);
49
+ }
50
+ /** k_e(G) from the pair secret: the sealing key of one group's entry; G is the canonical u form of the genesis digest. */
51
+ export async function groupSealKeyOf(shared, sender, recipient, salt, group, subtle = platformSubtle()) {
52
+ if (isZero(shared))
53
+ throw new Error('all-zero shared secret (Trust §2.1)');
54
+ return hkdf(shared, `rltp/visibility/seal/group-star/${sender}/${recipient}/${salt}/${group}`, 32, subtle);
55
+ }
56
+ // ── AEAD (§2.1): AES-256-GCM, 128-bit tag, 96-bit nonce; u + base64url(nonce || ct || tag) ──
57
+ async function seal(key, aad, plaintext, nonce, S) {
58
+ const k = await S.importKey('raw', key, 'AES-GCM', false, ['encrypt']);
59
+ const ct = new Uint8Array(await S.encrypt({ name: 'AES-GCM', iv: nonce, additionalData: te.encode(aad), tagLength: 128 }, k, te.encode(plaintext)));
60
+ const out = new Uint8Array(NONCE + ct.length);
61
+ out.set(nonce);
62
+ out.set(ct, NONCE);
63
+ return 'u' + b64uOf(out);
64
+ }
65
+ async function open(key, aad, c, S) {
66
+ try {
67
+ const raw = fromB64u(c.slice(1));
68
+ if (raw.length <= NONCE + TAG)
69
+ return null;
70
+ const k = await S.importKey('raw', key, 'AES-GCM', false, ['decrypt']);
71
+ const pt = await S.decrypt({ name: 'AES-GCM', iv: raw.subarray(0, NONCE), additionalData: te.encode(aad), tagLength: 128 }, k, raw.subarray(NONCE));
72
+ return td.decode(pt);
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ /** The opened body of a sealed entry; `memberShared` = X25519(memberX_sender, pairX_recipient). */
79
+ export async function buildGroupPairWith(i, memberShared, subtle = platformSubtle()) {
80
+ if (isZero(memberShared))
81
+ throw new Error('all-zero shared secret (Trust §2.1)');
82
+ const unproved = { type: 'group-pair@1', group: i.group, member: i.member, memberOp: i.memberOp, to: i.to, salt: i.salt };
83
+ const k = await hkdf(memberShared, 'rltp/visibility/mac/group-pair', 32, subtle);
84
+ return { ...unproved, proof: await macU(k, jcs(unproved), subtle) };
85
+ }
86
+ // every field of a group pair but salt has a fixed length on the wire
87
+ // (did:key 56, digest 47, oid 47, mac 44), so a real c's length is a
88
+ // function of the salt alone — the filler length of this star
89
+ const sealedLength = (to, salt) => NONCE + TAG + te.encode(jcs({
90
+ type: 'group-pair@1', group: 'u' + 'A'.repeat(46), member: to, memberOp: 'oid:' + 'A'.repeat(43), to, salt, proof: 'u' + 'A'.repeat(43),
91
+ })).length;
92
+ /** The chunks of one group-star delivery toward one recipient. */
93
+ export async function buildGroupStarWith(i) {
94
+ const S = i.subtle ?? platformSubtle();
95
+ const kg = await groupStarKeyOf(i.shared, i.sender, i.to, i.salt, S);
96
+ const cLen = sealedLength(i.to, i.salt);
97
+ const ds = new Set(), cs = new Set();
98
+ const entries = [];
99
+ const fillerC = () => { for (;;) {
100
+ const c = 'u' + b64uOf(i.draw(cLen, 'filler'));
101
+ if (!cs.has(c))
102
+ return c;
103
+ } };
104
+ for (const g of i.groups) {
105
+ const d = await macU(kg, g.group, S);
106
+ if (ds.has(d))
107
+ continue; // one entry per group
108
+ let c;
109
+ if (g.sealed) {
110
+ if (!g.member || !g.memberOp || !g.memberShared)
111
+ throw new Error('a sealed group pair needs member and memberOp');
112
+ const pair = await buildGroupPairWith({ group: g.group, member: g.member, memberOp: g.memberOp, to: i.to, salt: i.salt }, await g.memberShared(), S);
113
+ c = await seal(await groupSealKeyOf(i.shared, i.sender, i.to, i.salt, g.group, S), d, jcs(pair), i.draw(NONCE, 'nonce'), S);
114
+ if (c.length !== 'u'.length + Math.ceil(cLen * 4 / 3))
115
+ throw new Error('group pair of unexpected length — filler would be distinguishable');
116
+ }
117
+ else
118
+ c = fillerC();
119
+ ds.add(d);
120
+ cs.add(c);
121
+ entries.push({ d, c });
122
+ }
123
+ const target = Math.max(GROUP_STAR_PAD, Math.ceil(entries.length / GROUP_STAR_PAD) * GROUP_STAR_PAD);
124
+ while (entries.length < target) {
125
+ let d;
126
+ do
127
+ d = 'u' + b64uOf(i.draw(32, 'filler'));
128
+ while (ds.has(d)); // collisions with any value are resampled
129
+ const c = fillerC();
130
+ ds.add(d);
131
+ cs.add(c);
132
+ entries.push({ d, c });
133
+ }
134
+ entries.sort((x, y) => (x.d < y.d ? -1 : x.d > y.d ? 1 : 0));
135
+ const n = Math.ceil(entries.length / GROUP_STAR_CHUNK);
136
+ const chunks = [];
137
+ for (let k = 0; k < n; k++) {
138
+ const body = { type: 'group-star@1', salt: i.salt, seq: String(k + 1), last: k === n - 1, groups: entries.slice(k * GROUP_STAR_CHUNK, (k + 1) * GROUP_STAR_CHUNK) };
139
+ chunks.push({ body, proof: { mac: await macU(kg, jcs(body), S) } });
140
+ }
141
+ return { chunks };
142
+ }
143
+ // ── the recipient ───────────────────────────────────────────────────────
144
+ /** Is this a schema-valid group-star@1 chunk (visibility-group-star.schema.json)? */
145
+ export const groupStarChunkOk = (chunk) => schemaOk('visibility-group-star.schema.json', chunk);
146
+ /** The §5.2b checks of a completed union: a positive multiple of 16
147
+ * entries, strictly ascending by d, every c of the same length. */
148
+ export function groupStarUnionOk(entries) {
149
+ if (!entries.every((e, k) => k === 0 || entries[k - 1].d < e.d))
150
+ return 'order';
151
+ if (entries.length === 0 || entries.length % GROUP_STAR_PAD !== 0)
152
+ return 'size';
153
+ if (entries.some((e) => e.c.length !== entries[0].c.length))
154
+ return 'c length';
155
+ return null;
156
+ }
157
+ /**
158
+ * One delivery's chunks → its entry union (5.2a under the group star's
159
+ * own salt): every chunk schema-valid and MAC-valid under k_g of the
160
+ * arrival tuple, one salt, seq 1..n each once, exactly the n-th last,
161
+ * the union strictly ascending by d — and the §5.2b reception rules on
162
+ * the completed assembly: a positive multiple of 16 entries, every c of
163
+ * the same length. A failing assembly rejects the delivery.
164
+ */
165
+ export async function assembleGroupStarWith(i) {
166
+ const S = i.subtle ?? platformSubtle();
167
+ if (!Array.isArray(i.chunks) || !i.chunks.length)
168
+ return { ok: false, reason: 'no chunk' };
169
+ for (const ch of i.chunks)
170
+ if (!groupStarChunkOk(ch))
171
+ return { ok: false, reason: 'schema' };
172
+ const salt = i.chunks[0].body.salt;
173
+ if (i.chunks.some((ch) => ch.body.salt !== salt))
174
+ return { ok: false, reason: 'salt' };
175
+ let kg;
176
+ try {
177
+ kg = await groupStarKeyOf(await i.shared(), i.from, i.own, salt, S);
178
+ }
179
+ catch {
180
+ return { ok: false, reason: 'ecdh' };
181
+ }
182
+ for (const ch of i.chunks)
183
+ if ((await macU(kg, jcs(ch.body), S)) !== ch.proof.mac)
184
+ return { ok: false, reason: 'mac' };
185
+ const bySeq = new Map();
186
+ for (const ch of i.chunks) {
187
+ if (bySeq.has(ch.body.seq))
188
+ return { ok: false, reason: 'seq repeated' };
189
+ bySeq.set(ch.body.seq, ch.body);
190
+ }
191
+ const lasts = i.chunks.filter((ch) => ch.body.last);
192
+ if (lasts.length !== 1)
193
+ return { ok: false, reason: 'last' };
194
+ const n = Number(lasts[0].body.seq);
195
+ if (bySeq.size !== n)
196
+ return { ok: false, reason: 'incomplete' };
197
+ const entries = [];
198
+ for (let k = 1; k <= n; k++) {
199
+ const b = bySeq.get(String(k));
200
+ if (!b)
201
+ return { ok: false, reason: 'incomplete' };
202
+ entries.push(...b.groups);
203
+ }
204
+ const bad = groupStarUnionOk(entries);
205
+ if (bad !== null)
206
+ return { ok: false, reason: bad };
207
+ return { ok: true, salt, entries };
208
+ }
209
+ /**
210
+ * Reception (§5.2b): for each group the recipient is a current member
211
+ * of, compute d and test the union; on a hit derive k_e(G) and try to
212
+ * open c — a c that does not open is a miss, not an error. An opened
213
+ * pair is accepted only if, in order: schema · group = G · to = the
214
+ * own pair anchor and salt = the assembly's · memberOp names a
215
+ * canonical admission of member (or the genesis) and member is current
216
+ * · the MAC verifies under that operation's card key, non-zero ECDH
217
+ * (`agreeMember(cardKeyAgreement)` = X25519(own pairX, that key), null
218
+ * where it cannot agree). An accepted pair is class V: merged
219
+ * holder-locally, never republished.
220
+ */
221
+ export async function openGroupStarWith(i) {
222
+ const S = i.subtle ?? platformSubtle();
223
+ const kg = await groupStarKeyOf(i.shared, i.from, i.own, i.salt, S);
224
+ const byD = new Map(i.entries.map((e) => [e.d, e.c]));
225
+ const out = [];
226
+ for (const m of i.memberships) {
227
+ const d = await macU(kg, m.group, S);
228
+ const c = byD.get(d);
229
+ if (c === undefined) {
230
+ out.push({ group: m.group, hit: false, opened: false, accepted: false });
231
+ continue;
232
+ }
233
+ const pt = await open(await groupSealKeyOf(i.shared, i.from, i.own, i.salt, m.group, S), d, c, S);
234
+ if (pt === null) {
235
+ out.push({ group: m.group, hit: true, opened: false, accepted: false });
236
+ continue;
237
+ }
238
+ const reject = (reason) => out.push({ group: m.group, hit: true, opened: true, accepted: false, reason });
239
+ let pair;
240
+ try {
241
+ pair = JSON.parse(pt);
242
+ }
243
+ catch {
244
+ reject('schema');
245
+ continue;
246
+ }
247
+ if (!schemaOk('visibility-group-pair.schema.json', pair)) {
248
+ reject('schema');
249
+ continue;
250
+ }
251
+ if (pair.group !== m.group) {
252
+ reject('group');
253
+ continue;
254
+ }
255
+ if (pair.to !== i.own || pair.salt !== i.salt) {
256
+ reject('to');
257
+ continue;
258
+ }
259
+ const op = m.resolve(pair.memberOp);
260
+ if (!op || op.subject !== pair.member || !m.isMember(pair.member)) {
261
+ reject('member');
262
+ continue;
263
+ }
264
+ const sh = await i.agreeMember(op.keyAgreement);
265
+ if (!sh || isZero(sh)) {
266
+ reject('mac');
267
+ continue;
268
+ }
269
+ const { proof, ...unproved } = pair;
270
+ if ((await macU(await hkdf(sh, 'rltp/visibility/mac/group-pair', 32, S), jcs(unproved), S)) !== proof) {
271
+ reject('mac');
272
+ continue;
273
+ }
274
+ out.push({ group: m.group, hit: true, opened: true, accepted: true, member: pair.member, memberOp: pair.memberOp });
275
+ }
276
+ return out;
277
+ }
278
+ // ── over identity Contexts ─────────────────────────────────────────────
279
+ // The same constructions with the private keys of identity Contexts (the
280
+ // form vectors, generators and tests use); the holder goes through the
281
+ // *With forms with secrets from its KeyProvider. Entropy (AEAD nonces,
282
+ // filler) is the caller's `rand`, so the vectors replay.
283
+ const shared = async (own, peerKeyAgreement) => {
284
+ const x = xRawOfMk(peerKeyAgreement);
285
+ if (!x)
286
+ throw new Error('peer key agreement is not a multikey');
287
+ const sh = await ecdh(own.x.priv, x);
288
+ if (sh.every((b) => b === 0))
289
+ throw new Error('all-zero shared secret (Trust §2.1)'); // rejected before key derivation
290
+ return sh;
291
+ };
292
+ /** k_g of one direction: `sender` and `recipient` are the two pair anchors, salt in its wire string form. */
293
+ export async function groupStarKey(own, peerKeyAgreement, sender, recipient, salt) {
294
+ return groupStarKeyOf(await shared(own, peerKeyAgreement), sender, recipient, salt);
295
+ }
296
+ /** k_e(G): the sealing key of one group's entry; G is the canonical u form of the genesis digest. */
297
+ export async function groupSealKey(own, peerKeyAgreement, sender, recipient, salt, group) {
298
+ return groupSealKeyOf(await shared(own, peerKeyAgreement), sender, recipient, salt, group);
299
+ }
300
+ /** The opened body of a sealed entry; proof under memberX × pairX of the recipient. */
301
+ export async function buildGroupPair(i) {
302
+ return buildGroupPairWith({ group: i.group, member: i.member.anchor, memberOp: i.memberOp, to: i.to, salt: i.salt }, await shared(i.member, i.toKeyAgreement));
303
+ }
304
+ /** The chunks of one group-star delivery toward one recipient. */
305
+ export async function buildGroupStar(i) {
306
+ const rnd = i.rand;
307
+ return buildGroupStarWith({
308
+ shared: await shared(i.own, i.toKeyAgreement), sender: i.own.anchor, to: i.to, salt: i.salt,
309
+ groups: i.groups.map((g) => ({
310
+ group: g.group, sealed: g.sealed, member: g.member?.anchor, memberOp: g.memberOp,
311
+ memberShared: g.member ? () => shared(g.member, i.toKeyAgreement) : undefined,
312
+ })),
313
+ draw: (n) => rnd(n),
314
+ });
315
+ }
316
+ // ── the recipient ───────────────────────────────────────────────────────
317
+ /**
318
+ * One delivery's chunks → its entry union (5.2a under the group star's
319
+ * own salt): every chunk schema-valid and MAC-valid under k_g of the
320
+ * arrival tuple, one salt, seq 1..n each once, exactly the n-th last,
321
+ * the union strictly ascending by d — and the §5.2b reception rules on
322
+ * the completed assembly: a positive multiple of 16 entries, every c of
323
+ * the same length. A failing assembly rejects the delivery.
324
+ */
325
+ export async function assembleGroupStar(i) {
326
+ return assembleGroupStarWith({ shared: () => shared(i.own, i.fromKeyAgreement), from: i.from, own: i.own.anchor, chunks: i.chunks });
327
+ }
328
+ /**
329
+ * Reception (§5.2b): for each group the recipient is a current member
330
+ * of, compute d and test the union; on a hit derive k_e(G) and try to
331
+ * open c — a c that does not open is a miss, not an error. An opened
332
+ * pair is accepted only if, in order: schema · group = G · to = the
333
+ * own pair anchor and salt = the assembly's · memberOp names a
334
+ * canonical admission of member (or the genesis) and member is current
335
+ * · the MAC verifies under that operation's card key, non-zero ECDH.
336
+ * An accepted pair is class V: merged holder-locally, never republished.
337
+ */
338
+ export async function openGroupStar(i) {
339
+ const res = await openGroupStarWith({
340
+ shared: await shared(i.own, i.fromKeyAgreement), from: i.from, own: i.own.anchor, salt: i.salt, entries: i.entries, memberships: i.memberships,
341
+ agreeMember: async (keyAgreement) => {
342
+ const memberX = xRawOfMk(keyAgreement);
343
+ return memberX ? ecdh(i.own.x.priv, memberX) : null;
344
+ },
345
+ });
346
+ return res.map(({ memberOp: _, ...r }) => r);
347
+ }
348
+ //# sourceMappingURL=group-star.js.map