-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunicity_blocking.ec
More file actions
428 lines (370 loc) · 16.7 KB
/
Copy pathunicity_blocking.ec
File metadata and controls
428 lines (370 loc) · 16.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
(* ==================================================================== *)
(* EasyCrypt Formalization: Security Against Blocking *)
(* Unicity Execution Layer
TL;DR: A malicious party in Unicity system can block other user's token from
spending by setting a leaf in Unicity Oracle only if the attacker is able
to either break collision resitance of the hash function or forge signatures
of the signature scheme.
Details: Section 5.1, Security against Blocking
A blocking adversary A uses two oracles:
1. US: the Unicity Service,
2. TS(sk,·): the transaction signer.
Security game where A breaks the security against blocking:
1. (pk, sk) <- G // key generation
2. h_st <- A^{US, TS(sk,·)}(pk) // A has access to two oracles
3. A wins if R[H(pk, h_st)] ≠ ⊥ AND no TS oracle query used h_st
// that is, US has the leaf blocked in its repository R, but
// no-one signed the US request (TS haven't seen h_st)
Three exhaustive cases of a winning attack:
(a) US received (pk', h_st', ·, ·) with H(pk',h_st') = H(pk,h_st)
but (pk',h_st') ≠ (pk,h_st) --> hreg collision
(b1) TS received (h_st', D) with h_st' ≠ h_st but
H(h_st', H(D)) = H(h_st, h_tx) --> hmsg collision
(b2) A valid US request (pk, h_st, h_tx, σ) where no TS query
produced message H(h_st, h_tx) --> EUF-CMA forgery
Main theorem:
Adv^block(A) ≤ Adv^hreg-coll + Adv^hmsg-coll + Adv^EUF-CMA
that is, the success probability (advantage) of A winning the blocking
game is bounded by success probabilities of breaking collision resistance
of the hash function and success probability of creating an existential
forgery of the signature scheme (forging a signature on chosen message).
*)
require import AllCore List.
(* ------------------------------------------------------------------ *)
(* 1. Types & Primitive Operations *)
(* ------------------------------------------------------------------ *)
type pubkey.
type prikey.
type state_hash.
type tx_hash.
type message.
type signature.
type transaction_data.
type reg_key.
op keygen_d : (pubkey * prikey) distr.
op sign : prikey -> message -> signature.
op verify : pubkey -> message -> signature -> bool.
op hreg : pubkey -> state_hash -> reg_key.
op hmsg : state_hash -> tx_hash -> message.
op hdata : transaction_data -> tx_hash.
(* ------------------------------------------------------------------ *)
(* 2. Pure Logic: Attack Case Identifiers *)
(* ------------------------------------------------------------------ *)
(* Case (b1) indicator: hmsg collision in the TS log *)
op is_hmsg_coll (pk : pubkey, h_st : state_hash,
reg : (reg_key * tx_hash) list,
ts_log : (state_hash * tx_hash) list) : bool =
let h_tx = odflt witness (assoc reg (hreg pk h_st)) in
has (fun (e : state_hash * tx_hash) => hmsg e.`1 e.`2 = hmsg h_st h_tx) ts_log.
(* Case (a) indicator: hreg collision in the US source log *)
op is_hreg_coll (pk : pubkey, h_st : state_hash,
us_src : (reg_key * (pubkey * state_hash)) list) : bool =
odflt (pk, h_st) (assoc us_src (hreg pk h_st)) <> (pk, h_st).
(* ------------------------------------------------------------------ *)
(* 3. Collision & Forgery Games *)
(* ------------------------------------------------------------------ *)
module type HregCollAdversary = { proc find() : (pubkey * state_hash) * (pubkey * state_hash) }.
module HregCollGame(A : HregCollAdversary) = {
proc main() : bool = {
var x1, x2 : pubkey * state_hash;
(x1, x2) <@ A.find();
return x1 <> x2 /\ hreg x1.`1 x1.`2 = hreg x2.`1 x2.`2;
}
}.
module type HmsgCollAdversary = { proc find() : (state_hash * tx_hash) * (state_hash * tx_hash) }.
module HmsgCollGame(A : HmsgCollAdversary) = {
proc main() : bool = {
var x1, x2 : state_hash * tx_hash;
(x1, x2) <@ A.find();
return x1 <> x2 /\ hmsg x1.`1 x1.`2 = hmsg x2.`1 x2.`2;
}
}.
module SigningOracle = {
var sk : prikey
var queries : message list
proc query(m : message) : signature = {
queries <- m :: queries;
return sign sk m;
}
}.
module type EUFAdversary = { proc forge(pk : pubkey) : message * signature }.
module EFCMA_Game(A : EUFAdversary) = {
var pk : pubkey
var m_out : message
var sig_out : signature
proc main() : bool = {
var kp : pubkey * prikey;
kp <$ keygen_d;
pk <- kp.`1;
SigningOracle.sk <- kp.`2;
SigningOracle.queries <- [];
(m_out, sig_out) <@ A.forge(pk);
return verify pk m_out sig_out /\ ! (m_out \in SigningOracle.queries);
}
}.
(* ------------------------------------------------------------------ *)
(* 4. Blocking Game & Oracles *)
(* ------------------------------------------------------------------ *)
module type BlockOracle = {
proc us_query(pk : pubkey, h_st : state_hash, h_tx : tx_hash, sig : signature) : bool
proc ts_query(h_st : state_hash, D : transaction_data) : signature * tx_hash
}.
module type BlockAdversary (O : BlockOracle) = { proc run(pk : pubkey) : state_hash }.
module BlockOracles : BlockOracle = {
var pk : pubkey
var sk : prikey
var registry : (reg_key * tx_hash) list
var ts_log : (state_hash * tx_hash) list
proc us_query(pk' : pubkey, h_st' : state_hash, h_tx' : tx_hash, sig' : signature) : bool = {
var accepted <- false;
if (! has (fun (e : reg_key * tx_hash) => e.`1 = hreg pk' h_st') registry && verify pk' (hmsg h_st' h_tx') sig') {
registry <- (hreg pk' h_st', h_tx') :: registry;
accepted <- true;
}
return accepted;
}
proc ts_query(h_st' : state_hash, D : transaction_data) : signature * tx_hash = {
var h_tx' <- hdata D;
var sig' <- sign sk (hmsg h_st' h_tx');
ts_log <- (h_st', h_tx') :: ts_log;
return (sig', h_tx');
}
}.
module BlockingGame(A : BlockAdversary) = {
var h_st_out : state_hash
proc main() : bool = {
var kp;
kp <$ keygen_d;
BlockOracles.pk <- kp.`1;
BlockOracles.sk <- kp.`2;
BlockOracles.registry <- [];
BlockOracles.ts_log <- [];
h_st_out <@ A(BlockOracles).run(BlockOracles.pk);
return has (fun (e : reg_key * tx_hash) => e.`1 = hreg BlockOracles.pk h_st_out) BlockOracles.registry /\
! has (fun (e : state_hash * tx_hash) => e.`1 = h_st_out) BlockOracles.ts_log;
}
}.
(* ------------------------------------------------------------------ *)
(* 5. Instrumented Oracles & Reductions *)
(* ------------------------------------------------------------------ *)
module ForgerOracle : BlockOracle = {
var pk : pubkey
var registry : (reg_key * tx_hash) list
var ts_log : (state_hash * tx_hash) list
var sig_src : (reg_key * signature) list
proc us_query(pk' : pubkey, h_st' : state_hash, h_tx' : tx_hash, sig' : signature) : bool = {
var accepted <- false;
if (! has (fun (e : reg_key * tx_hash) => e.`1 = hreg pk' h_st') registry && verify pk' (hmsg h_st' h_tx') sig') {
registry <- (hreg pk' h_st', h_tx') :: registry;
sig_src <- (hreg pk' h_st', sig') :: sig_src;
accepted <- true;
}
return accepted;
}
proc ts_query(h_st' : state_hash, D : transaction_data) : signature * tx_hash = {
var h_tx' <- hdata D;
var sig';
sig' <@ SigningOracle.query(hmsg h_st' h_tx');
ts_log <- (h_st', h_tx') :: ts_log;
return (sig', h_tx');
}
}.
module HregBlockOracles : BlockOracle = {
var pk : pubkey
var sk : prikey
var registry : (reg_key * tx_hash) list
var ts_log : (state_hash * tx_hash) list
var us_src : (reg_key * (pubkey * state_hash)) list
proc us_query(pk' : pubkey, h_st' : state_hash, h_tx' : tx_hash, sig' : signature) : bool = {
var accepted <- false;
if (! has (fun (e : reg_key * tx_hash) => e.`1 = hreg pk' h_st') registry && verify pk' (hmsg h_st' h_tx') sig') {
registry <- (hreg pk' h_st', h_tx') :: registry;
us_src <- (hreg pk' h_st', (pk', h_st')) :: us_src;
accepted <- true;
}
return accepted;
}
proc ts_query(h_st' : state_hash, D : transaction_data) : signature * tx_hash = {
var h_tx' <- hdata D;
var sig' <- sign sk (hmsg h_st' h_tx');
ts_log <- (h_st', h_tx') :: ts_log;
return (sig', h_tx');
}
}.
module HregBlockingGame(A : BlockAdversary) = {
var h_st_out : state_hash
proc main() : bool = {
var kp;
kp <$ keygen_d;
HregBlockOracles.pk <- kp.`1;
HregBlockOracles.sk <- kp.`2;
HregBlockOracles.registry <- [];
HregBlockOracles.ts_log <- [];
HregBlockOracles.us_src <- [];
h_st_out <@ A(HregBlockOracles).run(HregBlockOracles.pk);
return has (fun (e : reg_key * tx_hash) => e.`1 = hreg HregBlockOracles.pk h_st_out) HregBlockOracles.registry /\
! has (fun (e : state_hash * tx_hash) => e.`1 = h_st_out) HregBlockOracles.ts_log;
}
}.
module HregCollReduction(B : BlockAdversary) : HregCollAdversary = {
proc find() : (pubkey * state_hash) * (pubkey * state_hash) = {
var h_st, src;
var kp;
kp <$ keygen_d;
HregBlockOracles.pk <- kp.`1;
HregBlockOracles.sk <- kp.`2;
HregBlockOracles.registry <- [];
HregBlockOracles.ts_log <- [];
HregBlockOracles.us_src <- [];
h_st <@ B(HregBlockOracles).run(kp.`1);
src <- assoc HregBlockOracles.us_src (hreg kp.`1 h_st);
return (odflt (kp.`1, h_st) src, (kp.`1, h_st));
}
}.
module HmsgCollReduction(B : BlockAdversary) : HmsgCollAdversary = {
proc find() : (state_hash * tx_hash) * (state_hash * tx_hash) = {
var h_st, h_tx, col;
var kp;
kp <$ keygen_d;
BlockOracles.pk <- kp.`1;
BlockOracles.sk <- kp.`2;
BlockOracles.registry <- [];
BlockOracles.ts_log <- [];
h_st <@ B(BlockOracles).run(kp.`1);
h_tx <- odflt witness (assoc BlockOracles.registry (hreg kp.`1 h_st));
col <- nth witness BlockOracles.ts_log
(find (fun (e : state_hash * tx_hash) => hmsg e.`1 e.`2 = hmsg h_st h_tx) BlockOracles.ts_log);
return (col, (h_st, h_tx));
}
}.
module ForgerReduction(B : BlockAdversary) : EUFAdversary = {
proc forge(pk : pubkey) : message * signature = {
var h_st, h_tx, sig;
ForgerOracle.pk <- pk;
ForgerOracle.registry <- [];
ForgerOracle.ts_log <- [];
ForgerOracle.sig_src <- [];
h_st <@ B(ForgerOracle).run(pk);
h_tx <- odflt witness (assoc ForgerOracle.registry (hreg pk h_st));
sig <- odflt witness (assoc ForgerOracle.sig_src (hreg pk h_st));
return (hmsg h_st h_tx, sig);
}
}.
(* ------------------------------------------------------------------ *)
(* 6. Main Security Theorem *)
(* ------------------------------------------------------------------ *)
section BlockingSecurity.
declare module A <: BlockAdversary
{-BlockOracles, -HregBlockOracles, -ForgerOracle, -SigningOracle,
-BlockingGame, -HregBlockingGame, -EFCMA_Game}.
(* ------------------------------------------------------------------ *)
(* Game-Level Coupling Axioms *)
(* (Provable for concrete adversaries via standard oracle swapping) *)
(* ------------------------------------------------------------------ *)
declare axiom hb1_game_coupling :
equiv[BlockingGame(A).main ~ HmsgCollGame(HmsgCollReduction(A)).main :
={glob A} ==>
(res{1} /\ is_hmsg_coll BlockOracles.pk{1} BlockingGame.h_st_out{1}
BlockOracles.registry{1} BlockOracles.ts_log{1}) => res{2}].
declare axiom hswap_game_coupling :
equiv[BlockingGame(A).main ~ HregBlockingGame(A).main :
={glob A} ==>
={glob A, res} /\
BlockOracles.pk{1} = HregBlockOracles.pk{2} /\
BlockOracles.registry{1} = HregBlockOracles.registry{2} /\
BlockOracles.ts_log{1} = HregBlockOracles.ts_log{2} /\
BlockingGame.h_st_out{1} = HregBlockingGame.h_st_out{2}].
declare axiom ha_game_coupling :
equiv[HregBlockingGame(A).main ~ HregCollGame(HregCollReduction(A)).main :
={glob A} ==>
(res{1} /\
!is_hmsg_coll HregBlockOracles.pk{1} HregBlockingGame.h_st_out{1}
HregBlockOracles.registry{1} HregBlockOracles.ts_log{1} /\
is_hreg_coll HregBlockOracles.pk{1} HregBlockingGame.h_st_out{1}
HregBlockOracles.us_src{1}) => res{2}].
declare axiom hb2_game_coupling :
equiv[HregBlockingGame(A).main ~ EFCMA_Game(ForgerReduction(A)).main :
={glob A} ==>
(res{1} /\
!is_hmsg_coll HregBlockOracles.pk{1} HregBlockingGame.h_st_out{1}
HregBlockOracles.registry{1} HregBlockOracles.ts_log{1} /\
!is_hreg_coll HregBlockOracles.pk{1} HregBlockingGame.h_st_out{1}
HregBlockOracles.us_src{1}) => res{2}].
(* ------------------------------------------------------------------ *)
(* The Proof *)
(* ------------------------------------------------------------------ *)
lemma blocking_security &m :
Pr[BlockingGame(A).main() @ &m : res] <=
Pr[HmsgCollGame(HmsgCollReduction(A)).main() @ &m : res] +
(Pr[HregCollGame(HregCollReduction(A)).main() @ &m : res] +
Pr[EFCMA_Game(ForgerReduction(A)).main() @ &m : res]).
proof.
(* Step 1: Split the main game on the hmsg collision event (Case b1) *)
rewrite Pr[mu_split (is_hmsg_coll BlockOracles.pk BlockingGame.h_st_out
BlockOracles.registry BlockOracles.ts_log)].
(* Step 2: Bound the Case b1 branch by HmsgCollGame *)
have H_hb1 :
Pr[BlockingGame(A).main() @ &m :
res /\ is_hmsg_coll BlockOracles.pk BlockingGame.h_st_out
BlockOracles.registry BlockOracles.ts_log] <=
Pr[HmsgCollGame(HmsgCollReduction(A)).main() @ &m : res].
- by byequiv hb1_game_coupling.
(* Step 3: Bound the remaining branch (Cases a and b2) *)
have H_hnb1 :
Pr[BlockingGame(A).main() @ &m :
res /\ !is_hmsg_coll BlockOracles.pk BlockingGame.h_st_out
BlockOracles.registry BlockOracles.ts_log] <=
Pr[HregCollGame(HregCollReduction(A)).main() @ &m : res] +
Pr[EFCMA_Game(ForgerReduction(A)).main() @ &m : res].
- (* Swap to HregBlockingGame to expose the us_src log *)
have -> :
Pr[BlockingGame(A).main() @ &m :
res /\ !is_hmsg_coll BlockOracles.pk BlockingGame.h_st_out
BlockOracles.registry BlockOracles.ts_log] =
Pr[HregBlockingGame(A).main() @ &m :
res /\ !is_hmsg_coll HregBlockOracles.pk HregBlockingGame.h_st_out
HregBlockOracles.registry HregBlockOracles.ts_log].
+ byequiv hswap_game_coupling => // /#.
(* Split on the hreg collision event (Case a) *)
rewrite Pr[mu_split (is_hreg_coll HregBlockOracles.pk HregBlockingGame.h_st_out
HregBlockOracles.us_src)].
(* Bound Case a by HregCollGame *)
have H_ha :
Pr[HregBlockingGame(A).main() @ &m :
res /\ !is_hmsg_coll HregBlockOracles.pk HregBlockingGame.h_st_out
HregBlockOracles.registry HregBlockOracles.ts_log /\
is_hreg_coll HregBlockOracles.pk HregBlockingGame.h_st_out
HregBlockOracles.us_src] <=
Pr[HregCollGame(HregCollReduction(A)).main() @ &m : res].
+ byequiv ha_game_coupling => // /#.
(* Bound Case b2 by EFCMA_Game *)
have H_hb2 :
Pr[HregBlockingGame(A).main() @ &m :
res /\ !is_hmsg_coll HregBlockOracles.pk HregBlockingGame.h_st_out
HregBlockOracles.registry HregBlockOracles.ts_log /\
!is_hreg_coll HregBlockOracles.pk HregBlockingGame.h_st_out
HregBlockOracles.us_src] <=
Pr[EFCMA_Game(ForgerReduction(A)).main() @ &m : res].
+ byequiv hb2_game_coupling => // /#.
(* Combine Case a and Case b2 bounds *)
smt().
(* Final combination of all bounds (H_hb1 H_hnb1) *)
smt().
qed.
end section BlockingSecurity.
(*
Security summary
================
For any blocking adversary A:
Adv^block(A) ≤ Adv^hreg-coll( HregCollReduction(A) )
+ Adv^hmsg-coll( HmsgCollReduction(A) )
+ Adv^EUF-CMA( ForgerReduction(A) )
Blocking security reduces to:
1. Collision resistance of H applied to (pk, h_st) -- case (a)
2. Collision resistance of H applied to (h_st, h_tx) -- case (b1)
3. EUF-CMA security of the signature scheme -- case (b2)
Note: Security against Blocking proof in the paper combines cases (a) and
(b1) into a single collision-finder A_coll and uses one collision
game. This formalization keeps them separate (one game per hash
domain) for precision; the bound differs only by a factor of 2.
*)