def deserialize_proof(self, proofstr): """Extract the points and scalars as per comments to get_proof_serialized; this is obviously dumb and no appropriate sanity checking; TODO """ Ap = proofstr[:33] Sp = proofstr[33:66] T1p = proofstr[66:99] T2p = proofstr[99:132] #these are to be passed in as integers: tau_x = decode(proofstr[132:164], 256) mu = decode(proofstr[164:196], 256) t = decode(proofstr[196:228], 256) a = proofstr[228:260] b = proofstr[260:292] import math arraylen = int(math.log(self.bitlength, 2)) ctr = 292 Ls = [] Rs = [] for i in range(arraylen): Ls.append(proofstr[ctr:ctr + 33]) ctr += 33 for i in range(arraylen): Rs.append(proofstr[ctr:ctr + 33]) ctr += 33 return (Ap, Sp, T1p, T2p, tau_x, mu, t, (a, b, Ls, Rs))
def get_proof_recursive(self, a, b, P, g, h, n): """The prover starts with the full a*, b*, then recursively constructs the case n=1 where the proof is output in the form a', b', these are scalars, and c' = a' * b'. This will be checked by the verifier against the modified P', which the verifier can calculate independently, and it should satisfy P' = a'*G_1 + b'*H_1 + c'*U. So the prover must provide (L[], R[], a', b') as output to the verifier. The verifier checks against the pre-known P and c. """ if n == 1: #return the tuple: a', b', L[], R[] #note total size is 2 * scalar_size + log(n) * 2 * point_size return (a[0], b[0], self.L, self.R) #Split the existing vectors into halves aL, aR = halves(a) bL, bR = halves(b) gL, gR = halves(g) hL, hR = halves(h) self.L.append(IPC(aL, bR, g=gR, h=hL, u=self.U).get_commitment()) self.R.append(IPC(aR, bL, g=gL, h=hR, u=self.U).get_commitment()) x, xb, x_sq, x_sqb, xinv, xinvb, x_sq_inv, x_sq_invb = self.fiat_shamir( self.L[-1], self.R[-1], P) #Construct change of coordinates for base points, and for vector terms gprime = [] hprime = [] aprime = [] bprime = [] for i in range(n / 2): gprime.append( add_pubkeys([ multiply(xinvb, g[i], False), multiply(xb, g[i + n / 2], False) ], False)) hprime.append( add_pubkeys([ multiply(xb, h[i], False), multiply(xinvb, h[i + n / 2], False) ], False)) aprime.append( encode( (x * decode(a[i], 256) + xinv * decode(a[i + n / 2], 256)) % N, 256, 32)) bprime.append( encode( (xinv * decode(b[i], 256) + x * decode(b[i + n / 2], 256)) % N, 256, 32)) Pprime = add_pubkeys([ P, multiply(x_sqb, self.L[-1], False), multiply(x_sq_invb, self.R[-1], False) ], False) return self.get_proof_recursive(aprime, bprime, Pprime, gprime, hprime, n / 2)
def inner_product(a, b, vtype="bin"): assert len(a) == len(b) assert isinstance(a, list) assert isinstance(b, list) c = 0 for i in range(len(a)): if vtype == "bin": c += decode(a[i], 256) * decode(b[i], 256) else: c += a[i] * b[i] c = c % N if vtype == "bin": c = encode(c, 256, 32) return c
def verify(self, commitment, index_range): """For an object created without a private key, check that the opened commitment verifies for at least one NUMS point as defined by the range in index_range """ if not all([self.P, self.P2, self.s, self.e]): raise PoDLEError("Verify called without sufficient data") if not self.get_commitment() == commitment: return False for J in [getNUMS(i) for i in index_range]: sig_priv = podle_PrivateKey(self.s) sG = sig_priv.public_key sJ = multiply(self.s, J.format(), False) e_int = decode(self.e, 256) minus_e = encode(-e_int % N, 256, minlen=32) minus_e_P = multiply(minus_e, self.P.format(), False) minus_e_P2 = multiply(minus_e, self.P2.format(), False) KGser = add_pubkeys([sG.format(), minus_e_P], False) KJser = add_pubkeys([sJ, minus_e_P2], False) #check 2: e =?= H(K_G || K_J || P || P2) e_check = hashlib.sha256(KGser + KJser + self.P.format() + self.P2.format()).digest() if e_check == self.e: return True #commitment fails for any NUMS in the provided range return False
def range_verifier(proof, negetive, V, rangebits): rp2 = RangeProof(rangebits) A, S, T1, T2, tau_x, mu, t, iproof = rp2.deserialize_proof(proof) print("Now attempting to verify a proof in range: 0 -", 2**rangebits) if negetive: #As mentioned in comments above, here create a Pedersen commitment #to our actual value, which is out of range, with the same blinding #value. Varg = PC(encode(2**128 + decode(os.urandom(32), 256), 256, minlen=32), blinding=os.urandom(32)).get_commitment() else: Varg = V if not rp2.verify(A, S, T1, T2, tau_x, mu, t, iproof, Varg): if not negetive: print('Rangeproof should have verified but is invalid; bug.') return False else: print( "Rangeproof fail, as it should because value is not in range.") return True else: if not negetive: print('Rangeproof verified correctly, as expected.') return False else: print( "Rangeproof succeeded but it should not have, value is not in range; bug." ) return True return negetive
def inner_product_verifier(n, V, prf): final_pc, pc2, a, b, L, R, comm1 = prf # Simulate a challenge e, TODO: use fiat shamir later e = hashlib.sha256(V).digest() e = decode(e, 256) lhs = final_pc rhs = ecmult(e, V, False) rhs_2 = pc2 rhs = ecadd_pubkeys([rhs, rhs_2], False) print("**Verifying Inner product: **") #Note that the 'a' and 'b' vectors in the following constructor are dummy #values, they only set the length: verifier_ipc = IPC(["\x01"] * n, ["\x02"] * n) result = verifier_ipc.verify_proof(a, b, comm1, L, R) print("Verification result: ", result) if lhs == rhs: print("Positive Number") return True else: print("Negetive Number") return False
def ecmult(scalar, point, usehex, rawpub=True, return_serialized=True): if isinstance(scalar, (int, long)): scalar = encode(scalar, 256, minlen=32) if decode(scalar, 256) == 0: return None return multiply(scalar, point, usehex, rawpub=rawpub, return_serialized=return_serialized)
def inner_product_prover(w, xi, n, V, gamma): w_vec = Vector(w) xi_vec = Vector(xi) value = xi_vec.inner_product(w_vec) randints = [decode(os.urandom(32), 256) for _ in range(n)] b_vec = Vector(randints) # Simulate a challenge e, TODO: use fiat shamir later e = hashlib.sha256(V).digest() e = decode(e, 256) w_blind_vec = w_vec.scalar_mult(e).add(b_vec) blinded_commit_product = xi_vec.inner_product(b_vec) blinded_final_product = xi_vec.inner_product(w_blind_vec) r0 = os.urandom(32) pc2 = PC(blinded_commit_product, blinding=r0) r2 = (decode(gamma, 256) * e + decode(r0, 256)) % N r2_enc = encode(r2, 256, 32) final_pc = PC(blinded_final_product, blinding=r2) w_blind_enc = [encode(x, 256, 32) for x in w_blind_vec.v] xi_enc = [encode(x, 256, 32) for x in xi_vec.v] ipc1 = IPC(w_blind_enc, xi_enc) comm1 = ipc1.get_commitment() # print('generated commitment: ', binascii.hexlify(comm1)) proof = ipc1.generate_proof() a, b, L, R = proof print('generated proof: ') print('a: ', binascii.hexlify(a)) print('b: ', binascii.hexlify(b)) print('L: ', [binascii.hexlify(_) for _ in L]) print('R: ', [binascii.hexlify(_) for _ in R]) print( 'Total byte length is: ', len(a) + len(b) + len(L) * len(L[0]) + len(R) * len(R[0]) + len(comm1) + len(final_pc.get_commitment()) + len(pc2.get_commitment())) print('Length of L, R array: ', len(L)) prf = final_pc.get_commitment(), pc2.get_commitment(), a, b, L, R, comm1 return prf
def generate_podle(self, index=0, k=None): """Given a raw private key, in hex format, construct a commitment sha256(P2), which is the hash of the value x*J, where x is the private key as a raw scalar, and J is a NUMS alternative basepoint on the Elliptic Curve; we use J(i) where i is an index, so as to be able to create multiple commitments against the same privkey. The procedure for generating the J(i) value is shown in getNUMS(). Also construct a signature (s,e) of Schnorr type, which will serve as a zero knowledge proof that the private key of P2 is the same as the private key of P (=x*G). Signature is constructed as: s = k + x*e where k is a standard 32 byte nonce and: e = sha256(k*G || k*J || P || P2) Possibly Joinmarket specific comment: Users *should* generate with lower indices first, since verifiers will give preference to lower indices (each verifier may have their own policy about how high an index to allow, which really means how many reuses of utxos to allow in Joinmarket). Returns a commitment of form H(P2) which, note, will depend on the index choice. Repeated calls will reset the commitment and the associated signature data that can be used to open the commitment. """ #TODO nonce could be rfc6979? if not k: k = os.urandom(32) J = getNUMS(index) KG = podle_PrivateKey(k).public_key KJ = multiply(k, J.format(), False, return_serialized=False) self.P2 = getP2(self.priv, J) self.get_commitment() self.e = hashlib.sha256(b''.join( [x.format() for x in [KG, KJ, self.P, self.P2]])).digest() k_int = decode(k, 256) priv_int = decode(self.priv.secret, 256) e_int = decode(self.e, 256) sig_int = (k_int + priv_int * e_int) % N self.s = encode(sig_int, 256, minlen=32) return self.reveal()
def fiat_shamir(self, data, nret=2): """Generates nret integer challenge values from the current interaction (data) and the previous challenge values (self.fsstate), thus fulfilling the requirement of basing the challenge on the transcript of the prover-verifier communication up to this point. """ xb = hashlib.sha256("".join([self.fsstate] + [str(_) for _ in data])).digest() challenges = [] for i in range(nret): challenges.append(decode(xb, 256)) xb = hashlib.sha256(xb).digest() self.fsstate = xb return challenges
def run_test_VPC(): rawv = raw_input("Enter a vector cseparated: ") v = [int(x) for x in rawv.split(',')] vpc = VPC(v, v, vtype="int") print("Successfully created the pedersen commitment to: ", rawv) C = vpc.get_commitment() print("Here is the commitment: ", binascii.hexlify(C)) rawv2 = raw_input("Test homomorphism: enter second vector: ") v2 = [int(x) for x in rawv2.split(',')] vpc2 = VPC(v2, v2, vtype="int") C2 = vpc2.get_commitment() print("Here is the commitment for the second vector: ", binascii.hexlify(C2)) assert len(v2) == len(v), "try again" sumv = [x + y for x, y in zip(v, v2)] print('here is sumv: ', sumv) newc = encode((decode(vpc.c, 256) + decode(vpc2.c, 256)) % N, 256, 32) print("here is newc len: ", len(newc)) sumvpc = VPC(sumv, sumv, vtype="int") #reset the blinding value sumvpc.set_blinding(c=newc) Csum = sumvpc.get_commitment() print("Here is the commitment to the sum: ", binascii.hexlify(Csum)) print("Here is the sum of C and C2: ", binascii.hexlify(ecadd_pubkeys([C, C2], False))) if Csum == ecadd_pubkeys([C, C2], False): print("Successly verified homomorphism") else: print("Homomorphism failed to verify.") exit(0) #test out opening commitments if not verify_opening(C, vpc.c, v, v, vtype="int"): print("V1 did not verify") if not verify_opening(C2, vpc2.c, v2, v2, vtype="int"): print("V2 did not verify") if not verify_opening(Csum, sumvpc.c, sumv, sumv, vtype="int"): print("Vsum did not verify")
def fiat_shamir(self, L, R, P): """Generates a challenge value x from the "transcript" up to this point, using the previous hash, and uses the L and R values from the current iteration, and commitment P. Returned is the value of the challenge and its modular inverse, as well as the squares of those values, both as integers and binary strings, for convenience. """ xb = hashlib.sha256("".join([self.fsstate] + [str(_) for _ in [L, R, P]])).digest() self.fsstate = xb x = decode(xb, 256) % N x_sq = (x * x) % N xinv = modinv(x, N) x_sq_inv = (xinv * xinv) % N x_sqb, xinvb, x_sq_invb = [ encode(_, 256, 32) for _ in [x_sq, xinv, x_sq_inv] ] return (x, xb, x_sq, x_sqb, xinv, xinvb, x_sq_inv, x_sq_invb)
def generate_proof(self, value): """Given the value value, follow the algorithm laid out on p.16, 17 (section 4.2) of paper for prover side. """ # generate Pederson commitment for value self.fsstate = "" self.value = value self.gamma = os.urandom(32) pc = PC(encode(self.value, 256, minlen=32), blinding=self.gamma) # generate the 3 conditions self.V = pc.get_commitment() self.aL = Vector(value, self.bitlength) self.aR = self.aL.subtract([1] * self.bitlength) # assert checks assert self.aL.hadamard(self.aR).v == Vector([0] * self.bitlength).v assert self.aL.inner_product(PowerVector(2, self.bitlength)) == value # Get the commitment A self.alpha = self.get_blinding_value() # print("Maybe") self.A = VPC(self.aL.v, self.aR.v, vtype="int", u=getNUMS(255).serialize()) self.A.set_blinding(c=self.alpha) self.A.get_commitment() # get the 3 corresponding blinding vectors/values to convert proof into zero knowledge self.rho = self.get_blinding_value() self.sL = self.get_blinding_vector() self.sR = self.get_blinding_vector() # create a commitment to the blinding vector self.S = VPC(self.sL.v, self.sR.v, vtype="int", u=getNUMS(255).serialize()) self.S.set_blinding(c=self.rho) self.S.get_commitment() # generate the challenges y and z as per fiat shamir hueristic self.y, self.z = self.fiat_shamir([self.V, self.A.P, self.S.P]) self.z2 = (self.z * self.z) % N self.zv = Vector([self.z] * self.bitlength) #construct l(X) and r(X) coefficients; l[0] = constant term, l[1] linear term, #same for r(X) self.l = [] self.l.append(self.aL.subtract(self.zv)) self.l.append(self.sL) self.yn = PowerVector(self.y, self.bitlength) self.r = [] #0th coeff is y^n o (aR + z.1^n) + z^2 . 2^n self.r.append( self.yn.hadamard(self.aR.add(self.zv)).add( PowerVector(2, self.bitlength).scalar_mult(self.z2))) self.r.append(self.yn.hadamard(self.sR)) #constant term of t(X) = <l(X), r(X)> is the inner product of the #constant terms of l(X) and r(X) self.t0 = self.l[0].inner_product(self.r[0]) self.t1 = (self.l[0].inner_product(self.r[1]) + (self.l[1].inner_product(self.r[0]))) % N self.t2 = self.l[1].inner_product(self.r[1]) # we have constructed the polynomials l(x) r(x) and t(x) upto here self.tau1 = self.get_blinding_value() self.tau2 = self.get_blinding_value() self.T1 = PC(self.t1, blinding=self.tau1) self.T2 = PC(self.t2, blinding=self.tau2) # Since we know the values of we must also send commiments to remaining polynomial. # this is similar to what I did in vecotrs proving dot product # After commiting to the value we get the next challenge x66 self.x_1 = self.fiat_shamir( [self.T1.get_commitment(), self.T2.get_commitment()], nret=1)[0] self.mu = (self.alpha + self.rho * self.x_1) % N self.tau_x = (self.tau1 * self.x_1 + self.tau2 * self.x_1 * self.x_1 + \ self.z2 * decode(self.gamma, 256)) % N #lx and rx are vector-valued first degree polynomials evaluated at #the challenge value self.x_1 self.lx = self.l[0].add(self.l[1].scalar_mult(self.x_1)) self.rx = self.r[0].add(self.r[1].scalar_mult(self.x_1)) self.t = (self.t0 + self.t1 * self.x_1 + self.t2 * self.x_1 * self.x_1) % N assert self.t == self.lx.inner_product(self.rx) #Prover will now send tau_x, mu and t to verifier, and inner product argument #can be verified from this data. self.hprime = [] self.yinv = modinv(self.y, N) for i in range(1, self.bitlength + 1): self.hprime.append( ecmult(pow(self.yinv, i - 1, N), self.A.h[i - 1], False)) self.uchallenge = self.fiat_shamir([self.tau_x, self.mu, self.t], nret=1)[0] self.U = ecmult(self.uchallenge, getG(True), False) #On the prover side, need to construct an inner product argument: self.iproof = IPC(self.lx.v, self.rx.v, vtype="int", h=self.hprime, u=self.U) self.proof = self.iproof.generate_proof() #At this point we have a valid data set, but here is included a #sanity check that the inner product proof we've generated, actually verifies: self.iproof2 = IPC([1] * self.bitlength, [2] * self.bitlength, vtype="int", h=self.hprime, u=self.U) ak, bk, lk, rk = self.proof assert self.iproof2.verify_proof(ak, bk, self.iproof.get_commitment(), lk, rk)
def get_blinding_value(self): return decode(os.urandom(32), 256)