def raw_parse(cls, s): '''Returns a HDPrivateKey from a stream''' # first 4 bytes are the version version = s.read(4) # check that the version is one of the TESTNET or MAINNET # private keys, if not raise a ValueError if version in (TESTNET_XPRV, TESTNET_YPRV, TESTNET_ZPRV): testnet = True elif version in (MAINNET_XPRV, MAINNET_YPRV, MAINNET_ZPRV): testnet = False else: raise ValueError('not an xprv, yprv or zprv: {}'.format(version)) # the next byte is depth depth = byte_to_int(s.read(1)) # next 4 bytes are the parent_fingerprint parent_fingerprint = s.read(4) # next 4 bytes is the child number in big-endian child_number = big_endian_to_int(s.read(4)) # next 32 bytes are the chain code chain_code = s.read(32) # the next byte should be b'\x00' if byte_to_int(s.read(1)) != 0: raise ValueError('private key should be preceded by a zero byte') # last 32 bytes should be the private key in big endian private_key = PrivateKey(secret=big_endian_to_int(s.read(32))) # return an instance of the class return cls( private_key=private_key, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=testnet, )
def raw_parse(cls, s): '''Returns a HDPublicKey from a stream''' # first 4 bytes are the version version = s.read(4) # check that the version is one of the TESTNET or MAINNET # public keys, if not raise a ValueError if version in (TESTNET_XPUB, TESTNET_YPUB, TESTNET_ZPUB): testnet = True elif version in (MAINNET_XPUB, MAINNET_YPUB, MAINNET_ZPUB): testnet = False else: raise ValueError('not an xpub, ypub or zpub: {} {}'.format(s, version)) # the next byte is depth depth = byte_to_int(s.read(1)) # next 4 bytes are the parent_fingerprint parent_fingerprint = s.read(4) # next 4 bytes is the child number in big-endian child_number = big_endian_to_int(s.read(4)) # next 32 bytes are the chain code chain_code = s.read(32) # last 33 bytes should be the SEC point = S256Point.parse(s.read(33)) # return an instance of the class return cls( point=point, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=testnet, )
def child(self, index): '''Returns the child HDPrivateKey at a particular index. Raises ValueError for indices >= 0x8000000. ''' # if index >= 0x80000000, raise a ValueError if index >= 0x80000000: raise ValueError('child number should always be less than 2^31') # data is the SEC compressed and the index in 4 bytes big-endian data = self.point.sec() + int_to_big_endian(index, 4) # get hmac_sha512 with chain code, data h = hmac_sha512(self.chain_code, data) # the new public point is the current point + # the first 32 bytes in big endian * G point = self.point + big_endian_to_int(h[:32]) * G # chain code is the last 32 bytes chain_code = h[32:] # depth is current depth + 1 depth = self.depth + 1 # parent_fingerprint is the fingerprint of this node parent_fingerprint = self.fingerprint() # child number is the index child_number = index # return the HDPublicKey instance return HDPublicKey( point=point, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=self.testnet, )
def from_seed(cls, seed, testnet=False): h = hmac_sha512(b'Bitcoin seed', seed) private_key = PrivateKey(secret=big_endian_to_int(h[:32])) chain_code = h[32:] return cls( private_key=private_key, chain_code=chain_code, testnet=testnet, )
def verify_message(self, message, sig): '''Verify a message in the form of bytes. Assumes that the z is calculated using hash256 interpreted as a big-endian integer''' # calculate the hash256 of the message h256 = hash256(message) # z is the big-endian interpretation. use big_endian_to_int z = big_endian_to_int(h256) # verify the message using the self.verify method return self.verify(z, sig)
def sign_message(self, message): '''Sign a message in the form of bytes instead of the z. The z should be assumed to be the hash256 of the message interpreted as a big-endian integer.''' # compute the hash256 of the message h256 = hash256(message) # z is the big-endian interpretation. use big_endian_to_int z = big_endian_to_int(h256) # sign the message using the self.sign method return self.sign(z)
def from_seed(cls, seed, testnet=False): # get hmac_sha512 with b'Bitcoin seed' and seed h = hmac_sha512(b'Bitcoin seed', seed) # create the private key using the first 32 bytes in big endian private_key = PrivateKey(secret=big_endian_to_int(h[:32])) # chaincode is the last 32 bytes chain_code = h[32:] # return an instance of the class return cls( private_key=private_key, chain_code=chain_code, testnet=testnet, )
def xprv_raw_parse(cls, s): version = s.read(4) if version in (TESTNET_XPRV, TESTNET_YPRV, TESTNET_ZPRV): testnet = True elif version in (MAINNET_XPRV, MAINNET_YPRV, MAINNET_ZPRV): testnet = False else: raise ValueError('not an xprv, yprv or zprv: {}'.format(version)) depth = byte_to_int(s.read(1)) parent_fingerprint = s.read(4) child_number = big_endian_to_int(s.read(4)) chain_code = s.read(32) if byte_to_int(s.read(1)) != 0: raise ValueError('private key should be preceded by a zero byte') private_key = PrivateKey(secret=big_endian_to_int(s.read(32))) return cls( private_key=private_key, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=testnet, )
def parse(cls, wif): '''Converts WIF to a PrivateKey object''' raw = raw_decode_base58(wif) if len(raw) == 34: # compressed if raw[-1] != 1: raise ValueError('Invalid WIF') raw = raw[:-1] secret = big_endian_to_int(raw[1:]) if raw[0] == 0xef: testnet = True elif raw[0] == 0x80: testnet = False else: raise ValueError('Invalid WIF') return cls(secret, testnet=testnet)
def xpub_child(self, index): if index >= 0x80000000: raise ValueError('child number should always be less than 2^31') data = self.point.sec() + int_to_big_endian(index, 4) h = hmac_sha512(key=self.chain_code, msg=data) point = self.point + big_endian_to_int(h[:32]) * G chain_code = h[32:] depth = self.depth + 1 parent_fingerprint = self.fingerprint() child_number = index return HDPublicKey( point=point, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=self.testnet, )
def sig_hash(self, input_index, redeem_script=None): '''Returns the integer representation of the hash that needs to get signed for index input_index''' # create the serialization per spec # start with version: int_to_little_endian in 4 bytes s = int_to_little_endian(self.version, 4) # next, how many inputs there are: encode_varint s += encode_varint(len(self.tx_ins)) # loop through each input: for i, tx_in in enumerate(self.tx_ins) for i, tx_in in enumerate(self.tx_ins): # if the input index is the one we're signing if i == input_index: # if the RedeemScript was passed in, that's the ScriptSig if redeem_script: script_sig = redeem_script # otherwise the previous tx's ScriptPubkey is the ScriptSig else: script_sig = tx_in.script_pubkey(self.testnet) # Otherwise, the ScriptSig is empty else: script_sig = None # create a TxIn object with the prev_tx, prev_index and sequence # the same as the current tx_in and the script_sig from above new_tx_in = TxIn( prev_tx=tx_in.prev_tx, prev_index=tx_in.prev_index, script_sig=script_sig, sequence=tx_in.sequence, ) # add the serialization of the TxIn object s += new_tx_in.serialize() # add how many outputs there are using encode_varint s += encode_varint(len(self.tx_outs)) # add the serialization of each output for tx_out in self.tx_outs: s += tx_out.serialize() # add the locktime using int_to_little_endian in 4 bytes s += int_to_little_endian(self.locktime, 4) # add SIGHASH_ALL using int_to_little_endian in 4 bytes s += int_to_little_endian(SIGHASH_ALL, 4) # hash256 the serialization h256 = hash256(s) # convert the result to an integer using big_endian_to_int(x) return big_endian_to_int(h256)
def sig_hash_bip143(self, input_index, redeem_script=None, witness_script=None): '''Returns the integer representation of the hash that needs to get signed for index input_index''' # grab the input being signed by looking up the input_index tx_in = self.tx_ins[input_index] # start with the version in 4 bytes, little endian s = int_to_little_endian(self.version, 4) # add the HashPrevouts and HashSequence s += self.hash_prevouts() + self.hash_sequence() # add the previous transaction hash in little endian s += tx_in.prev_tx[::-1] # add the previous transaction index in 4 bytes, little endian s += int_to_little_endian(tx_in.prev_index, 4) # for p2wpkh, we need to compute the ScriptCode if redeem_script: # Exercise 8: for p2sh-p2wpkh, get the hash160 which is the 2nd command of the RedeemScript h160 = redeem_script.commands[1] # the ScriptCode is the P2PKHScriptPubKey created using the hash160 script_code = P2PKHScriptPubKey(h160) else: # get the script pubkey associated with the previous output (remember testnet) script_pubkey = tx_in.script_pubkey(self.testnet) # next get the hash160 in the script_pubkey. for p2wpkh, it's the second command h160 = script_pubkey.commands[1] # finally the ScriptCode is the P2PKHScriptPubKey created using the hash160 script_code = P2PKHScriptPubKey(h160) # add the serialized ScriptCode s += script_code.serialize() # add the value of the input in 8 bytes, little endian s += int_to_little_endian(tx_in.value(testnet=self.testnet), 8) # add the sequence of the input in 4 bytes, little endian s += int_to_little_endian(tx_in.sequence, 4) # add the HashOutputs s += self.hash_outputs() # add the locktime in 4 bytes, little endian s += int_to_little_endian(self.locktime, 4) # add the sighash (SIGHASH_ALL) in 4 bytes, little endian s += int_to_little_endian(SIGHASH_ALL, 4) # hash256 the whole thing, interpret the as a big endian integer using int_to_big_endian return big_endian_to_int(hash256(s))
def deterministic_k(self, z): k = b'\x00' * 32 v = b'\x01' * 32 if z > N: z -= N z_bytes = int_to_big_endian(z, 32) secret_bytes = int_to_big_endian(self.secret, 32) s256 = hashlib.sha256 k = hmac.new(k, v + b'\x00' + secret_bytes + z_bytes, s256).digest() v = hmac.new(k, v, s256).digest() k = hmac.new(k, v + b'\x01' + secret_bytes + z_bytes, s256).digest() v = hmac.new(k, v, s256).digest() while True: v = hmac.new(k, v, s256).digest() candidate = big_endian_to_int(v) if candidate >= 1 and candidate < N: return candidate k = hmac.new(k, v + b'\x00', s256).digest() v = hmac.new(k, v, s256).digest()
def xprv_child(self, index): if index >= 0x80000000: data = int_to_big_endian(self.private_key.secret, 33) + int_to_big_endian(index, 4) else: data = self.private_key.point.sec() + int_to_big_endian(index, 4) h = hmac_sha512(self.chain_code, data) secret = (big_endian_to_int(h[:32]) + self.private_key.secret) % N private_key = PrivateKey(secret=secret) chain_code = h[32:] depth = self.depth + 1 parent_fingerprint = self.pub.hash160()[:4] child_number = index return HDPrivateKey( private_key=private_key, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=self.testnet, )
def parse(cls, s, compressed=True, testnet=False): b = decode_base58_checksum(s) # prepend b'\xef' on testnet, b'\x80' on mainnet if testnet: prefix = b'\xef' else: prefix = b'\x80' if prefix != b[:1]: raise RuntimeError("bad suffix or prefix") # append b'\x01' if compressed if compressed: suffix = b'\x01' if suffix != b[-1:]: raise RuntimeError("bad suffix or prefix") secret_bytes = b[1:-1] else: secret_bytes = b[1:] return cls(big_endian_to_int(secret_bytes))
def xpub_raw_parse(cls, s): version = s.read(4) if version in (TESTNET_XPUB, TESTNET_YPUB, TESTNET_ZPUB): testnet = True elif version in (MAINNET_XPUB, MAINNET_YPUB, MAINNET_ZPUB): testnet = False else: raise ValueError('not an xpub, ypub or zpub: {} {}'.format(s, version)) depth = byte_to_int(s.read(1)) parent_fingerprint = s.read(4) child_number = big_endian_to_int(s.read(4)) chain_code = s.read(32) point = S256Point.parse(s.read(33)) return cls( point=point, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=testnet, )
def child(self, index): '''Returns the child HDPrivateKey at a particular index. Hardened children return for indices >= 0x8000000. ''' # if index >= 0x80000000 if index >= 0x80000000: # the message data is the private key secret in 33 bytes in # big-endian and the index in 4 bytes big-endian. data = int_to_big_endian(self.private_key.secret, 33) + int_to_big_endian(index, 4) else: # the message data is the public key compressed SEC # and the index in 4 bytes big-endian. data = self.private_key.point.sec() + int_to_big_endian(index, 4) # get the hmac_sha512 with chain code and data h = hmac_sha512(self.chain_code, data) # the new secret is the first 32 bytes as a big-endian integer # plus the secret mod N secret = (big_endian_to_int(h[:32]) + self.private_key.secret) % N # create the PrivateKey object private_key = PrivateKey(secret=secret) # the chain code is the last 32 bytes chain_code = h[32:] # depth is whatever the current depth + 1 depth = self.depth + 1 # parent_fingerprint is the fingerprint of this node parent_fingerprint = self.fingerprint() # child number is the index child_number = index # return a new HDPrivateKey instance return HDPrivateKey( private_key=private_key, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=self.testnet, )
def encrypt_message(self, message: bytes) -> bytes: k = big_endian_to_int(token_bytes(32)) private_key = PrivateKey(k) cipher = private_key.cipher(self) encrypted = cipher.encrypt(message) return private_key.point.sec() + encode_varstr(encrypted)