def test_rfc6979_tv(): fname = "rfc6979.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_dict = json.load(f) for ec_name in test_dict: ec = CURVES[ec_name] test_vectors = test_dict[ec_name] for x, x_U, y_U, hf, msg, k, r, s in test_vectors: x = int(x, 16) # test RFC6979 implementation k2 = rfc6979(msg, x, ec, eval("hashlib." + hf)) assert k == hex(k2) # test RFC6979 usage in DSA sig = dsa.sign(msg, x, k2, ec, eval("hashlib." + hf)) assert r == hex(sig[0]) assert s in (hex(sig[1]), hex(ec.n - sig[1])) # test that RFC6979 is the default nonce for DSA sig = dsa.sign(msg, x, k=None, ec=ec, hf=eval("hashlib." + hf)) assert r == hex(sig[0]) assert s in (hex(sig[1]), hex(ec.n - sig[1])) # test key-pair coherence U = mult(x, ec.G, ec) assert (int(x_U, 16), int(y_U, 16)) == U # test signature validity dsa.assert_as_valid(msg, U, sig, ec, hf=eval("hashlib." + hf))
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_rfc6979_tv() -> None: fname = "rfc6979.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as file_: test_dict = json.load(file_) for ec_name in test_dict: ec = CURVES[ec_name] test_vectors = test_dict[ec_name] for x, x_U, y_U, hf, msg, k, r, s in test_vectors: x = int(x, 16) m = reduce_to_hlen(msg, hf=getattr(hashlib, hf)) # test RFC6979 implementation k2 = _rfc6979(m, x, ec, getattr(hashlib, hf)) assert k == hex(k2) # test RFC6979 usage in DSA sig = dsa._sign(m, x, k2, low_s=False, ec=ec, hf=getattr(hashlib, hf)) assert r == hex(sig[0]) assert s == hex(sig[1]) # test that RFC6979 is the default nonce for DSA sig = dsa._sign(m, x, None, low_s=False, ec=ec, hf=getattr(hashlib, hf)) assert r == hex(sig[0]) assert s == hex(sig[1]) # test key-pair coherence U = mult(x, ec.G, ec) assert int(x_U, 16), int(y_U, 16) == U # test signature validity dsa.assert_as_valid(msg, U, sig, ec, getattr(hashlib, hf))
def test_ledger() -> None: """Hybrid ECDSA Bitcoin message signature generated by Ledger""" mnemonic = ( "barely sun snack this snack relief pipe attack disease boss enlist lawsuit" ) # non-standard leading 31 in DER serialization derivation_path = "m/1" msg = b"\xfb\xa3\x1f\x8cd\x85\xe29#K\xb3{\xfd\xa7<?\x95oL\xee\x19\xb2'oh\xa7]\xd9A\xfeU\xd8" dersig_hex_str = "3144022012ec0c174936c2a46dc657252340b2e6e6dd8c31dd059b6f9f33a90c21af2fba022030e6305b3ccf88009d419bf7651afcfcc0a30898b93ae9de9aa6ac03cf8ec56b" # pubkey derivation rprv = bip32.mxprv_from_bip39_mnemonic(mnemonic) xprv = bip32.derive(rprv, derivation_path) # the actual message being signed magic_msg = bms._magic_message(msg) # save key_id and patch dersig dersig = bytes.fromhex(dersig_hex_str) key_id = dersig[0] dersig = b"\x30" + dersig[1:] r, s = dsa.deserialize(dersig) # ECDSA signature verification of the patched dersig dsa.assert_as_valid(magic_msg, xprv, dersig, ec, hf) assert dsa.verify(magic_msg, xprv, dersig) # compressed address addr = base58address.p2pkh(xprv) # equivalent Bitcoin Message Signature (non-serialized) rec_flag = 27 + 4 + (key_id & 0x01) btcmsgsig = (rec_flag, r, s) # Bitcoin Message Signature verification bms.assert_as_valid(msg, addr, btcmsgsig) assert bms.verify(msg, addr, btcmsgsig) assert not bms.verify(magic_msg, addr, btcmsgsig) bms.sign(msg, xprv) # standard leading 30 in DER serialization derivation_path = "m/0/0" msg_str = "hello world" dersig_hex_str = "3045022100967dac3262b4686e89638c8219c5761017f05cd87a855edf034f4a3ec6b59d3d0220108a4ef9682b71a45979d8c75c393382d9ccb8eb561d73b8c5fc0b87a47e7d27" # pubkey derivation rprv = bip32.mxprv_from_bip39_mnemonic(mnemonic) xprv = bip32.derive(rprv, derivation_path) # the actual message being signed magic_msg = bms._magic_message(msg_str) # save key_id and patch dersig dersig = bytes.fromhex(dersig_hex_str) key_id = dersig[0] dersig = b"\x30" + dersig[1:] r, s = dsa.deserialize(dersig) # ECDSA signature verification of the patched dersig dsa.assert_as_valid(magic_msg, xprv, dersig, ec, hf) assert dsa.verify(magic_msg, xprv, dersig) # compressed address addr = base58address.p2pkh(xprv) # equivalent Bitcoin Message Signature (non-serialized) rec_flag = 27 + 4 + (key_id & 0x01) btcmsgsig = (rec_flag, r, s) # Bitcoin Message Signature verification bms.assert_as_valid(msg_str, addr, btcmsgsig) assert bms.verify(msg_str, addr, btcmsgsig) assert not bms.verify(magic_msg, addr, btcmsgsig)
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)