Skip to content

Commit 73e8f02

Browse files
committed
State-tester testing for removal
1 parent bb04927 commit 73e8f02

File tree

5 files changed

+128
-16
lines changed

5 files changed

+128
-16
lines changed

src/art-state.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ class ARTState {
7676
return { path: path };
7777
}
7878

79-
remove(index) {/* TODO */}
79+
async remove(index) {
80+
/* TODO */
81+
return {};
82+
}
8083

8184
get groupInitKey() {
8285
return {
@@ -103,7 +106,9 @@ class ARTState {
103106
await this.art.merge(update.path);
104107
}
105108

106-
handleRemove(remove) {/* TODO */}
109+
async handleRemove(remove) {
110+
/* TODO */
111+
}
107112
}
108113

109114
module.exports = ARTState;

src/flat-state.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ class FlatState {
6060
};
6161
}
6262

63-
remove(index) {/* TODO */}
63+
async remove(index) {
64+
/* TODO */
65+
return {};
66+
}
6467

6568
get groupInitKey() {
6669
let publicNodes = {};
@@ -92,7 +95,9 @@ class FlatState {
9295
this.nodes[2 * update.from].public = update.public;
9396
}
9497

95-
handleRemove(remove) {/* TODO */}
98+
async handleRemove(remove) {
99+
/* TODO */
100+
}
96101
}
97102

98103
module.exports = FlatState;

src/state-tester.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,83 @@ async function testUpdate(State, transcode) {
178178
console.log(`[${label}] PASS`);
179179
}
180180

181+
async function testRemove(State) {
182+
// Create a group via GroupAdds
183+
const testGroupSize = 5;
184+
let creator = await State.oneMemberGroup(base64.random(32));
185+
let members = [creator];
186+
for (let i = 1; i < testGroupSize; ++i) {
187+
let initLeaf = base64.random(32);
188+
let initKP = await iota(initLeaf);
189+
let ga = await members[members.length - 1].add(initKP.publicKey)
190+
191+
for (let m of members) {
192+
await m.handleGroupAdd(ga);
193+
}
194+
195+
let joiner = await State.fromGroupAdd(initLeaf, ga);
196+
members.push(joiner);
197+
}
198+
199+
// Have the first member remove two members
200+
let remover = members[0];
201+
let removed = [2, 3];
202+
for (let index of removed) {
203+
let leaf = base64.random(32);
204+
let removeIn = await remover.remove(leaf, index);
205+
let removeEnc = JSON.stringify(removeIn);
206+
let remove = JSON.parse(removeEnc);
207+
208+
members = members.filter(x => x.index != index);
209+
210+
for (let m of members) {
211+
await m.handleRemove(remove);
212+
}
213+
214+
for (let m of members) {
215+
let eq = await groupEqual(m, remover);
216+
if (!eq) {
217+
await groupDump(remover.index, remover);
218+
await groupDump(m.index, m);
219+
throw 'state-remove';
220+
}
221+
}
222+
}
223+
224+
// Have each remaining member update and verify that others are consistent
225+
for (let m1 of members) {
226+
let leaf = base64.random(32);
227+
let updateIn = await m1.update(leaf);
228+
let updateEnc = JSON.stringify(updateIn);
229+
let update = JSON.parse(updateEnc);
230+
231+
await m1.handleSelfUpdate(update, leaf);
232+
233+
for (let m2 of members) {
234+
if (m2.index == m1.index) {
235+
continue
236+
}
237+
238+
await m2.handleUpdate(update);
239+
240+
let eq = await groupEqual(m1, m2);
241+
if (!eq) {
242+
await groupDump(m1.index, m1);
243+
await groupDump(m2.index, m2);
244+
throw 'state-remove';
245+
}
246+
}
247+
}
248+
249+
console.log('[state-remove] PASS');
250+
}
251+
181252
module.exports = {
182253
test: async function(State) {
183254
await testUserAdd(State);
184255
await testGroupAdd(State);
185256
await testUpdate(State, false);
186257
await testUpdate(State, true);
258+
await testRemove(State);
187259
},
188260
};

src/treekem-state.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class TreeKEMState {
5555

5656
static async join(leaf, groupInitKey) {
5757
let tkem = await TreeKEM.fromFrontier(groupInitKey.size, groupInitKey.frontier, leaf);
58-
let ct = await tkem.encrypt(leaf)
58+
let ct = await tkem.encrypt(leaf, tkem.index)
5959
return {
6060
ciphertexts: ct.ciphertexts,
6161
nodes: ct.nodes,
@@ -80,15 +80,22 @@ class TreeKEMState {
8080
}
8181

8282
async update(leaf) {
83-
let ct = await this.tkem.encrypt(leaf);
83+
let ct = await this.tkem.encrypt(leaf, this.tkem.index);
8484
return {
8585
from: this.tkem.index,
8686
ciphertexts: ct.ciphertexts,
8787
nodes: ct.nodes,
8888
}
8989
}
9090

91-
remove(index) {/* TODO */}
91+
async remove(leaf, index) {
92+
let ct = await this.tkem.encrypt(leaf, index);
93+
return {
94+
index: index,
95+
ciphertexts: ct.ciphertexts,
96+
nodes: ct.nodes,
97+
};
98+
}
9299

93100
get groupInitKey() {
94101
return {
@@ -122,7 +129,11 @@ class TreeKEMState {
122129
this.tkem.merge(pt.nodes);
123130
}
124131

125-
handleRemove(remove) {/* TODO */}
132+
async handleRemove(remove) {
133+
let pt = await this.tkem.decrypt(remove.index, remove.ciphertexts);
134+
this.tkem.merge(pt.root);
135+
this.tkem.remove(remove.index);
136+
}
126137
}
127138

128139
module.exports = TreeKEMState;

src/treekem.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ class TreeKEM {
100100

101101
/*
102102
* Encrypt a fresh root value in a way that all participants in
103-
* the group can decrypt (except for the current node).
103+
* the group can decrypt, except for an excluded node.
104104
*
105105
* Arguments:
106106
* * leaf - BufferSource with leaf secret
107+
* * except - index of the node to exclude
107108
*
108109
* Returns: Promise resolving to a TreeKEMCiphertext object:
109110
* {
@@ -120,12 +121,12 @@ class TreeKEM {
120121
* ciphertexts: [ ECKEMCiphertext ]
121122
* }
122123
*/
123-
async encrypt(leaf) {
124-
let dirpath = tm.dirpath(2 * this.index, this.size);
125-
let copath = tm.copath(2 * this.index, this.size);
124+
async encrypt(leaf, except) {
125+
let dirpath = tm.dirpath(2 * except, this.size);
126+
let copath = tm.copath(2 * except, this.size);
126127

127128
// Generate hashes up the tree
128-
let privateNodes = await TreeKEM.hashUp(2 * this.index, this.size, leaf);
129+
let privateNodes = await TreeKEM.hashUp(2 * except, this.size, leaf);
129130
let nodes = {};
130131
for (let n in privateNodes) {
131132
nodes[n] = util.publicNode(privateNodes[n]);
@@ -182,21 +183,39 @@ class TreeKEM {
182183
let h = await ECKEM.decrypt(encryptions[decNode], this.nodes[decNode].private);
183184

184185
// Hash up to the root (plus one if we're growing the tree)
186+
let rootNode = tm.root(senderSize);
185187
let newDirpath = tm.dirpath(2 * this.index, senderSize);
186-
newDirpath.push(tm.root(senderSize));
188+
newDirpath.push(rootNode);
187189
let nodes = await TreeKEM.hashUp(newDirpath[dirIndex+1], senderSize, h);
188190

189-
let root = tm.root(senderSize);
191+
let root = {}
192+
root[rootNode] = nodes[root];
193+
190194
return {
195+
root: root,
191196
nodes: nodes,
192197
}
193198
}
194199

200+
/*
201+
* Remove a node from the tree, including its direct path
202+
*
203+
* Arguments:
204+
* index - Index of the node to remove
205+
*
206+
* Returns: None
207+
*/
208+
remove(index) {
209+
for (let n of tm.dirpath(2 * index, this.size)) {
210+
delete this.nodes[n];
211+
}
212+
}
213+
195214
/*
196215
* Updates nodes in the tree.
197216
*
198217
* Arguments:
199-
* nodes - Dictionary of nodes to udpate: { Int: Node }
218+
* nodes - Dictionary of nodes to update: { Int: Node }
200219
*
201220
* Returns: None
202221
*/

0 commit comments

Comments
 (0)