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 sign_donation_tx(tx, i, priv): from bitcoin.main import fast_multiply, decode_privkey, G, inv, N from bitcoin.transaction import der_encode_sig k = sign_k hashcode = btc.SIGHASH_ALL i = int(i) if len(priv) <= 33: priv = btc.safe_hexlify(priv) pub = btc.privkey_to_pubkey(priv) address = btc.pubkey_to_address(pub) signing_tx = btc.signature_form( tx, i, btc.mk_pubkey_script(address), hashcode) msghash = btc.bin_txhash(signing_tx, hashcode) z = btc.hash_to_int(msghash) # k = deterministic_generate_k(msghash, priv) r, y = fast_multiply(G, k) s = inv(k, N) * (z + r * decode_privkey(priv)) % N rawsig = 27 + (y % 2), r, s sig = der_encode_sig(*rawsig) + btc.encode(hashcode, 16, 2) # sig = ecdsa_tx_sign(signing_tx, priv, hashcode) txobj = btc.deserialize(tx) txobj["ins"][i]["script"] = btc.serialize_script([sig, pub]) return btc.serialize(txobj)
def sign_donation_tx(tx, i, priv): from bitcoin.main import fast_multiply, decode_privkey, G, inv, N from bitcoin.transaction import der_encode_sig k = sign_k hashcode = btc.SIGHASH_ALL i = int(i) if len(priv) <= 33: priv = btc.safe_hexlify(priv) pub = btc.privkey_to_pubkey(priv) address = btc.pubkey_to_address(pub) signing_tx = btc.signature_form(tx, i, btc.mk_pubkey_script(address), hashcode) msghash = btc.bin_txhash(signing_tx, hashcode) z = btc.hash_to_int(msghash) # k = deterministic_generate_k(msghash, priv) r, y = fast_multiply(G, k) s = inv(k, N) * (z + r * decode_privkey(priv)) % N rawsig = 27 + (y % 2), r, s sig = der_encode_sig(*rawsig) + btc.encode(hashcode, 16, 2) # sig = ecdsa_tx_sign(signing_tx, priv, hashcode) txobj = btc.deserialize(tx) txobj["ins"][i]["script"] = btc.serialize_script([sig, pub]) return btc.serialize(txobj)
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()