def test_gec() -> None: """GEC 2: Test Vectors for SEC 1, section 2 http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf """ # 2.1.1 Scheme setup ec = CURVES["secp160r1"] hf = sha1 # 2.1.2 Key Deployment for U dU = 971761939728640320549601132085879836204587084162 dU, QU = dsa.gen_keys(dU, ec) assert (format(dU, str(ec.n_size) + "x") == "aa374ffc3ce144e6b073307972cb6d57b2a4e982") assert QU == ( 466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220, ) assert (bytes_from_point( QU, ec).hex() == "0251b4496fecc406ed0e75a24a3c03206251419dc0") # 2.1.3 Signing Operation for U msg = b"abc" k = 702232148019446860144825009548118511996283736794 lower_s = False sig = dsa.sign_(reduce_to_hlen(msg, hf), dU, k, lower_s, ec, hf) assert sig.r == 0xCE2873E5BE449563391FEB47DDCBA2DC16379191 assert sig.s == 0x3480EC1371A091A464B31CE47DF0CB8AA2D98B54 assert sig.ec == ec # 2.1.4 Verifying Operation for V dsa.assert_as_valid(msg, QU, sig, lower_s, hf) assert dsa.verify(msg, QU, sig, lower_s, hf)
def test_pub_key_recovery() -> None: ec = CURVES["secp112r2"] q = 0x10 Q = mult(q, ec.G, ec) msg = "Satoshi Nakamoto".encode() sig = dsa.sign(msg, q, ec=ec) dsa.assert_as_valid(msg, Q, sig) assert dsa.verify(msg, Q, sig) keys = dsa.recover_pub_keys(msg, sig) assert len(keys) == 4 assert Q in keys for Q in keys: assert dsa.verify(msg, Q, sig)
def test_libsecp256k1() -> None: try: import btclib_libsecp256k1.dsa # pylint: disable=import-outside-toplevel except ImportError: # pragma: no cover pytest.skip() prvkey, Q = dsa.gen_keys(0x1) pubkey_bytes = bytes_from_point(Q) msg = "Satoshi Nakamoto".encode() msg_hash = reduce_to_hlen(msg) libsecp256k1_sig = btclib_libsecp256k1.dsa.sign(msg_hash, prvkey) btclib_sig = dsa.sign_(msg_hash, prvkey) assert btclib_libsecp256k1.dsa.verify(msg_hash, pubkey_bytes, btclib_sig.serialize()) assert dsa.verify(msg, prvkey, libsecp256k1_sig)
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" # pub_key derivation rprv = bip39.mxprv_from_mnemonic(mnemonic) xprv = bip32.derive(rprv, derivation_path) # the actual message being signed magic_msg = magic_message(msg) # save key_id and patch dersig dersig = bytes.fromhex(dersig_hex_str) key_id = dersig[0] dsa_sig = dsa.Sig.parse(b"\x30" + dersig[1:]) # ECDSA signature verification of the patched dersig dsa.assert_as_valid(magic_msg, xprv, dsa_sig) assert dsa.verify(magic_msg, xprv, dsa_sig) # compressed address addr = b58.p2pkh(xprv) # equivalent Bitcoin Message Signature rec_flag = 27 + 4 + (key_id & 0x01) bms_sig = bms.Sig(rec_flag, dsa_sig) # Bitcoin Message Signature verification bms.assert_as_valid(msg, addr, bms_sig) assert bms.verify(msg, addr, bms_sig) assert not bms.verify(magic_msg, addr, bms_sig) bms.sign(msg, xprv) # standard leading 30 in DER serialization derivation_path = "m/0/0" msg_str = "hello world".encode() dersig_hex_str = "3045022100967dac3262b4686e89638c8219c5761017f05cd87a855edf034f4a3ec6b59d3d0220108a4ef9682b71a45979d8c75c393382d9ccb8eb561d73b8c5fc0b87a47e7d27" # pub_key derivation rprv = bip39.mxprv_from_mnemonic(mnemonic) xprv = bip32.derive(rprv, derivation_path) # the actual message being signed magic_msg = magic_message(msg_str) # save key_id and patch dersig dersig = bytes.fromhex(dersig_hex_str) key_id = dersig[0] dsa_sig = dsa.Sig.parse(b"\x30" + dersig[1:]) # ECDSA signature verification of the patched dersig dsa.assert_as_valid(magic_msg, xprv, dsa_sig, lower_s=True) assert dsa.verify(magic_msg, xprv, dsa_sig) # compressed address addr = b58.p2pkh(xprv) # equivalent Bitcoin Message Signature rec_flag = 27 + 4 + (key_id & 0x01) bms_sig = bms.Sig(rec_flag, dsa_sig) # Bitcoin Message Signature verification bms.assert_as_valid(msg_str, addr, bms_sig) assert bms.verify(msg_str, addr, bms_sig) assert not bms.verify(magic_msg, addr, bms_sig)
q %= ec.n Q = mult(q, ec.G) print(f"prvkey: {hex(q).upper()}") print(f"PubKey: {'02' if Q[1] % 2 == 0 else '03'} {hex(Q[0]).upper()}") print("\n1. Message to be signed") msg1 = "Paolo is afraid of ephemeral random numbers".encode() print(msg1.decode()) print("2. Sign message") sig1 = sign(msg1, q) print(f" r1: {hex(sig1.r).upper()}") print(f" s1: {hex(sig1.s).upper()}") print("3. Verify signature") print(verify(msg1, Q, sig1)) print("4. Recover keys") keys = recover_pub_keys(msg1, sig1) for i, key in enumerate(keys): print( f" key#{i}: {'02' if key[1] % 2 == 0 else '03'} {hex(key[0]).upper()}") print("\n** Malleated signature") sm = ec.n - sig1.s print(f" r1: {hex(sig1.r).upper()}") print(f" sm: {hex(sm).upper()}") print("** Verify malleated signature") print(verify(msg1, Q, Sig(sig1.r, sm))) print(verify(msg1, Q, Sig(sig1.r, sm), False))
def test_signature() -> None: msg = "Satoshi Nakamoto".encode() q, Q = dsa.gen_keys(0x1) sig = dsa.sign(msg, q) dsa.assert_as_valid(msg, Q, sig) assert dsa.verify(msg, Q, sig) assert sig == dsa.Sig.parse(sig.serialize()) assert sig == dsa.Sig.parse(sig.serialize().hex()) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) r = 0x934B1EA10A4B3C1757E2B0C017D0B6143CE3C9A7E6A4A49860D7A6AB210EE3D8 s = 0x2442CE9D2B916064108014783E923EC36B49743E2FFA1C4496F01A512AAFD9E5 assert sig.r == r assert sig.s in (s, sig.ec.n - s) # malleability malleated_sig = dsa.Sig(sig.r, sig.ec.n - sig.s) assert dsa.verify(msg, Q, malleated_sig, lower_s=False) keys = dsa.recover_pub_keys(msg, sig) assert len(keys) == 2 assert Q in keys keys = dsa.recover_pub_keys(msg, sig.serialize()) assert len(keys) == 2 assert Q in keys msg_fake = "Craig Wright".encode() 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_invalid = dsa.Sig(sig.ec.p, sig.s, check_validity=False) 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 = dsa.Sig(sig.r, sig.ec.p, check_validity=False) 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, sig.ec.n)
msg = "Hello, I'm Alice!".encode() print("\n", msg.decode()) # 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.r)) print("s:", hex(dsa_sig.s)) 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.r)) print("s:", hex(ssa_sig.s)) ssa_valid = ssa.verify(msg, ssa_pub, ssa_sig) print("valid ECSSA sig:", ssa_valid)