In ZKBoo, computing soundness parameters is straightforward - (1/3) ^ FSrounds
. However in V2, I'm getting somewhat odd numbers. Script as follows:
import math
def calc(bits,mpcRounds,opened,parties):
# V1
# * soundness fixed to 1/3 as adversary controls 2 of 3 values
soundness=3/2
# V2
# * adversary has to to embed his party into half of all mpcRounds,
# to maximize hit rate of the sampler.
# * 50% chance to hit the correct sample
# if it hits, it must also meet adversarial party, chance 1-in-n?
if parties > 3:
soundness = 2 * parties
invP = soundness**opened
return "bits=%d, mpcRounds=%d, opened=%d, parties=%d, soundness=%d" % (bits,mpcRounds,opened,parties,int(math.log2(invP)))
print("== PicnicV1 parameters, 3 party => soundness")
print("L1",calc(128, 219, 219, 3))
print("L3",calc(192, 329, 329, 3))
print("L5",calc(256, 438, 438, 3))
print()
print("== PicnicV2 parameters, 64 party => soundness")
print("L1",calc(128, 343, 27, 64))
print("L3",calc(192, 570, 39, 64))
print("L5",calc(256, 803, 50, 64))
print()
Results:
== PicnicV1 parameters, 3 party => soundness
L1 bits=128, mpcRounds=219, opened=219, parties=3, soundness=128
L3 bits=192, mpcRounds=329, opened=329, parties=3, soundness=192
L5 bits=256, mpcRounds=438, opened=438, parties=3, soundness=256
== PicnicV2 parameters, 64 party => soundness
L1 bits=128, mpcRounds=343, opened=27, parties=64, soundness=189
L3 bits=192, mpcRounds=570, opened=39, parties=64, soundness=273
L5 bits=256, mpcRounds=803, opened=50, parties=64, soundness=350
The soundness values seem overshot, and I'm not sure why that is. Compensation for birthday paradox (from where?). Or multitarget attacks? Or is my fault chance formula plain wrong?
In the unlikely event the values are indeed overshot, one could do something like:
print("== Tight parameters, 64party => soundness")
print("X1",calc(128, 256, 19, 64))
print("X3",calc(192, 384, 28, 64))
print("X5",calc(256, 512, 37, 64))
print()
print("== Tight parameters, 256party => soundness")
print("Y1",calc(128, 256, 15, 256))
print("Y3",calc(192, 384, 22, 256))
print("Y5",calc(256, 512, 30, 256))
print()
print("== Tight+64bit slack, 256party => soundness")
print("Z1",calc(128, 256, 22, 256))
print("Z3",calc(192, 384, 30, 256))
print("Z5",calc(256, 512, 36, 256))
print()
Resulting:
== Tight parameters, 64party => soundness
X1 bits=128, mpcRounds=256, opened=19, parties=64, soundness=133
X3 bits=192, mpcRounds=384, opened=28, parties=64, soundness=196
X5 bits=256, mpcRounds=512, opened=37, parties=64, soundness=259
== Tight parameters, 256party => soundness
Y1 bits=128, mpcRounds=256, opened=15, parties=256, soundness=135
Y3 bits=192, mpcRounds=384, opened=22, parties=256, soundness=198
Y5 bits=256, mpcRounds=512, opened=30, parties=256, soundness=270
== Tight+64bit slack, 256party => soundness
Z1 bits=128, mpcRounds=256, opened=22, parties=256, soundness=198
Z3 bits=192, mpcRounds=384, opened=30, parties=256, soundness=270
Z5 bits=256, mpcRounds=512, opened=36, parties=256, soundness=324
256-party variant might be worth pursuing in any case to save roughly 20% signature size.