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.pubkey sJ = multiply(self.s, J.serialize(), False) e_int = decode(self.e, 256) minus_e = encode(-e_int % N, 256, minlen=32) minus_e_P = multiply(minus_e, self.P.serialize(), False) minus_e_P2 = multiply(minus_e, self.P2.serialize(), False) KGser = add_pubkeys([sG.serialize(), 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.serialize() + self.P2.serialize()).digest() if e_check == self.e: return True #commitment fails for any NUMS in the provided range return False
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).pubkey KJ = multiply(k, J.serialize(), False, return_serialized=False) self.P2 = getP2(self.priv, J) self.get_commitment() self.e = hashlib.sha256(''.join( [x.serialize() for x in [KG, KJ, self.P, self.P2]])).digest() k_int = decode(k, 256) priv_int = decode(self.priv.private_key, 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 __init__(self, u=None, priv=None, P=None, P2=None, s=None, e=None, used=False): #This class allows storing of utxo in format "txid:n" only for #convenience of storage/access; it doesn't check or use the data. #Arguments must be provided in hex. self.u = u if not priv: if P: #Construct a pubkey from raw hex self.P = podle_PublicKey(binascii.unhexlify(P)) else: self.P = None else: if P: raise PoDLEError("Pubkey should not be provided with privkey") #any other formatting abnormality will just throw in PrivateKey if len(priv) == 66 and priv[-2:] == '01': priv = priv[:-2] self.priv = podle_PrivateKey(binascii.unhexlify(priv)) self.P = self.priv.pubkey if P2: self.P2 = podle_PublicKey(binascii.unhexlify(P2)) else: self.P2 = None #These sig values should be passed in hex. self.s = None self.e = None if s: self.s = binascii.unhexlify(s) if e: self.e = binascii.unhexlify(e) #Optionally maintain usage state (boolean) self.used = used #the H(P2) value self.commitment = None