diff --git a/coconut/proofs_up.py b/coconut/proofs_up.py new file mode 100644 index 0000000..293d150 --- /dev/null +++ b/coconut/proofs_up.py @@ -0,0 +1,120 @@ +""" Coconut zero-knowledge proofs. """ +from petlib.bn import Bn +from bplib.bp import BpGroup +from hashlib import sha256 +from binascii import hexlify +from coconut.utils import ec_sum + + +def to_challenge(elements): + """ generates a Bn challenge by hashing a number of EC points """ + Cstring = b",".join([hexlify(x.export()) for x in elements]) + Chash = sha256(Cstring).digest() + return Bn.from_binary(Chash) + + +def make_pi_s(params, gamma, ciphertext, cm, k, r, public_m, private_m): + """ prove correctness of ciphertext and cm """ + (G, o, g1, hs, g2, e) = params + attributes = private_m + public_m + assert len(ciphertext) == len(k) and len(ciphertext) == len(private_m) + assert len(attributes) <= len(hs) + # create the witnesses + wr = o.random() + wk = [o.random() for _ in k] + wm = [o.random() for _ in attributes] + # compute h + h = G.hashG1(cm.export()) + # compute the witnesses commitments + Aw = [wki*g1 for wki in wk] + Bw = [wk[i]*gamma + wm[i]*h for i in range(len(private_m))] + Cw = wr*g1 + ec_sum([wm[i]*hs[i] for i in range(len(attributes))]) + # create the challenge + c = to_challenge([g1, g2, cm, h, Cw]+hs+Aw+Bw) + # create responses + rr = (wr - c * r) % o + rk = [(wk[i] - c*k[i]) % o for i in range(len(wk))] + rm = [(wm[i] - c*attributes[i]) % o for i in range(len(wm))] + return (c, rk, rm, rr) + + +def verify_pi_s(params, gamma, ciphertext, cm, proof): + """ verify orrectness of ciphertext and cm """ + (G, o, g1, hs, g2, e) = params + (a, b) = zip(*ciphertext) + (c, rk, rm, rr) = proof + assert len(ciphertext) == len(rk) + # re-compute h + h = G.hashG1(cm.export()) + # re-compute witnesses commitments + Aw = [c*a[i] + rk[i]*g1 for i in range(len(rk))] + Bw = [c*b[i] + rk[i]*gamma + rm[i]*h for i in range(len(ciphertext))] + Cw = c*cm + rr*g1 + ec_sum([rm[i]*hs[i] for i in range(len(rm))]) + # compute the challenge prime + return c == to_challenge([g1, g2, cm, h, Cw]+hs+Aw+Bw) + +def make_pi_s_up(params, Ls, commits, cm, r, public_m, private_m): + """ prove correctness of ciphertext and cm """ + (G, o, g1, hs, h_blind, g2, e) = params + attributes = private_m + public_m + assert len(commits) == len(private_m) + assert len(attributes) <= len(hs) + # create the witnesses + wr = o.random() + wL = [o.random() for _ in Ls] + wm = [o.random() for _ in attributes] + # compute h + h = G.hashG1(cm.export()) + # compute the witnesses commitments + Com_w = [wL[i]*h_blind + wm[i]*h for i in range(len(private_m))] + Cw = wr*g1 + ec_sum([wm[i]*hs[i] for i in range(len(attributes))]) + # create the challenge + c = to_challenge([g1, g2, cm, h, Cw]+hs+commits+Com_w) + # create responses + rr = (wr - c * r) % o + rL = [(wL[i] - c*Ls[i]) % o for i in range(len(wL))] + rm = [(wm[i] - c*attributes[i]) % o for i in range(len(wm))] + return (c, rL, rm, rr) + + +def verify_pi_s_up(params, commits, cm, proof): + """ verify orrectness of ciphertext and cm """ + (G, o, g1, hs, h_blind, g2, e) = params + (c, rL, rm, rr) = proof + assert len(commits) == len(rL) + # re-compute h + h = G.hashG1(cm.export()) + # re-compute witnesses commitments + Com_w = [c*commits[i] + rL[i] * h_blind + rm[i] * h for i in range(len(commits))] + Cw = c*cm + rr*g1 + ec_sum([rm[i]*hs[i] for i in range(len(rm))]) + # compute the challenge prime + return c == to_challenge([g1, g2, cm, h, Cw]+hs+commits+Com_w) + + +def make_pi_v(params, aggr_vk, sigma, private_m, t): + """ prove correctness of kappa and nu """ + (G, o, g1, hs, g2, e) = params + (g2, alpha, beta) = aggr_vk + (h, s) = sigma + # create the witnesses + wm = [o.random() for _ in private_m] + wt = o.random() + # compute the witnesses commitments + Aw = wt*g2 + alpha + ec_sum([wm[i]*beta[i] for i in range(len(private_m))]) + # create the challenge + c = to_challenge([g1, g2, alpha, Aw]+hs+beta) + # create responses + rm = [(wm[i] - c*private_m[i]) % o for i in range(len(private_m))] + rt = (wt - c*t) % o + return (c, rm, rt) + +def verify_pi_v(params, aggr_vk, sigma, kappa, pi_v): + """ verify correctness of kappa """ + (G, o, g1, hs, g2, e) = params + (g2, alpha, beta) = aggr_vk + (h, s) = sigma + (c, rm, rt) = pi_v + # re-compute witnesses commitments + Aw = c*kappa + rt*g2 + (1-c)*alpha + ec_sum([rm[i]*beta[i] for i in range(len(rm))]) + # compute the challenge prime + return c == to_challenge([g1, g2, alpha, Aw]+hs+beta) diff --git a/coconut/scheme_up.py b/coconut/scheme_up.py index 5a24ee7..dcb477a 100644 --- a/coconut/scheme_up.py +++ b/coconut/scheme_up.py @@ -4,7 +4,7 @@ """ from bplib.bp import BpGroup, G2Elem from coconut.utils import * -from coconut.proofs import * +from coconut.proofs_up import * def setup(q=1): @@ -190,3 +190,63 @@ def agg_cred(params, aggr_vk, sigs, Ls, threshold=True): aggr_sigma = (h[0], aggr_s) return aggr_sigma + + + + +def prove_cred(params, aggr_vk, sigma, private_m): + """ + Build cryptographic material for blind verify. + + Parameters: + - `params`: public parameters generated by `setup` + - `aggr_vk`: aggregated verification key + - `sigma`: credential + - `private_m` [Bn]: array containing the private attributes + + Returns: + - `Theta`: randomized credential and cryptographic material to verify them + """ + assert len(private_m) > 0 + (G, o, g1, hs, g2, e) = params + (g2, alpha, beta) = aggr_vk + (h, s) = sigma + assert len(private_m) <= len(beta) + r, r_prime = o.random(), o.random() + (h_prime , s_prime) = (r_prime*h , r_prime*s + r*r_prime*h) + sigma_prime =(h_prime, s_prime) + kappa = r*g2 + alpha + ec_sum([private_m[i]*beta[i] for i in range(len(private_m))]) + pi_v = make_pi_v(params, aggr_vk, sigma_prime, private_m, r) + Theta = (kappa, sigma_prime, pi_v) + return Theta + + +def verify_cred(params, aggr_vk, Theta, public_m=[]): + """ + Verify credentials. + + Parameters: + - `params`: public parameters generated by `setup` + - `aggr_vk`: aggregated verification key + - `Theta`: credential and cryptographic material to verify them + - `public_m` [Bn]: optional, array containing the public attributes + + Returns: + - `ret` (bool): whether the credential verifies + """ + (G, o, g1, hs, g2, e) = params + (g2, _, beta) = aggr_vk + (kappa, sigma, pi_v) = Theta + (h, s) = sigma + private_m_len = len(pi_v[1]) + coco_ensure(len(public_m)+private_m_len <= len(beta), "Too many parameters") + # verify proof of correctness + coco_ensure(verify_pi_v(params, aggr_vk, sigma, kappa, pi_v), "ZK proof failed") + # add clear text messages + aggr = G2Elem.inf(G) + if len(public_m) != 0: + aggr = ec_sum([public_m[i]*beta[i+private_m_len] for i in range(len(public_m))]) + # verify + return not h.isinf() and e(h, kappa+aggr) == e(s, g2) + + diff --git a/tests/test_coconut_up.py b/tests/test_coconut_up.py index b0a51ce..abf944b 100644 --- a/tests/test_coconut_up.py +++ b/tests/test_coconut_up.py @@ -1,5 +1,5 @@ from coconut.scheme_up import setup, prepare_blind_sign, keygen, agg_key, blind_sign, agg_cred -from coconut.scheme import prove_cred, verify_cred +from coconut.scheme_up import prove_cred, verify_cred def test_multi_authority(): q = 7 # number of attributes @@ -35,4 +35,4 @@ def test_multi_authority(): Theta = prove_cred(min_params, min_aggr_vk, sigma, private_m) # verify credentials - verify_cred(min_params, min_aggr_vk, Theta, public_m=public_m) + assert verify_cred(min_params, min_aggr_vk, Theta, public_m=public_m)