@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.
- package/CHANGELOG.md +32 -0
- package/README.md +187 -78
- package/dist/ack.d.ts +84 -0
- package/dist/ack.d.ts.map +1 -0
- package/dist/ack.js +114 -0
- package/dist/ack.js.map +1 -0
- package/dist/adapters/vti-mediator.d.ts +154 -0
- package/dist/adapters/vti-mediator.d.ts.map +1 -0
- package/dist/adapters/vti-mediator.js +621 -0
- package/dist/adapters/vti-mediator.js.map +1 -0
- package/dist/anchor.d.ts +226 -0
- package/dist/anchor.d.ts.map +1 -0
- package/dist/anchor.js +347 -0
- package/dist/anchor.js.map +1 -0
- package/dist/{holder.d.ts → carrier-holder.d.ts} +1 -1
- package/dist/carrier-holder.d.ts.map +1 -0
- package/dist/{holder.js → carrier-holder.js} +5 -4
- package/dist/carrier-holder.js.map +1 -0
- package/dist/carrier-identity.js +4 -4
- package/dist/carrier-identity.js.map +1 -1
- package/dist/carrier.d.ts.map +1 -1
- package/dist/carrier.js +4 -3
- package/dist/carrier.js.map +1 -1
- package/dist/ceremony.d.ts +3 -2
- package/dist/ceremony.d.ts.map +1 -1
- package/dist/ceremony.js +16 -26
- package/dist/ceremony.js.map +1 -1
- package/dist/codec.d.ts +46 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +410 -0
- package/dist/codec.js.map +1 -0
- package/dist/continuity.d.ts +67 -0
- package/dist/continuity.d.ts.map +1 -0
- package/dist/continuity.js +110 -0
- package/dist/continuity.js.map +1 -0
- package/dist/core.d.ts +4 -0
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js.map +1 -1
- package/dist/crypto.d.ts +23 -7
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +54 -20
- package/dist/crypto.js.map +1 -1
- package/dist/delivery.d.ts +31 -3
- package/dist/delivery.d.ts.map +1 -1
- package/dist/delivery.js +75 -28
- package/dist/delivery.js.map +1 -1
- package/dist/encounter/ceremony.d.ts +37 -0
- package/dist/encounter/ceremony.d.ts.map +1 -0
- package/dist/encounter/ceremony.js +662 -0
- package/dist/encounter/ceremony.js.map +1 -0
- package/dist/encounter.d.ts +91 -1
- package/dist/encounter.d.ts.map +1 -1
- package/dist/encounter.js +191 -12
- package/dist/encounter.js.map +1 -1
- package/dist/engine.d.ts +291 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +1395 -0
- package/dist/engine.js.map +1 -0
- package/dist/entropy.d.ts +28 -0
- package/dist/entropy.d.ts.map +1 -0
- package/dist/entropy.js +96 -0
- package/dist/entropy.js.map +1 -0
- package/dist/events.d.ts +177 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/fixtures.d.ts +30 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +155 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/forge.d.ts +47 -0
- package/dist/forge.d.ts.map +1 -0
- package/dist/forge.js +20 -0
- package/dist/forge.js.map +1 -0
- package/dist/group-star.d.ts +251 -0
- package/dist/group-star.d.ts.map +1 -0
- package/dist/group-star.js +348 -0
- package/dist/group-star.js.map +1 -0
- package/dist/holder-api.d.ts +137 -0
- package/dist/holder-api.d.ts.map +1 -0
- package/dist/holder-api.js +132 -0
- package/dist/holder-api.js.map +1 -0
- package/dist/holder-state-v1.d.ts +801 -0
- package/dist/holder-state-v1.d.ts.map +1 -0
- package/dist/holder-state-v1.js +2 -0
- package/dist/holder-state-v1.js.map +1 -0
- package/dist/identity-state.d.ts +43 -0
- package/dist/identity-state.d.ts.map +1 -0
- package/dist/identity-state.js +345 -0
- package/dist/identity-state.js.map +1 -0
- package/dist/identity.d.ts +24 -2
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +70 -11
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +77 -29
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +36 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +22 -0
- package/dist/internal.js.map +1 -0
- package/dist/introduction/holder.d.ts +18 -0
- package/dist/introduction/holder.d.ts.map +1 -0
- package/dist/introduction/holder.js +757 -0
- package/dist/introduction/holder.js.map +1 -0
- package/dist/introduction.d.ts +101 -0
- package/dist/introduction.d.ts.map +1 -0
- package/dist/introduction.js +132 -0
- package/dist/introduction.js.map +1 -0
- package/dist/invariants.d.ts +43 -0
- package/dist/invariants.d.ts.map +1 -0
- package/dist/invariants.js +883 -0
- package/dist/invariants.js.map +1 -0
- package/dist/keys-seed.d.ts +7 -0
- package/dist/keys-seed.d.ts.map +1 -0
- package/dist/keys-seed.js +133 -0
- package/dist/keys-seed.js.map +1 -0
- package/dist/log-memory.d.ts +12 -0
- package/dist/log-memory.d.ts.map +1 -0
- package/dist/log-memory.js +101 -0
- package/dist/log-memory.js.map +1 -0
- package/dist/membership/holder.d.ts +35 -0
- package/dist/membership/holder.d.ts.map +1 -0
- package/dist/membership/holder.js +852 -0
- package/dist/membership/holder.js.map +1 -0
- package/dist/membership.d.ts +92 -0
- package/dist/membership.d.ts.map +1 -0
- package/dist/membership.js +157 -0
- package/dist/membership.js.map +1 -0
- package/dist/ports.d.ts +237 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +2 -0
- package/dist/ports.js.map +1 -0
- package/dist/profile.d.ts +56 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +193 -0
- package/dist/profile.js.map +1 -0
- package/dist/prompts.d.ts +14 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +69 -0
- package/dist/prompts.js.map +1 -0
- package/dist/receive.d.ts +133 -0
- package/dist/receive.d.ts.map +1 -0
- package/dist/receive.js +298 -0
- package/dist/receive.js.map +1 -0
- package/dist/results.d.ts +158 -0
- package/dist/results.d.ts.map +1 -0
- package/dist/results.js +21 -0
- package/dist/results.js.map +1 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +42 -38
- package/dist/schemas.js.map +1 -1
- package/dist/sha256.d.ts +3 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +69 -0
- package/dist/sha256.js.map +1 -0
- package/dist/star.d.ts +31 -0
- package/dist/star.d.ts.map +1 -0
- package/dist/star.js +56 -0
- package/dist/star.js.map +1 -0
- package/dist/state-builder.d.ts +42 -0
- package/dist/state-builder.d.ts.map +1 -0
- package/dist/state-builder.js +100 -0
- package/dist/state-builder.js.map +1 -0
- package/dist/state.d.ts +853 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +36 -0
- package/dist/state.js.map +1 -0
- package/dist/store-contract.d.ts +11 -0
- package/dist/store-contract.d.ts.map +1 -0
- package/dist/store-contract.js +189 -0
- package/dist/store-contract.js.map +1 -0
- package/dist/store-memory.d.ts +8 -0
- package/dist/store-memory.d.ts.map +1 -0
- package/dist/store-memory.js +17 -0
- package/dist/store-memory.js.map +1 -0
- package/dist/testing.d.ts +22 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +21 -0
- package/dist/testing.js.map +1 -0
- package/dist/trust/continuity.d.ts +71 -0
- package/dist/trust/continuity.d.ts.map +1 -0
- package/dist/trust/continuity.js +1216 -0
- package/dist/trust/continuity.js.map +1 -0
- package/dist/trust/duties.d.ts +67 -0
- package/dist/trust/duties.d.ts.map +1 -0
- package/dist/trust/duties.js +179 -0
- package/dist/trust/duties.js.map +1 -0
- package/dist/trust/entries.d.ts +139 -0
- package/dist/trust/entries.d.ts.map +1 -0
- package/dist/trust/entries.js +694 -0
- package/dist/trust/entries.js.map +1 -0
- package/dist/trust/group-star.d.ts +43 -0
- package/dist/trust/group-star.d.ts.map +1 -0
- package/dist/trust/group-star.js +188 -0
- package/dist/trust/group-star.js.map +1 -0
- package/dist/trust/mapping.d.ts +56 -0
- package/dist/trust/mapping.d.ts.map +1 -0
- package/dist/trust/mapping.js +584 -0
- package/dist/trust/mapping.js.map +1 -0
- package/dist/trust/profile.d.ts +25 -0
- package/dist/trust/profile.d.ts.map +1 -0
- package/dist/trust/profile.js +249 -0
- package/dist/trust/profile.js.map +1 -0
- package/dist/trust/star.d.ts +208 -0
- package/dist/trust/star.d.ts.map +1 -0
- package/dist/trust/star.js +588 -0
- package/dist/trust/star.js.map +1 -0
- package/dist/trust/state.d.ts +83 -0
- package/dist/trust/state.d.ts.map +1 -0
- package/dist/trust/state.js +154 -0
- package/dist/trust/state.js.map +1 -0
- package/dist/util-freeze.d.ts +3 -0
- package/dist/util-freeze.d.ts.map +1 -0
- package/dist/util-freeze.js +11 -0
- package/dist/util-freeze.js.map +1 -0
- package/dist/views.d.ts +147 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +268 -0
- package/dist/views.js.map +1 -0
- package/dist/visibility.d.ts +5 -5
- package/dist/visibility.d.ts.map +1 -1
- package/dist/visibility.js +22 -28
- package/dist/visibility.js.map +1 -1
- package/dist/wire.d.ts +178 -84
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js.map +1 -1
- package/package.json +58 -11
- package/schemas/holder-state-v1.schema.json +3880 -0
- package/dist/holder.d.ts.map +0 -1
- package/dist/holder.js.map +0 -1
- package/dist/probe/acks.d.ts +0 -27
- package/dist/probe/acks.d.ts.map +0 -1
- package/dist/probe/acks.js +0 -152
- package/dist/probe/acks.js.map +0 -1
- package/dist/probe/continuity.d.ts +0 -20
- package/dist/probe/continuity.d.ts.map +0 -1
- package/dist/probe/continuity.js +0 -805
- package/dist/probe/continuity.js.map +0 -1
- package/dist/probe/deps.d.ts +0 -61
- package/dist/probe/deps.d.ts.map +0 -1
- package/dist/probe/deps.js +0 -54
- package/dist/probe/deps.js.map +0 -1
- package/dist/probe/encounter.d.ts +0 -26
- package/dist/probe/encounter.d.ts.map +0 -1
- package/dist/probe/encounter.js +0 -938
- package/dist/probe/encounter.js.map +0 -1
- package/dist/probe/introduce.d.ts +0 -70
- package/dist/probe/introduce.d.ts.map +0 -1
- package/dist/probe/introduce.js +0 -295
- package/dist/probe/introduce.js.map +0 -1
- package/dist/probe/membership.d.ts +0 -90
- package/dist/probe/membership.d.ts.map +0 -1
- package/dist/probe/membership.js +0 -450
- package/dist/probe/membership.js.map +0 -1
- package/dist/probe/trust.d.ts +0 -156
- package/dist/probe/trust.d.ts.map +0 -1
- package/dist/probe/trust.js +0 -1233
- package/dist/probe/trust.js.map +0 -1
- package/dist/probe.d.ts +0 -7
- package/dist/probe.d.ts.map +0 -1
- package/dist/probe.js +0 -42
- 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
|