def _rfc6979(ec: Curve, hf: Callable[[Any], Any], h_int: int, x: int) -> int: # https://tools.ietf.org/html/rfc6979 section 3.2 # h_int = hf(m) # 3.2.a # convert the private key x to a sequence of nsize octets bprv = octets_from_int(x, ec.nsize) # bprv = x.to_bytes(nsize, 'big') # truncate and/or expand h_int: encoding size is driven by nsize bm = octets_from_int(h_int, ec.nsize) # bm = h_int.to_bytes(nsize, 'big') bprvbm = bprv + bm hsize = hf().digest_size V = b'\x01' * hsize # 3.2.b K = b'\x00' * hsize # 3.2.c K = hmac.new(K, V + b'\x00' + bprvbm, hf).digest() # 3.2.d V = hmac.new(K, V, hf).digest() # 3.2.e K = hmac.new(K, V + b'\x01' + bprvbm, hf).digest() # 3.2.f V = hmac.new(K, V, hf).digest() # 3.2.g while True: # 3.2.h T = b'' # 3.2.h.1 while len(T) < ec.nsize: # 3.2.h.2 V = hmac.new(K, V, hf).digest() T += V k = _int_from_bits(ec, T) # candidate # 3.2.h.3 if 0 < k < ec.n: # acceptable values for k return k # successful candidate K = hmac.new(K, V + b'\x00', hf).digest() V = hmac.new(K, V, hf).digest()
def test_wif_exceptions(self): # not a 32-bytes private key badq = 33 * b'\x02' self.assertRaises(ValueError, wif_from_prvkey, badq, True) #wif_from_prvkey(badq, True) # private key not in (0, n) badq = ec.n self.assertRaises(ValueError, wif_from_prvkey, badq, True) #wif_from_prvkey(badq, True) # Not a private key WIF: missing leading 0x80 payload = b'\x81' + octets_from_int(badq, ec.psize) badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a compressed WIF: missing trailing 0x01 payload = b'\x80' + octets_from_int(badq, ec.psize) + b'\x00' badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a WIF: wrong size (35) payload = b'\x80' + octets_from_int(badq, ec.psize) + b'\x01\x00' badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a WIF: private key not in (0, n) payload = b'\x80' + octets_from_int(badq, ec.psize) badwif = base58.encode(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif)
def test_key_deployment(self): """GEC 2: Test Vectors for SEC 1, section 4.1 http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf """ # 4.1.1 # ec = secp160r1 # hf = sha1 # 4.1.2 dU = 971761939728640320549601132085879836204587084162 self.assertEqual(format(dU, str(ec.psize) + 'x'), 'aa374ffc3ce144e6b073307972cb6d57b2a4e982') QU = mult(dU, ec.G, ec) self.assertEqual(QU, (466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220)) self.assertEqual( octets_from_point(QU, True, ec).hex(), '0251b4496fecc406ed0e75a24a3c03206251419dc0') # 4.1.3 dV = 399525573676508631577122671218044116107572676710 self.assertEqual(format(dV, str(ec.psize) + 'x'), '45fb58a92a17ad4b15101c66e74f277e2b460866') QV = mult(dV, ec.G, ec) self.assertEqual(QV, (420773078745784176406965940076771545932416607676, 221937774842090227911893783570676792435918278531)) self.assertEqual( octets_from_point(QV, True, ec).hex(), '0349b41e0e9c0369c2328739d90f63d56707c6e5bc') # expected results z_exp = 1155982782519895915997745984453282631351432623114 zstr = 'ca7c0f8c3ffa87a96e1b74ac8e6af594347bb40a' size = 20 keying_data_exp = '744ab703f5bc082e59185f6d049d2d367db245c2' # 4.1.4 z, _ = mult(dU, QV, ec) # x coordinate only self.assertEqual(z, z_exp) self.assertEqual(format(z, str(ec.psize) + 'x'), zstr) keyingdata = dh.ansi_x963_kdf(octets_from_int(z, ec.psize), size, ec, hf) self.assertEqual(keyingdata.hex(), keying_data_exp) # 4.1.5 z, _ = mult(dV, QU, ec) # x coordinate only self.assertEqual(z, z_exp) self.assertEqual(format(z, str(ec.psize) + 'x'), zstr) keyingdata = dh.ansi_x963_kdf(octets_from_int(z, ec.psize), size, ec, hf) self.assertEqual(keyingdata.hex(), keying_data_exp)
def _e(ec: Curve, hf, r: int, P: Point, mhd: bytes) -> int: # Let e = int(hf(bytes(x(R)) || bytes(dG) || mhd)) mod n. ebytes = octets_from_int(r, ec.psize) # FIXME: hsize, nsize ? ebytes += octets_from_point(ec, P, True) ebytes += mhd ebytes = hf(ebytes).digest() e = int_from_bits(ec, ebytes) return e
def test_wif_from_prvkey(self): q = 0xC28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D # compressed WIF wif = wif_from_prvkey(q, True) self.assertEqual( wif, b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617') q2 = prvkey_from_wif(wif) self.assertEqual(q2[0], q) self.assertEqual(q2[1], True) # uncompressed WIF wif = wif_from_prvkey(q, False) self.assertEqual( wif, b'5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ') q3 = prvkey_from_wif(wif) self.assertEqual(q3[0], q) self.assertEqual(q3[1], False) # private key not in (0, n) badq = ec.n self.assertRaises(ValueError, wif_from_prvkey, badq, True) #wif = wif_from_prvkey(badq, True) # Not a private key WIF: missing leading 0x80 payload = b'\x81' + octets_from_int(badq, ec.psize) badwif = base58.encode_check(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a compressed WIF: missing trailing 0x01 payload = b'\x80' + octets_from_int(badq, ec.psize) + b'\x00' badwif = base58.encode_check(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) # prvkey_from_wif(badwif) # Not a WIF: wrong size (35) payload = b'\x80' + octets_from_int(badq, ec.psize) + b'\x01\x00' badwif = base58.encode_check(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif) #prvkey_from_wif(badwif) # Not a WIF: private key not in (0, n) payload = b'\x80' + octets_from_int(badq, ec.psize) badwif = base58.encode_check(payload) self.assertRaises(ValueError, prvkey_from_wif, badwif)
def _e(ec: Curve, hf: Callable[[Any], Any], r: int, P: Point, mhd: bytes) -> int: # Let e = int(hf(bytes(x(R)) || bytes(dG) || mhd)) mod n. h = hf() h.update(octets_from_int(r, ec.psize)) h.update(octets_from_point(ec, P, True)) h.update(mhd) e = int_from_bits(ec, h.digest()) return e
def key_agreement(dUV: int, QVU: Point, keydatasize: int, ec: Curve, hf) -> bytes: P = mult(ec, dUV, QVU) if P[1] == 0: "invalid (zero) private key" z = P[0] zbytes = octets_from_int(z, ec.psize) k = kdf(zbytes, keydatasize, ec, hf) return k
def wif_from_prvkey(prvkey: int, compressed: bool) -> bytes: """private key to Wallet Import Format""" if not 0 < prvkey < ec.n: raise ValueError(f"private key {hex(prvkey)} not in (0, n)") payload = b'\x80' + octets_from_int(prvkey, ec.nsize) if compressed: payload += b'\x01' return base58.encode_check(payload)
def kdf(zbytes: bytes, keydatasize: int, ec: Curve, hf) -> bytes: """ ANS-X9.63-KDF - SEC 1 specification source: http://www.secg.org/sec1-v2.pdf, section 3.6.1 """ hsize = hf().digest_size assert keydatasize < hsize * (2**32 - 1), "invalid" counter = 1 counter_bytes = counter.to_bytes(4, 'big') K_temp = [] for i in range((keydatasize + 1) // hsize): K_temp.append(hf(zbytes + counter_bytes).digest()) counter += 1 counter_bytes = counter.to_bytes(4, 'big') i += 1 K_bytes = b''.join(K_temp[i] for i in range(keydatasize // hsize)) K = int_from_octets(K_bytes) >> (keydatasize - hsize) return octets_from_int(K, ec.psize)