def test_octets2point(self): for ec in all_curves.values(): Q = mult(ec.p, ec.G, ec) # just a random point, not INF Q_bytes = b'\x03' if Q[1] & 1 else b'\x02' Q_bytes += Q[0].to_bytes(ec.psize, byteorder='big') R = point_from_octets(Q_bytes, ec) self.assertEqual(R, Q) self.assertEqual(bytes_from_point(R, ec), Q_bytes) Q_hex_str = Q_bytes.hex() R = point_from_octets(Q_hex_str, ec) self.assertEqual(R, Q) Q_bytes = b'\x04' + Q[0].to_bytes(ec.psize, byteorder='big') Q_bytes += Q[1].to_bytes(ec.psize, byteorder='big') R = point_from_octets(Q_bytes, ec) self.assertEqual(R, Q) self.assertEqual(bytes_from_point(R, ec, False), Q_bytes) Q_hex_str = Q_bytes.hex() R = point_from_octets(Q_hex_str, ec) self.assertEqual(R, Q) # scalar in point multiplication can be int, str, or bytes t = tuple() self.assertRaises(TypeError, mult, t, ec.G, ec) # not a compressed point Q_bytes = b'\x01' * (ec.psize + 1) self.assertRaises(ValueError, point_from_octets, Q_bytes, ec) # not a point Q_bytes += b'\x01' self.assertRaises(ValueError, point_from_octets, Q_bytes, ec) # not an uncompressed point Q_bytes = b'\x01' * 2 * (ec.psize + 1) self.assertRaises(ValueError, point_from_octets, Q_bytes, ec) # invalid x coordinate ec = CURVES['secp256k1'] x = 0xEEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34 xstr = format(x, '32X') self.assertRaises(ValueError, point_from_octets, "03" + xstr, ec) self.assertRaises(ValueError, point_from_octets, "04" + 2 * xstr, ec) self.assertRaises(ValueError, bytes_from_point, (x, x), ec) self.assertRaises(ValueError, bytes_from_point, (x, x), ec, False) # Point must be a tuple[int, int] P = x, x, x self.assertRaises(ValueError, ec.is_on_curve, P) # y-coordinate not in (0, p) P = x, ec.p + 1 self.assertRaises(ValueError, ec.is_on_curve, P)
def test_p2wpkh(self): # https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki # leading/trailing spaces should be tolerated pub = " 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2" "815B16F81798" addr = b"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" self.assertEqual(addr, p2wpkh(pub)) addr = b"tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" self.assertEqual(addr, p2wpkh(pub, "testnet")) # http://bitcoinscri.pt/pages/segwit_native_p2wpkh pub = "02 530c548d402670b13ad8887ff99c294e67fc18097d236d57880c69" "261b42def7" addr = b"bc1qg9stkxrszkdqsuj92lm4c7akvk36zvhqw7p6ck" self.assertEqual(addr, p2wpkh(pub)) _, wp, _, _ = witness_from_b32address(addr) self.assertEqual(bytes(wp), hash160(pub)) # Wrong size (65-bytes) for compressed SEC key uncompr_pub = bytes_from_point(point_from_octets(pub), compressed=False) self.assertRaises(ValueError, p2wpkh, uncompr_pub) # p2wpkh(uncompr_pub) # Wrong pubkey size: 34 instead of 33 self.assertRaises(ValueError, p2wpkh, pub + "00") # p2wpkh(pub + '00') # Witness program length (21) is not 20 self.assertRaises(ValueError, b32address_from_witness, 0, hash160(pub) + b"\x00")
def test_p2wpkh() -> None: # https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki # leading/trailing spaces should be tolerated pub = " 02 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" addr = b"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" assert addr == p2wpkh(pub) addr = b"tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" assert addr == p2wpkh(pub, "testnet") # http://bitcoinscri.pt/pages/segwit_native_p2wpkh pub = "02 530c548d402670b13ad8887ff99c294e67fc18097d236d57880c69261b42def7" addr = b"bc1qg9stkxrszkdqsuj92lm4c7akvk36zvhqw7p6ck" assert addr == p2wpkh(pub) _, wp, _, _ = witness_from_b32address(addr) assert bytes(wp) == hash160(pub) uncompr_pub = bytes_from_point(point_from_octets(pub), compressed=False) err_msg = "not a private or compressed public key: " with pytest.raises(BTClibValueError, match=err_msg): p2wpkh(uncompr_pub) with pytest.raises(BTClibValueError, match=err_msg): p2wpkh(pub + "00") err_msg = "invalid witness program length for witness v0: " with pytest.raises(BTClibValueError, match=err_msg): b32address_from_witness(0, hash160(pub) + b"\x00")
def test_forge_hash_sig() -> None: """forging valid hash signatures""" ec = CURVES["secp256k1"] # see https://twitter.com/pwuille/status/1063582706288586752 # Satoshi's key key = "03 11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" Q = point_from_octets(key, ec) # pick u1 and u2 at will u1 = 1 u2 = 2 R = double_mult(u2, Q, u1, ec.G, ec) r = R[0] % ec.n u2inv = mod_inv(u2, ec.n) s = r * u2inv % ec.n e = s * u1 % ec.n dsa.__assert_as_valid(e, (Q[0], Q[1], 1), r, s, ec) # pick u1 and u2 at will u1 = 1234567890 u2 = 987654321 R = double_mult(u2, Q, u1, ec.G, ec) r = R[0] % ec.n u2inv = mod_inv(u2, ec.n) s = r * u2inv % ec.n e = s * u1 % ec.n dsa.__assert_as_valid(e, (Q[0], Q[1], 1), r, s, ec)
def test_forge_hash_sig(self): """forging valid hash signatures""" # see https://twitter.com/pwuille/status/1063582706288586752 # Satoshi's key P = point_from_octets( "03 11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", ec) u1 = 1 u2 = 2 # pick them at will R = double_mult(u2, P, u1, ec.G, ec) r = R[0] % ec.n u2inv = mod_inv(u2, ec.n) s = r * u2inv % ec.n sig = r, s e = s * u1 % ec.n dsa._verhlp(e, (P[0], P[1], 1), r, s, ec) u1 = 1234567890 u2 = 987654321 # pick them at will R = double_mult(u2, P, u1, ec.G, ec) r = R[0] % ec.n u2inv = mod_inv(u2, ec.n) s = r * u2inv % ec.n sig = r, s e = s * u1 % ec.n dsa._verhlp(e, (P[0], P[1], 1), r, s, ec)
def test_p2pkh_from_pubkey(self): # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses pub = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" addr = p2pkh(pub) self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs') _, h160, _, _ = h160_from_b58address(addr) self.assertEqual(h160, hash160(pub)) uncompr_pub = bytes_from_point( point_from_octets(pub), compressed=False) addr = p2pkh(uncompr_pub, compressed=False) self.assertEqual(addr, b'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM') _, h160, _, _ = h160_from_b58address(addr) self.assertEqual(h160, hash160(uncompr_pub)) # trailing/leading spaces in string pub = ' 02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352' addr = p2pkh(pub) self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs') _, h160, _, _ = h160_from_b58address(addr) self.assertEqual(h160, hash160(pub)) pub = '02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352 ' addr = p2pkh(pub) self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')
def test_all_curves(self): for ec in all_curves.values(): self.assertEqual(mult(0, ec.G, ec), INF) self.assertEqual(mult(0, ec.G, ec), INF) self.assertEqual(mult(1, ec.G, ec), ec.G) self.assertEqual(mult(1, ec.G, ec), ec.G) Gy_odd = ec.y_odd(ec.G[0], True) self.assertEqual(Gy_odd % 2, 1) Gy_even = ec.y_odd(ec.G[0], False) self.assertEqual(Gy_even % 2, 0) self.assertTrue(ec.G[1] in (Gy_odd, Gy_even)) Gbytes = bytes_from_point(ec.G, ec) G2 = point_from_octets(Gbytes, ec) self.assertEqual(ec.G, G2) Gbytes = bytes_from_point(ec.G, ec, False) G2 = point_from_octets(Gbytes, ec) self.assertEqual(ec.G, G2) P = ec.add(INF, ec.G) self.assertEqual(P, ec.G) P = ec.add(ec.G, INF) self.assertEqual(P, ec.G) P = ec.add(INF, INF) self.assertEqual(P, INF) P = ec.add(ec.G, ec.G) self.assertEqual(P, mult(2, ec.G, ec)) P = mult(ec.n - 1, ec.G, ec) self.assertEqual(ec.add(P, ec.G), INF) self.assertEqual(mult(ec.n, ec.G, ec), INF) self.assertEqual(mult(0, INF, ec), INF) self.assertEqual(mult(1, INF, ec), INF) self.assertEqual(mult(25, INF, ec), INF) ec_repr = repr(ec) if ec in low_card_curves.values() or ec.psize < 24: ec_repr = ec_repr[:-1] + ", False)" ec2 = eval(ec_repr) self.assertEqual(str(ec), str(ec2))
def test_octets2point(): for ec in all_curves.values(): Gbytes = bytes_from_point(ec.G, ec) G2 = point_from_octets(Gbytes, ec) assert ec.G == G2 Gbytes = bytes_from_point(ec.G, ec, False) G2 = point_from_octets(Gbytes, ec) assert ec.G == G2 # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = _mult_aff(q, ec.G, ec) Q_bytes = b"\x03" if Q[1] & 1 else b"\x02" Q_bytes += Q[0].to_bytes(ec.psize, byteorder="big") R = point_from_octets(Q_bytes, ec) assert R == Q assert bytes_from_point(R, ec) == Q_bytes Q_hex_str = Q_bytes.hex() R = point_from_octets(Q_hex_str, ec) assert R == Q Q_bytes = b"\x04" + Q[0].to_bytes(ec.psize, byteorder="big") Q_bytes += Q[1].to_bytes(ec.psize, byteorder="big") R = point_from_octets(Q_bytes, ec) assert R == Q assert bytes_from_point(R, ec, False) == Q_bytes Q_hex_str = Q_bytes.hex() R = point_from_octets(Q_hex_str, ec) assert R == Q t = tuple() err_msg = "'<' not supported between instances of 'tuple' and 'int'" with pytest.raises(TypeError, match=err_msg): _mult_aff(t, ec.G, ec) Q_bytes = b"\x01" + b"\x01" * ec.psize with pytest.raises(ValueError, match="not a point: "): point_from_octets(Q_bytes, ec) Q_bytes = b"\x01" + b"\x01" * 2 * ec.psize with pytest.raises(ValueError, match="not a point: "): point_from_octets(Q_bytes, ec) Q_bytes = b"\x04" + b"\x01" * ec.psize with pytest.raises(ValueError, match="invalid size for uncompressed point: "): point_from_octets(Q_bytes, ec) Q_bytes = b"\x02" + b"\x01" * 2 * ec.psize with pytest.raises(ValueError, match="invalid size for compressed point: "): point_from_octets(Q_bytes, ec) Q_bytes = b"\x03" + b"\x01" * 2 * ec.psize with pytest.raises(ValueError, match="invalid size for compressed point: "): point_from_octets(Q_bytes, ec) # invalid x_Q coordinate ec = CURVES["secp256k1"] x_Q = 0xEEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34 xstr = format(x_Q, "32X") with pytest.raises(ValueError, match="invalid x-coordinate: "): point_from_octets("03" + xstr, ec) with pytest.raises(ValueError, match="point not on curve: "): point_from_octets("04" + 2 * xstr, ec) with pytest.raises(ValueError, match="point not on curve"): bytes_from_point((x_Q, x_Q), ec) with pytest.raises(ValueError, match="point not on curve"): bytes_from_point((x_Q, x_Q), ec, False)
def test_second_generator(self): """ important remark on secp256-zkp prefix for compressed encoding of the second generator: https://github.com/garyyu/rust-secp256k1-zkp/wiki/Pedersen-Commitment """ ec = secp256k1 hf = sha256 H = pedersen.second_generator(ec, hf) self.assertEqual( H, point_from_octets( "02 50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", ec)) # 0*G + 1*H T = double_mult(1, H, 0, ec.G, ec) self.assertEqual( T, point_from_octets( "02 50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", ec)) # 0*G + 2*H T = double_mult(2, H, 0, ec.G, ec) self.assertEqual( T, point_from_octets( "03 fad265e0a0178418d006e247204bcf42edb6b92188074c9134704c8686eed37a", ec)) T = mult(2, H, ec) self.assertEqual( T, point_from_octets( "03 fad265e0a0178418d006e247204bcf42edb6b92188074c9134704c8686eed37a", ec)) # 0*G + 3*H T = double_mult(3, H, 0, ec.G, ec) self.assertEqual( T, point_from_octets( "02 5ef47fcde840a435e831bbb711d466fc1ee160da3e15437c6c469a3a40daacaa", ec)) T = mult(3, H, ec) self.assertEqual( T, point_from_octets( "02 5ef47fcde840a435e831bbb711d466fc1ee160da3e15437c6c469a3a40daacaa", ec)) # 1*G+0*H T = double_mult(0, H, 1, ec.G, ec) self.assertEqual( T, point_from_octets( "02 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", ec)) T = ec.G self.assertEqual( T, point_from_octets( "02 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", ec)) # 2*G+0*H T = double_mult(0, H, 2, ec.G, ec) self.assertEqual( T, point_from_octets( "02 c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", ec)) T = mult(2, ec.G, ec) self.assertEqual( T, point_from_octets( "02 c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", ec)) # 3*G+0*H T = double_mult(0, H, 3, ec.G, ec) self.assertEqual( T, point_from_octets( "02 f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", ec)) T = mult(3, ec.G, ec) self.assertEqual( T, point_from_octets( "02 f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", ec)) # 0*G+5*H T = double_mult(5, H, 0, ec.G, ec) self.assertEqual( T, point_from_octets( "03 9e431be0851721f9ce35cc0f718fce7d6d970e3ddd796643d71294d7a09b554e", ec)) T = mult(5, H, ec) self.assertEqual( T, point_from_octets( "03 9e431be0851721f9ce35cc0f718fce7d6d970e3ddd796643d71294d7a09b554e", ec)) # 0*G-5*H T = double_mult(-5, H, 0, ec.G, ec) self.assertEqual( T, point_from_octets( "02 9e431be0851721f9ce35cc0f718fce7d6d970e3ddd796643d71294d7a09b554e", ec)) T = mult(-5, H, ec) self.assertEqual( T, point_from_octets( "02 9e431be0851721f9ce35cc0f718fce7d6d970e3ddd796643d71294d7a09b554e", ec)) # 1*G-5*H U = double_mult(-5, H, 1, ec.G, ec) self.assertEqual( U, point_from_octets( "02 b218ddacb34d827c71760e601b41d309bc888cf7e3ab7cc09ec082b645f77e5a", ec)) U = ec.add(ec.G, T) # reusing previous T value self.assertEqual( U, point_from_octets( "02 b218ddacb34d827c71760e601b41d309bc888cf7e3ab7cc09ec082b645f77e5a", ec)) H = pedersen.second_generator(secp256r1, hf) H = pedersen.second_generator(secp384r1, sha384)
from btclib import base58wif, bip32, bip39, btcmsg, der, slip32 from btclib.secpoint import bytes_from_point, point_from_octets from btclib.utils import bytes_from_octets mnemonic = "token output grass below such awake census safe orphan device other meat" passphrase = "" seed = bip39.seed_from_mnemonic(mnemonic, passphrase) rxprv = bip32.rootxprv_from_seed(seed) rxpub = bip32.xpub_from_xprv(rxprv) assert rxpub == b"xpub661MyMwAqRbcFzL26X6G7bySxgU1oV6GviUrNnhbeAS3ULQq35KEV6uSf1aJXEHjFYy6LXUPrYnfR9bSKWdFZ5VnYaEb3AbHPmXFVAoKKYT", rxpub firmware_xprv = bip32.derive(rxprv, "m/0") firmware_xpub = bip32.derive(rxpub, "m/0") assert firmware_xpub == bip32.xpub_from_xprv(bip32.derive(rxprv, "m/0")) firmware_pubkey = bip32.deserialize(firmware_xpub)['key'] assert bytes_from_point(point_from_octets(firmware_pubkey), False).hex( ) == "042374b3b6b06b65a3b831f857634ea135bf10b014d5bba0f935cb9eb26a4b6547ed3b37f277427a0ab23bda0ca79c5785dc54d2387fa3f295f4d5674d5b637de2" assert bytes_from_point(point_from_octets(firmware_pubkey), True).hex( ) == "022374b3b6b06b65a3b831f857634ea135bf10b014d5bba0f935cb9eb26a4b6547" firmware_wif = base58wif.wif_from_xprv(firmware_xprv) firmware_address = slip32.address_from_xpub(firmware_xpub) app_xprv = bip32.derive(rxprv, "m/1") app_xpub = bip32.derive(rxpub, "m/1") assert app_xpub == bip32.xpub_from_xprv(bip32.derive(rxprv, "m/1")) app_pubkey = bip32.deserialize(app_xpub)['key'] assert bytes_from_point(point_from_octets(app_pubkey), False).hex( ) == "04ae3d0d5c669ed364636e79e72abc012a33be63e537babddf56bfd393256acf6dba0fac21da6386513674573a2d7baff4375c9b6d2498383853c52f0565f97f1a" app_wif = base58wif.wif_from_xprv(app_xprv) app_address = slip32.address_from_xpub(app_xpub) assert app_address == b'1J6674MtZBpfHytdNofLUX6sLHAUaG33uK'