VRF=(Gen,Hash,Prove,Verify)
VRF.Gen() -> (sk, pk)
VRF.Prove(sk, alpha) -> prf
VRF.Hash(prf) -> rnd – deterministic in sk, alpha?
VRF.Verify(pk, alpha, prf) -> 2
forall pk, alpha exists! prf: VRF.Verify(pk, alpha, prf) = 1
hard to find pk, alpha, (prf1, prf2: VRF.Verify(pk, alpha, prfi) = 1) such that VRF.Hash(prf1) != VRF.Hash(prf2)
forall pk, alpha1, alpha2 => VRF.Hash(VRF.Prove(pk, alpha1)) != VRF.Hash(VRF.Prove(pk, alpha2))
hard to find pk, (alpha1, alpha2, prf1, prf2: VRF.Verify(pk, alphai, prfi) = 1) such that VRF.Hash(prf1) == VRF.Hash(prf2)
If sk and prf are unknown then hash=VRF.Hash(prf) is indistinguishable from random for any alpha
Similar to pseudorandomness property but with "good-enough" key gen (ie. with enough entropy)
Two variants: with or without key validation (KV).
ECVRF(sk, alpha) -> rnd
Properties (mostly follow from properties of ZKP, Hash, KDF):
ECVRF.Gen(sk) -> pk
ECVRF.Prove(sk, alpha, [salt]) -> prf
ECVRF.Hash(prf) -> Option<rnd>
ECVRF.Verify(pk, alpha, prf, [salt], KV) -> 2 // KV: 2; validate keys or not
ECVRF.encode_to_curve(salt, alpha) -> H – hash to curve
ECVRF.nonce_gen(sk, h) -> k – deterministic gen nonce scalar
ECVRF.challenge_gen(Y, H, X, U, V) -> c – transcript/hash
ECVRF.decode_prf(prf) -> Option<X, c, s> – parse/validate PK and scalars
ECVRF.validate_key(Y) -> 2 – for curve25519 there's a list of 8 bad points, so use lookup instead of cofactor*
TODO:
BLS.KeyGen(sk) -> pk = sk G_2
BLS.Sign(sk; m) -> sig = sk H(m)
BLS.Verify(pk; m) -> 2 = e(H(m), pk) =? e(sig, G_2)
BLS.VRF(sk; alpha) -> rnd = H(sk H(alpha))
BLS is essentially DlEq proof.
Schnorr DlEq:
BLS-dl.KeyGen(sk) -> pk = sk G
BLS-dl.Sign(sk; m) -> sig
BLS-dl.Verify(pk; m, (s,R,X,T)) -> 2
BLS-dl.Verify(pk; m, (s,c,T)) -> 2
Pairing allows to verify DlEq without an explicit proof.
Schnorr requires an explicit proof for DlEq.
Standard way for DVRF:
DRB vs VRF:
BLS.Sign(sk,m) = sk H(m)
BLS.Verify(pk,sig,m) = e(H(m),pk) ?= e(sig,G)
aggregation: {sig_i} => sig=\sum_i sig_i
verify: e(sig,G) ?= \sum_i e(H(m_i),pk_i)
rogue-key attack (with m_1=m_2): pk_2(a) = aG - pk_1
defense: Prove KoSK, use different m_1!=m_2
Constructions: MSP, MSP-pop, AMSP, AMSP-pop, ASM, MSDL, MSDL-pop
MSP.KeyGen(sk_i) -> pk_i = sk_i G_2
MSP.Agg({pk_i}) -> A_i = H(pk_i,{pk_j})
MSP.KeyAgg({pk_i}) -> apk = \sum_i A_i pk_i
MSP.Sign({pk_i}, sk_i, m) -> sig_i = (sk_i A_i) H(m)
MSP.SigAgg({sig_i}) -> sig = \sum_i sig_i
MSP.Verify(apk, m, sig) -> 2 = e(sig,G_2) ?= e(H(m), apk)
MSP.BatchVerify …
Proof-of-possession variant of MSP. Simpler KeyAgg, but instead public keys are shared with PoP:
MSP-pop.KeyGen(sk_i) -> (pk_i, prf_i) = (sk_i G_2, sk_i H(pk_i))
MSP-pop.Agg({pk_i}) -> A_i = 1
MSP-KeyAgg({pk_i}) -> apk = \sum_i pk_i
…
AMSP := MSP
AMSP.Sign({pk_i}, sk_i, m) -> sig_i = (sk_i A_i) H(apk, m)
AMSP.Verify …
AMSP.BatchVerify …
Similar to MSP-pop.
ASM.KeyGen := MSP.KeyGen
ASM.KeyAgg := MSP.KeyAgg
ASM.GroupSetup(sk_i, {pk_i}) -> mk_i
MSDL.KeyGen = MSP.KeyGen
MSDL.Agg := MSP.Agg
MSDL.KeyAgg := MSP.KeyAgg
MSDL.Sign({pk_i}, sk_i, m) -> sig
Extra round in KeyGen?
ASM Schemes
Feldman VSS + PoP, subgroup unknown before signing
vASM.KeyGen(sk_i) -> pk_i = sk_i G_2
vASM.GroupSetup({pk_i}, S={1..n}, sk_i) -> (mk_i, C)
vASM.Sign(mk_i, m) -> sig_i = mk_i H(m) // send to combiner
vASM.SigAgg(S, {sig_i}) -> sig = \sum_{i in S} sig_i
vASM.Verify(C, S, m, sig) -> 2 = e(sig, G_2) ?= e(H(m), \sum_k (\sum_{i in S} i^k) C_k)
vASM.Verify(C, S, m, sig) does DlEq verification:
Dl_{H(m)}(sig) ?= Dl_{G_2}(\sum_k (\sum_{i in S} i^k) C_k)
Let's make it explicit with Schnorr:
vASM-dl.KeyGen
vASM-dl.GroupSetup -> (mk_i, C) // Feldman DKG, mk_i G = f© – public key reconstructed from commitments C
vASM-dl.Sign(mk_i, r_i, m) -> sig_i
Explicit Subgroup
ASMwSA.KeyGen := vASM.KeyGen
ASMwSA.Agg({pk_i}) -> A_i = H(pk_i, {pk_i})
ASMwSA.KeyAgg({pk_i}) -> apk = \sum_i A_i pk_i
ASMwSA.GroupSetup := ASM.GroupSetup?
ASMwSA.GroupSetup({pk_i}) -> m_{i,j} = (A_i sk_i) H(apk, j)
ASMwSA.Sign({m_{i,j}}, S, m, sk_i) -> sig_i
ASMwSA.SigAgg({pk_i}, S, {sig_i}) -> (spk, sig) = (\sum_{i in S} A_i pk_i, \sum_{i in S} sig_i)
ASMwSA.Verify(apk, (spk, sig), S, m) -> 2 = e(sig, G_2) ?= e(H(apk,m) + \sum_{j in S} H(apk, j), spk)
Designated combiner, subgroup unknown before signing
ASMwCA.KeyGen := ASMwSA.KeyGen
ASMwCA.Agg := ASMwSA.Agg
ASMwCA.KeyAgg := ASMwSA.KeyAgg
ASMwCA.GroupSetup := ASMwSA.GroupSetup
ASMwCA.Sign({m_{i,j}}, S, m, sk_i) -> sig_i = m_{i,i} + (A_i sk_i) H(apk, m)
ASMwCA.SigAgg({m_{i,j}}, {pk_i}, S, {sig_i}) -> (spk, sig) = (\sum_{i in S} A_i pk_i, \sum_{i in S} (sig_i + \sum_{j in S} m_{i,j}))
ASMwCA.Verify(apk, (spk, sig), S, m) -> 2 = e(sig, G_2) ?= e(H(apk,m) + \sum_{j in S} H(apk, j), spk)