def getNUMS(index=0): """Taking secp256k1's G as a seed, either in compressed or uncompressed form, append "index" as a byte, and append a second byte "counter" try to create a new NUMS base point from the sha256 of that bytestring. Loop counter and alternate compressed/uncompressed until finding a valid curve point. The first such point is considered as "the" NUMS base point alternative for this index value. The search process is of course deterministic/repeatable, so it's fine to just store a list of all the correct values for each index, but for transparency left in code for initialization by any user. The NUMS generator generated is returned as a secp256k1.PublicKey. """ assert index in range(256) nums_point = None for G in [getG(True), getG(False)]: seed = G + struct.pack(b'B', index) for counter in range(256): seed_c = seed + struct.pack(b'B', counter) hashed_seed = hashlib.sha256(seed_c).digest() #Every x-coord on the curve has two y-values, encoded #in compressed form with 02/03 parity byte. We just #choose the former. claimed_point = b"\x02" + hashed_seed try: nums_point = podle_PublicKey(claimed_point) return nums_point except: continue assert False, "It seems inconceivable, doesn't it?" # pragma: no cover
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 __init__(self, v, g=None, h=None, blinding=None): self.v = v self.g = getG(True) if not g else g self.h = getNUMS(255).serialize() if not h else h self.set_blinding(blinding) self.get_commitment()
def verify(self, Ap, Sp, T1p, T2p, tau_x, mu, t, proof, V): """Takes as input an already-deserialized rangeproof, along with the pedersen commitment V to the value (not here known), and checks if the proof verifies. """ #wipe FS state: self.fsstate = "" #compute the challenges to find y, z, x self.y, self.z = self.fiat_shamir([V, Ap, Sp]) self.z2 = (self.z * self.z) % N self.zv = Vector([self.z] * self.bitlength) self.x_1 = self.fiat_shamir([T1p, T2p], nret=1)[0] 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), getNUMS(self.bitlength + i).serialize(), False)) #construction of verification equation (61) #cmopute the dangling term onen = PowerVector(1, self.bitlength) twon = PowerVector(2, self.bitlength) yn = PowerVector(self.y, self.bitlength) self.k = (yn.inner_product(onen) * -self.z2) % N self.k = (self.k - (onen.inner_product(twon) * (pow(self.z, 3, N)))) % N self.gexp = (self.k + self.z * onen.inner_product(yn)) % N # this computes PC of <l,r> with t_x self.lhs = PC(t, blinding=tau_x).get_commitment() self.rhs = ecmult(self.gexp, getG(True), False) self.vz2 = ecmult((self.z * self.z) % N, V, False) self.rhs = ecadd_pubkeys([self.rhs, self.vz2], False) self.rhs = ecadd_pubkeys( [self.rhs, ecmult(self.x_1, T1p, False)], False) self.rhs = ecadd_pubkeys( [self.rhs, ecmult((self.x_1 * self.x_1) % N, T2p, False)], False) if not self.lhs == self.rhs: print("(61) verification check failed") print(binascii.hexlify(self.lhs)) print(binascii.hexlify(self.rhs)) return False #reconstruct P (62) # standard commitment check self.P = Ap self.P = ecadd_pubkeys([ecmult(self.x_1, Sp, False), self.P], False) # upto here P = A + x*S #now add g*^(-z) for i in range(self.bitlength): self.P = ecadd_pubkeys([ ecmult(-self.z % N, getNUMS(i + 1).serialize(), False), self.P ], False) # upto here P = A + x*S - g^(-z) #zynz22n is the exponent of hprime self.zynz22n = yn.scalar_mult(self.z).add( PowerVector(2, self.bitlength).scalar_mult(self.z2)) for i in range(self.bitlength): self.P = ecadd_pubkeys( [ecmult(self.zynz22n.v[i], self.hprime[i], False), self.P], False) # Here P value is computed correctly self.uchallenge = self.fiat_shamir([tau_x, mu, t], nret=1)[0] self.U = ecmult(self.uchallenge, getG(True), False) self.P = ecadd_pubkeys([ecmult(t, self.U, False), self.P], False) #P should now be : A + xS + -zG* + (zy^n+z^2.2^n)H'* + tU #One can show algebraically (the working is omitted from the paper) #that this will be the same as an inner product commitment to #(lx, rx) vectors (whose inner product is t), thus the variable 'proof' #can be passed into the IPC verify call, which should pass. #input to inner product proof is P.h^-(mu) self.Pprime = ecadd_pubkeys( [self.P, ecmult(-mu % N, getNUMS(255).serialize(), False)], False) #Now we can verify the inner product proof a, b, L, R = proof #dummy vals for constructor of verifier IPC self.iproof = IPC(["\x01"] * self.bitlength, ["\x02"] * self.bitlength, h=self.hprime, u=self.U) #self.iproof.P = self.Pprime if not self.iproof.verify_proof(a, b, self.Pprime, L, R): return False return True