def test_signature(self): ec = secp256k1 q, Q = dsa.gen_keys(0x1) msg = "Satoshi Nakamoto" sig = dsa.sign(msg, q) self.assertEqual(sig, dsa.deserialize(sig)) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) exp_sig = ( 0x934B1EA10A4B3C1757E2B0C017D0B6143CE3C9A7E6A4A49860D7A6AB210EE3D8, 0x2442CE9D2B916064108014783E923EC36B49743E2FFA1C4496F01A512AAFD9E5, ) r, s = sig self.assertEqual(sig[0], exp_sig[0]) self.assertIn(sig[1], (exp_sig[1], secp256k1.n - exp_sig[1])) self.assertTrue(dsa.verify(msg, Q, sig)) # malleability malleated_sig = (r, secp256k1.n - s) self.assertTrue(dsa.verify(msg, Q, malleated_sig)) keys = dsa.recover_pubkeys(msg, sig) self.assertTrue(len(keys) == 2) self.assertIn(Q, keys) fmsg = "Craig Wright" self.assertFalse(dsa.verify(fmsg, Q, sig)) fdsasig = (sig[0], sig[1], sig[1]) self.assertFalse(dsa.verify(msg, Q, fdsasig)) self.assertRaises(ValueError, dsa._verify, msg, Q, fdsasig, ec, hf) _, fQ = dsa.gen_keys() self.assertFalse(dsa.verify(msg, fQ, sig)) # r not in [1, n-1] invalid_dassig = 0, sig[1] self.assertFalse(dsa.verify(msg, Q, invalid_dassig)) # s not in [1, n-1] invalid_dassig = sig[0], 0 self.assertFalse(dsa.verify(msg, Q, invalid_dassig)) # pubkey = INF self.assertRaises(ValueError, dsa._verify, msg, INF, sig, ec, hf) # dsa._verify(msg, INF, sig, ec, hf) # private key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, msg, 0) # dsa.sign(msg, 0) # ephemeral key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, msg, 1, 0)
def test_signature(self): q, Q = dsa.gen_keys(0x1) msg = 'Satoshi Nakamoto' sig = dsa.sign(msg, q) self.assertEqual(sig, dsa.deserialize(sig)) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) exp_sig = ( 0x934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8, 0x2442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5) r, s = sig self.assertEqual(sig[0], exp_sig[0]) self.assertIn(sig[1], (exp_sig[1], secp256k1.n - exp_sig[1])) self.assertTrue(dsa.verify(msg, Q, sig)) # malleability malleated_sig = (r, secp256k1.n - s) self.assertTrue(dsa.verify(msg, Q, malleated_sig)) keys = dsa.recover_pubkeys(msg, sig) self.assertTrue(len(keys) == 2) self.assertIn(Q, keys) fmsg = 'Craig Wright' self.assertFalse(dsa.verify(fmsg, Q, sig)) fdsasig = (sig[0], sig[1], sig[1]) self.assertFalse(dsa.verify(msg, Q, fdsasig)) self.assertRaises(ValueError, dsa._verify, msg, Q, fdsasig, ec, hf) fq, fQ = dsa.gen_keys() self.assertFalse(dsa.verify(msg, fQ, sig)) # r not in [1, n-1] invalid_dassig = 0, sig[1] self.assertFalse(dsa.verify(msg, Q, invalid_dassig)) # s not in [1, n-1] invalid_dassig = sig[0], 0 self.assertFalse(dsa.verify(msg, Q, invalid_dassig)) # pubkey = INF self.assertRaises(ValueError, dsa._verify, msg, INF, sig, ec, hf) #dsa._verify(msg, INF, sig, ec, hf) # private key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, msg, 0) #dsa.sign(msg, 0) # ephemeral key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, msg, 1, 0)
def test_sign_to_contract_dsa() -> None: m = sha256(b"to be signed").digest() c = sha256(b"to be committed").digest() prvkey, pubkey = dsa.gen_keys() dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prvkey) dsa._assert_as_valid(m, pubkey, dsa_sig, ec, sha256) assert verify_commit(c, dsa_receipt) k = 1 + secrets.randbelow(ec.n - 1) dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prvkey, k) dsa._assert_as_valid(m, pubkey, dsa_sig, ec, sha256) assert verify_commit(c, dsa_receipt)
def test_ecdh() -> None: ec = CURVES["secp256k1"] hf = sha256 a, A = dsa.gen_keys() # Alice b, B = dsa.gen_keys() # Bob # Alice computes the shared secret using Bob's public key shared_secret_a = mult(a, B) # Bob computes the shared secret using Alice's public key shared_secret_b = mult(b, A) assert shared_secret_a == shared_secret_b assert shared_secret_a == mult(a * b, ec.G) # hash the shared secret to remove weak bits shared_secret_field_element = shared_secret_a[0] z = shared_secret_field_element.to_bytes(ec.psize, "big") shared_info = b"deadbeef" hsize = hf().digest_size for size in (hsize - 1, hsize, hsize + 1): shared_key = ansi_x9_63_kdf(z, size, hf, None) assert len(shared_key) == size assert shared_key == diffie_hellman(a, B, size, None, ec, hf) assert shared_key == diffie_hellman(b, A, size, None, ec, hf) shared_key = ansi_x9_63_kdf(z, size, hf, shared_info) assert len(shared_key) == size assert shared_key == diffie_hellman(a, B, size, shared_info, ec, hf) assert shared_key == diffie_hellman(b, A, size, shared_info, ec, hf) max_size = hsize * (2 ** 32 - 1) size = max_size + 1 with pytest.raises(BTClibValueError, match="cannot derive a key larger than "): ansi_x9_63_kdf(z, size, hf, None)
def test_signtocontract(): m = b"to be signed" c = b"to be committed" prv, pub = dsa.gen_keys() dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, None) dsa.assert_as_valid(m, pub, dsa_sig, ec, sha256) assert verify_commit(c, dsa_receipt) # 32 bytes message for ECSSA m = sha256(m).digest() prv, pub = ssa.gen_keys() ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, None) ssa.assert_as_valid(m, pub, ssa_sig, ec, sha256) assert verify_commit(c, ssa_receipt)
def test_signtocontract(self): m = b"to be signed" c = b"to be committed" prv, pub = dsa.gen_keys() dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, None) self.assertIsNone(dsa._verify(m, pub, dsa_sig, ec, sha256)) self.assertTrue(verify_commit(c, dsa_receipt)) # 32 bytes message for ECSSA m = sha256(m).digest() prv, pub = ssa.gen_keys() ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, None) self.assertIsNone(ssa._verify(m, pub, ssa_sig, ec, sha256)) self.assertTrue(verify_commit(c, ssa_receipt))
def test_borromean(self): nring = 4 # FIXME randomize; minimum number of rings? ring_sizes = [1 + secrets.randbelow(7) for _ in range(nring)] sign_key_idx = [secrets.randbelow(size) for size in ring_sizes] pubk_rings: Dict[int, List[Point]] = defaultdict(list) sign_keys: List[int] = [] for i in range(nring): for j in range(ring_sizes[i]): priv_key, pub_key = dsa.gen_keys() pubk_rings[i].append(pub_key) if j == sign_key_idx[i]: sign_keys.append(priv_key) msg = "Borromean ring signature" sig = borromean.sign(msg, list(range(1, 5)), sign_key_idx, sign_keys, pubk_rings) borromean.assert_as_valid(msg.encode(), sig[0], sig[1], pubk_rings) self.assertTrue(borromean.verify(msg, sig[0], sig[1], pubk_rings)) self.assertFalse(borromean.verify(0, sig[0], sig[1], pubk_rings))
def test_signtocontract() -> None: m = sha256(b"to be signed").digest() c = sha256(b"to be committed").digest() prv, Pub = dsa.gen_keys() dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv) dsa._assert_as_valid(m, Pub, dsa_sig, ec, sha256) assert verify_commit(c, dsa_receipt) k = 1 + secrets.randbelow(ec.n - 1) dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, k) dsa._assert_as_valid(m, Pub, dsa_sig, ec, sha256) assert verify_commit(c, dsa_receipt) prv, pub = ssa.gen_keys() ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv) ssa._assert_as_valid(m, pub, ssa_sig, ec, sha256) assert verify_commit(c, ssa_receipt) k = 1 + secrets.randbelow(ec.n - 1) ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, k) ssa._assert_as_valid(m, pub, ssa_sig, ec, sha256) assert verify_commit(c, ssa_receipt)
from btclib import bms, dsa, ssa msg = "Hello, I'm Alice!" print("\n", msg) # ECDSA print("\n ECDSA") dsa_prv, dsa_pub = dsa.gen_keys() print("prv", hex(dsa_prv)) print("pub", hex(dsa_pub[0]), hex(dsa_pub[1])) dsa_sig = dsa.sign(msg, dsa_prv) print("r:", hex(dsa_sig[0])) print("s:", hex(dsa_sig[1])) dsa_valid = dsa.verify(msg, dsa_pub, dsa_sig) print("valid ECDSA sig:", dsa_valid) # ECSSA print("\n ECSSA") ssa_prv, ssa_pub = ssa.gen_keys() print("prv", hex(ssa_prv)) print("pub", hex(ssa_pub)) ssa_sig = ssa.sign(msg, ssa_prv) print("r:", hex(ssa_sig[0])) print("s:", hex(ssa_sig[1])) ssa_valid = ssa.verify(msg, ssa_pub, ssa_sig)
from hashlib import sha256 as hf from btclib import dsa from btclib.ecc.curve import mult from btclib.ecc.curve import secp256k1 as ec from btclib.dh import ansi_x9_63_kdf # Diffie-Hellman print("\n Diffie-Hellman") a, A = dsa.gen_keys() # Alice b, B = dsa.gen_keys() # Bob # Alice calculates the shared secret using Bob's public key shared_secret_a = mult(a, B) # Bob calculates the shared secret using Alice's public key shared_secret_b = mult(b, A) print("same shared secret:", shared_secret_a == shared_secret_b) print("as expected:", shared_secret_a == mult(a * b, ec.G)) # hash the shared secret to remove weak bits shared_secret_field_element = shared_secret_a[0] z = shared_secret_field_element.to_bytes(ec.psize, "big") shared_info = None shared_key = ansi_x9_63_kdf(z, 32, hf, shared_info) print("shared key:", shared_key.hex())
def test_signature() -> None: ec = CURVES["secp256k1"] msg = "Satoshi Nakamoto" q, Q = dsa.gen_keys(0x1) sig = dsa.sign(msg, q) assert dsa.verify(msg, Q, sig) assert sig == dsa.deserialize(sig) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) exp_sig = ( 0x934B1EA10A4B3C1757E2B0C017D0B6143CE3C9A7E6A4A49860D7A6AB210EE3D8, 0x2442CE9D2B916064108014783E923EC36B49743E2FFA1C4496F01A512AAFD9E5, ) r, s = sig assert sig[0] == exp_sig[0] assert sig[1] in (exp_sig[1], ec.n - exp_sig[1]) dsa.assert_as_valid(msg, Q, sig) dsa.assert_as_valid(msg, Q, dsa.serialize(*sig)) dsa.assert_as_valid(msg, Q, dsa.serialize(*sig).hex()) # malleability malleated_sig = (r, ec.n - s) assert dsa.verify(msg, Q, malleated_sig) keys = dsa.recover_pubkeys(msg, sig) assert len(keys) == 2 assert Q in keys msg_fake = "Craig Wright" assert not dsa.verify(msg_fake, Q, sig) err_msg = "signature verification failed" with pytest.raises(BTClibRuntimeError, match=err_msg): dsa.assert_as_valid(msg_fake, Q, sig) _, Q_fake = dsa.gen_keys() assert not dsa.verify(msg, Q_fake, sig) err_msg = "signature verification failed" with pytest.raises(BTClibRuntimeError, match=err_msg): dsa.assert_as_valid(msg, Q_fake, sig) err_msg = "not a valid public key: " with pytest.raises(BTClibValueError, match=err_msg): dsa.assert_as_valid(msg, INF, sig) sig_fake = (sig[0], sig[1], sig[1]) assert not dsa.verify(msg, Q, sig_fake) # type: ignore err_msg = "too many values to unpack " with pytest.raises(ValueError, match=err_msg): dsa.assert_as_valid(msg, Q, sig_fake) # type: ignore sig_invalid = ec.p, sig[1] assert not dsa.verify(msg, Q, sig_invalid) err_msg = "scalar r not in 1..n-1: " with pytest.raises(BTClibValueError, match=err_msg): dsa.assert_as_valid(msg, Q, sig_invalid) sig_invalid = sig[0], ec.p assert not dsa.verify(msg, Q, sig_invalid) err_msg = "scalar s not in 1..n-1: " with pytest.raises(BTClibValueError, match=err_msg): dsa.assert_as_valid(msg, Q, sig_invalid) err_msg = "private key not in 1..n-1: " with pytest.raises(BTClibValueError, match=err_msg): dsa.sign(msg, 0) # ephemeral key not in 1..n-1 err_msg = "private key not in 1..n-1: " with pytest.raises(BTClibValueError, match=err_msg): dsa._sign(reduce_to_hlen(msg), q, 0) with pytest.raises(BTClibValueError, match=err_msg): dsa._sign(reduce_to_hlen(msg), q, ec.n)
def test_signature(): ec = secp256k1 q, Q = dsa.gen_keys(0x1) msg = "Satoshi Nakamoto" sig = dsa.sign(msg, q) dsa.assert_as_valid(msg, Q, sig, ec, hf) assert dsa.verify(msg, Q, sig) assert sig == dsa.deserialize(sig) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) exp_sig = ( 0x934B1EA10A4B3C1757E2B0C017D0B6143CE3C9A7E6A4A49860D7A6AB210EE3D8, 0x2442CE9D2B916064108014783E923EC36B49743E2FFA1C4496F01A512AAFD9E5, ) r, s = sig assert sig[0] == exp_sig[0] assert sig[1] in (exp_sig[1], secp256k1.n - exp_sig[1]) # malleability malleated_sig = (r, secp256k1.n - s) assert dsa.verify(msg, Q, malleated_sig) keys = dsa.recover_pubkeys(msg, sig) assert len(keys) == 2 assert Q in keys fmsg = "Craig Wright" assert not dsa.verify(fmsg, Q, sig) err_msg = "signature verification failed" with pytest.raises(AssertionError, match=err_msg): dsa.assert_as_valid(fmsg, Q, sig, ec, hf) _, fQ = dsa.gen_keys() assert not dsa.verify(msg, fQ, sig) err_msg = "signature verification failed" with pytest.raises(AssertionError, match=err_msg): dsa.assert_as_valid(msg, fQ, sig, ec, hf) err_msg = "not a valid public key: " with pytest.raises(ValueError, match=err_msg): dsa.assert_as_valid(msg, INF, sig, ec, hf) fdsasig = (sig[0], sig[1], sig[1]) assert not dsa.verify(msg, Q, fdsasig) err_msg = "too many values to unpack " with pytest.raises(ValueError, match=err_msg): dsa.assert_as_valid(msg, Q, fdsasig, ec, hf) invalid_sig = ec.p, sig[1] assert not dsa.verify(msg, Q, invalid_sig) err_msg = "scalar r not in 1..n-1: " with pytest.raises(ValueError, match=err_msg): dsa.assert_as_valid(msg, Q, invalid_sig, ec, hf) invalid_sig = sig[0], ec.p assert not dsa.verify(msg, Q, invalid_sig) err_msg = "scalar s not in 1..n-1: " with pytest.raises(ValueError, match=err_msg): dsa.assert_as_valid(msg, Q, invalid_sig, ec, hf) err_msg = "private key not in 1..n-1: " with pytest.raises(ValueError, match=err_msg): dsa.sign(msg, 0) # ephemeral key not in 1..n-1 err_msg = "private key not in 1..n-1: " with pytest.raises(ValueError, match=err_msg): dsa.sign(msg, 1, 0)
def test_exceptions() -> None: # from creator example psbt_encoded = "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" psbt = Psbt.decode(psbt_encoded) psbt.outputs[0].redeem_script = "bad script" # type: ignore with pytest.raises(BTClibValueError, match="invalid redeem script"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.inputs[0].witness_script = "bad script" # type: ignore with pytest.raises(BTClibValueError, match="invalid witness script"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.outputs[0].unknown = {"bad key": b""} # type: ignore with pytest.raises(BTClibValueError, match="invalid key in unknown"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.outputs[0].unknown = {b"deadbeef": "bad value"} # type: ignore with pytest.raises(BTClibValueError, match="invalid value in unknown"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.inputs[0].sighash = 101 with pytest.raises(BTClibValueError, match="invalid sighash: "): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.inputs[0].final_script_sig = "bad script" # type: ignore with pytest.raises(BTClibValueError, match="invalid final script_sig"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.inputs[0].final_script_witness = "bad script" # type: ignore with pytest.raises(BTClibValueError, match="invalid final script witness"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.inputs[0].final_script_witness = [b"", ""] # type: ignore with pytest.raises(BTClibValueError, match="invalid final script witness"): psbt.serialize() psbt = Psbt.decode(psbt_encoded) _, Q = dsa.gen_keys() pubkey = secpoint.bytes_from_point(Q) r = s = int.from_bytes(bytes.fromhex("FF" * 32), "big") sig_bytes = der._serialize_scalar(r) sig_bytes += der._serialize_scalar(s) sig_bytes = b"\x30" + len(sig_bytes).to_bytes(1, byteorder="big") + sig_bytes psbt.inputs[0].partial_signatures = {pubkey: sig_bytes} with pytest.raises(BTClibValueError, match="invalid partial signature: "): psbt.serialize() pubkey = bytes.fromhex("02" + 31 * "00" + "07") psbt.inputs[0].partial_signatures = {pubkey: sig_bytes} with pytest.raises(BTClibValueError, match="invalid partial signature pubkey: "): psbt.serialize() psbt = Psbt.decode(psbt_encoded) err_msg = "invalid version: " psbt.version = -1 with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt.version = 0xFFFFFFFF + 1 with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt.version = 1 # TODO: add to test vectors with pytest.raises(BTClibValueError, match="invalid non-zero version: "): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt_bin = psbt.serialize() psbt_bin = psbt_bin.replace(PSBT_SEPARATOR, PSBT_DELIMITER) # TODO: add to test vectors with pytest.raises(BTClibValueError, match="malformed psbt: missing separator"): Psbt.deserialize(psbt_bin) psbt = Psbt.decode(psbt_encoded) psbt.inputs.pop() err_msg = "mismatched number of tx.vin and psbt_in" # TODO: add to test vectors with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.tx.vin[0].txinwitness = [b""] err_msg = "non empty txinwitness" # TODO: add to test vectors with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt = Psbt.decode(psbt_encoded) psbt.outputs.pop() err_msg = "mismatched number of tx.vout and psbt_out" # TODO: add to test vectors with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize()