def _prv(self, version): raw = version raw += int_to_byte(self.depth) raw += self.parent_fingerprint raw += int_to_big_endian(self.child_number, 4) raw += self.chain_code raw += int_to_big_endian(self.private_key.secret, 33) return encode_base58_checksum(raw)
def sec(self, compressed=True): # returns the binary version of the sec format, NOT hex # if compressed, starts with b'\x02' if self.y.num is even, b'\x03' if self.y is odd # then self.x.num # remember, you have to convert self.x.num/self.y.num to binary using int_to_big_endian x = int_to_big_endian(self.x.num, 32) if compressed: if self.y.num % 2 == 0: return b'\x02' + x else: return b'\x03' + x else: # if non-compressed, starts with b'\x04' followod by self.x and then self.y y = int_to_big_endian(self.y.num, 32) return b'\x04' + x + y
def raw_serialize(self, version): # version + depth + parent_fingerprint + child number + chain code + private key # start with version, which should be a constant depending on testnet raw = version # add depth, which is 1 byte using int_to_byte raw += int_to_byte(self.depth) # add the parent_fingerprint raw += self.parent_fingerprint # add the child number 4 bytes using int_to_big_endian raw += int_to_big_endian(self.child_number, 4) # add the chain code raw += self.chain_code # add the 0 byte and the private key's secret in big endian, 33 bytes raw += int_to_big_endian(self.private_key.secret, 33) return raw
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 secure_mnemonic(entropy=0, num_bits=128): '''Generates a mnemonic phrase using the number of bits''' # if we have more than 128 bits, just mask everything but the last 128 bits if len(bin(entropy)) > num_bits + 2: entropy &= (1 << num_bits) - 1 # xor some random bits with the entropy that was passed in preseed = randbits(num_bits) ^ entropy # convert the number to big-endian s = int_to_big_endian(preseed, 16) # 1 extra bit for checksum is needed per 32 bits checksum_bits_needed = num_bits // 32 # the checksum is the sha256's first n bits. At most this is 8 checksum = sha256(s)[0] >> (8 - checksum_bits_needed) # we concatenate the checksum to the preseed total = (preseed << checksum_bits_needed) | checksum # now we get the mnemonic passphrase mnemonic = [] # now group into groups of 11 bits for _ in range((num_bits + checksum_bits_needed) // 11): # grab the last 11 bits current = total & ((1 << 11) - 1) # insert the correct word at the front mnemonic.insert(0, WORD_LIST[current]) # shift by 11 bits so we can move to the next set total >>= 11 # return the mnemonic phrase by putting spaces between return ' '.join(mnemonic)
def _serialize(self, version): raw = version raw += int_to_byte(self.depth) raw += self.parent_fingerprint raw += int_to_big_endian(self.child_number, 4) raw += self.chain_code raw += self.point.sec() return raw
def _prv(self, version): '''Returns the base58-encoded x/y/z prv. Expects a 4-byte version.''' # version + depth + parent_fingerprint + child number + chain code + private key # start with version, which should be a constant depending on testnet raw = version # add depth, which is 1 byte using int_to_byte raw += int_to_byte(self.depth) # add the parent_fingerprint raw += self.parent_fingerprint # add the child number 4 bytes using int_to_big_endian raw += int_to_big_endian(self.child_number, 4) # add the chain code raw += self.chain_code # add the 0 byte and the private key's secret in big endian, 33 bytes raw += int_to_big_endian(self.private_key.secret, 33) # return the whole thing base58-encoded return encode_base58_checksum(raw)
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 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 der(self): # convert the r part to bytes rbin = int_to_big_endian(self.r, 32) # if rbin has a high bit, add a 00 if rbin[0] >= 128: rbin = b'\x00' + rbin while rbin[0] == 0: if rbin[1] >= 128: break else: rbin = rbin[1:] result = bytes([2, len(rbin)]) + rbin sbin = int_to_big_endian(self.s, 32) # if sbin has a high bit, add a 00 if sbin[0] >= 128: sbin = b'\x00' + sbin while sbin[0] == 0: if sbin[1] >= 128: break else: sbin = sbin[1:] result += bytes([2, len(sbin)]) + sbin return bytes([0x30, len(result)]) + result
def _serialize(self, version): # start with the version raw = version # add the depth using int_to_byte raw += int_to_byte(self.depth) # add the parent_fingerprint raw += self.parent_fingerprint # add the child number in 4 bytes using int_to_big_endian raw += int_to_big_endian(self.child_number, 4) # add the chain code raw += self.chain_code # add the SEC pubkey raw += self.point.sec() return raw
def wif(self, compressed=True): # convert the secret from integer to a 32-bytes in big endian using int_to_big_endian(x, 32) secret_bytes = int_to_big_endian(self.secret, 32) # prepend b'\xef' on testnet, b'\x80' on mainnet if self.testnet: prefix = b'\xef' else: prefix = b'\x80' # append b'\x01' if compressed if compressed: suffix = b'\x01' else: suffix = b'' # encode_base58_checksum the whole thing return encode_base58_checksum(prefix + secret_bytes + suffix)
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 from_mnemonic(cls, mnemonic, password=b'', path='m', testnet=False): '''Returns a HDPrivateKey object from the mnemonic.''' # split the mnemonic into words with .split() words = mnemonic.split() # check that there are 12, 15, 18, 21 or 24 words # if not, raise a ValueError if len(words) not in (12, 15, 18, 21, 24): raise ValueError('you need 12, 15, 18, 21, or 24 words') # calculate the number number = 0 # each word is 11 bits for word in words: # get the number that the word represents using WORD_LOOKUP index = WORD_LOOKUP[word] # left-shift the number by 11 bits and bitwise-or the index number = (number << 11) | index # checksum is the last n bits where n = (# of words / 3) checksum_bits_length = len(words) // 3 # grab the checksum bits checksum = number & ((1 << checksum_bits_length) - 1) # get the actual number by right-shifting by the checksum bits length data_num = number >> checksum_bits_length # convert the number to big-endian data = int_to_big_endian(data_num, checksum_bits_length * 4) # the one byte we get is from sha256 of the data, shifted by # 8 - the number of bits we need for the checksum computed_checksum = sha256(data)[0] >> (8 - checksum_bits_length) # check that the checksum is correct or raise ValueError if checksum != computed_checksum: raise ValueError('words fail checksum: {}'.format(words)) # normalize in case we got a mnemonic that's just the first 4 letters normalized_words = [] for word in words: normalized_words.append(WORD_LIST[WORD_LOOKUP[word]]) normalized_mnemonic = ' '.join(normalized_words) # salt is b'mnemonic' + password salt = b'mnemonic' + password # the seed is the hmac_sha512_kdf with normalized mnemonic and salt seed = hmac_sha512_kdf(normalized_mnemonic, salt) # return the HDPrivateKey at the path specified return cls.from_seed(seed, testnet=testnet).traverse(path)
def from_mnemonic(cls, mnemonic, password=b'', path='m', testnet=False): words = mnemonic.split() if len(words) not in (12, 15, 18, 21, 24): raise ValueError('you need 12, 15, 18, 21, or 24 words') number = 0 for word in words: index = WORD_LOOKUP[word] number = (number << 11) | index checksum_bits_length = len(words) // 3 checksum = number & ((1 << checksum_bits_length) - 1) data_num = number >> checksum_bits_length data = int_to_big_endian(data_num, checksum_bits_length * 4) computed_checksum = sha256(data)[0] >> (8 - checksum_bits_length) if checksum != computed_checksum: raise ValueError('words fail checksum: {}'.format(words)) normalized_words = [] for word in words: normalized_words.append(WORD_LIST[WORD_LOOKUP[word]]) normalized_mnemonic = ' '.join(normalized_words) salt = b'mnemonic' + password seed = hmac_sha512_kdf(normalized_mnemonic, salt) return cls.from_seed(seed, testnet=testnet).traverse(path)