def child(self, i: int) -> 'Xprv': hardened = i >= 1 << 31 if hardened: I = hmac.new(key=self.code, msg=self.keydata() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() tmp = int_to_bytes(i).rjust(4, b'\x00') else: I = hmac.new(key=self.code, msg=self.key.to_public().encode(compressed=True) + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() tmp = self.key.to_public().encode( compressed=True) + int_to_bytes(i).rjust(4, b'\x00') I_L, I_R = bytes_to_int(I[:32]), I[32:] key = (I_L + self.key.int()) % CURVE.order if I_L >= CURVE.order or key == 0: return self.child(i + 1) ret_code = I_R if hardened: path = self.path + f'/{i - 2 ** 31}h' else: path = self.path + f'/{i}' return Xprv(WitPrivateKey.from_int(key), ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path)
def decode(cls, key: bytes) -> 'WitPublicKey': if key.startswith(b'\x04'): # uncompressed key assert len( key) == 65, 'An uncompressed public key must be 65 bytes long' x, y = bytes_to_int(key[1:33]), bytes_to_int(key[33:]) else: # compressed key assert len( key) == 33, 'A compressed public key must be 33 bytes long' x = bytes_to_int(key[1:]) root = nt.modsqrt(CURVE.f(x), P) if key.startswith(b'\x03'): # odd root y = root if root % 2 == 1 else -root % P elif key.startswith(b'\x02'): # even root y = root if root % 2 == 0 else -root % P else: assert False, 'Wrong key format' return cls(Point(x, y, curve=CURVE))
def encode(bts: bytes) -> str: n = bytes_to_int(bts) leading_zero_bytes = len(bts) - len(bts.lstrip(b'\x00')) int_digits = [] while n: int_digits.append(int(n % BASE)) n //= BASE for _ in range(leading_zero_bytes): int_digits.append(0) return ''.join(ALPHABET[i] for i in reversed(int_digits))
def from_seed(cls, seed: Union[bytes, str], network_key=b'Bitcoin seed') -> 'Xprv': """ :param seed: :param network_key: :return: """ if isinstance(seed, str): seed = hex_to_bytes(seed) assert 16 <= len(seed) <= 64, 'Seed should be between 128 and 512 bits' I = hmac.new(key=network_key, msg=seed, digestmod=hashlib.sha512).digest() I_L, I_R = I[:32], I[32:] if bytes_to_int(I_L) == 0 or bytes_to_int(I_L) > CURVE.order: raise KeyDerivationError key, code = WitPrivateKey(I_L), I_R # print(f'\tMaster Secret Key: {bytes_to_hex(I_L)}') # print(f'\tMaster Chain Code: {bytes_to_hex(I_R)}') return cls(key, code)
def verify_hash(self, _hash, public_key): from witnet.crypto.number_theory import mulinv public_key: WitPublicKey = public_key if not (1 <= self.r < N and 1 <= self.s < N): return False e = bytes_to_int(_hash) w = mulinv(self.s, N) u1 = (e * w) % N u2 = (self.r * w) % N point: Point = CURVE.generator * u1 + public_key.point * u2 return self.r % N == point.x % N
def sign_hash(self, _hash) -> Signature: e = hex_to_int(_hash) if isinstance(_hash, str) else bytes_to_int(_hash) r, s = 0, 0 while r == 0 or s == 0: k = secrets.randbelow(N) point = CURVE.generator * k r = point.x % N inv_k = nt.mulinv(k, N) s = (inv_k * (e + r * self.int())) % N return Signature(r=r, s=s)
def deserialize(cls, bts: bytes) -> 'ExtendedKey': from witnet.crypto.hd_wallet.extended_private_key import Xprv from witnet.crypto.hd_wallet.extended_public_key import Xpub def read(n): nonlocal bts data, bts = bts[:n], bts[n:] return data net = read(4) is_private = net in network('Mainnet').values() is_public = net in network('Mainnet').values() assert is_public ^ is_private, f'Invalid network bytes : {bytes_to_hex(net)}' # address_lookup = {val: key for key, val in (network('Mainnet') if is_private else network('Mainnet')).items()} constructor = Xprv if is_private else Xpub depth = bytes_to_int(read(1)) assert depth in range(256), f'Invalid depth : {depth}' fingerprint = read(4) i = bytes_to_int(read(4)) if depth == 0: i = None path = None else: ih = f'{i}' if i < 2**31 else f"{i - 2 ** 31}h" path = '/'.join([constructor.root_path] + ['x' for _ in range(depth - 1)] + [ih]) code = read(32) key = read(33) key = WitPrivateKey(key) if is_private else WitPublicKey.decode(key) assert not bts, 'Leftover bytes' return constructor(key, code, depth=depth, i=i, parent=fingerprint, path=path)
def decode(cls, bts): from collections import deque data = deque(bts) lead = data.popleft() == 0x30 assert lead, f'Invalid leading byte: 0x{lead:x}' # ASN1 SEQUENCE sequence_length = data.popleft() assert sequence_length <= 70, f'Invalid Sequence length: {sequence_length}' lead = data.popleft() assert lead == 0x02, f'Invalid r leading byte: 0x{lead:x}' # 0x02 byte before r len_r = data.popleft() assert len_r <= 33, f'Invalid r length: {len_r}' bts = bytes(data) r, data = bytes_to_int(bts[:len_r]), deque(bts[len_r:]) lead = data.popleft() assert lead == 0x02, f'Invalid s leading byte: 0x{lead:x}' # 0x02 byte before s len_s = data.popleft() assert len_s <= 33, f'Invalid s length: {len_s}' bts = bytes(data) s, rest = bytes_to_int(bts[:len_s]), bts[len_s:] assert len(rest) == 0, f'{len(rest)} leftover bytes' return cls(r, s)
def bin(self): return format(bytes_to_int(self.msg), 'b')
def int(self): return bytes_to_int(self.msg)
def __init__(self, bts): assert bytes_to_int(bts) < N, 'Key larger than Curve Order' super().__init__(bts)