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_pubkey_recovery(self): ec = secp112r2 q = 0x10 Q = mult(q, ec.G, ec) msg = "Satoshi Nakamoto" k = None sig = dsa.sign(msg, q, k, ec) self.assertTrue(dsa.verify(msg, Q, sig, ec)) dersig = dsa.serialize(*sig, ec) self.assertTrue(dsa.verify(msg, Q, dersig, ec)) r, s = dsa.deserialize(dersig) self.assertEqual((r, s), sig) keys = dsa.recover_pubkeys(msg, sig, ec) self.assertEqual(len(keys), 4) self.assertIn(Q, keys) for Q in keys: self.assertTrue(dsa.verify(msg, Q, sig, ec))
def test_pubkey_recovery() -> None: ec = CURVES["secp112r2"] q = 0x10 Q = mult(q, ec.G, ec) msg = "Satoshi Nakamoto" low_s = True sig = dsa.sign(msg, q, low_s, ec) assert dsa.verify(msg, Q, sig, ec) dersig = dsa.serialize(*sig, ec) assert dsa.verify(msg, Q, dersig, ec) r, s = dsa.deserialize(dersig) assert (r, s) == sig keys = dsa.recover_pubkeys(msg, sig, ec) assert len(keys) == 4 assert Q in keys for Q in keys: assert dsa.verify(msg, Q, sig, ec)
def test_pubkey_recovery(): ec = secp112r2 q = 0x10 Q = mult(q, ec.G, ec) msg = "Satoshi Nakamoto" k = None sig = dsa.sign(msg, q, k, ec) assert dsa.verify(msg, Q, sig, ec) dersig = dsa.serialize(*sig, ec) assert dsa.verify(msg, Q, dersig, ec) r, s = dsa.deserialize(dersig) assert (r, s) == sig keys = dsa.recover_pubkeys(msg, sig, ec) assert len(keys) == 4 assert Q in keys for Q in keys: assert dsa.verify(msg, Q, sig, ec)
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)
keys = recover_pubkeys(msg1, (r1, sm)) for i, key in enumerate(keys): print( f" key#{i}: {'02' if key[1] % 2 == 0 else '03'} {hex(key[0]).upper()}") print("\n0. Another message to sign") msg2 = "and Paolo is right to be afraid" print(msg2) print("2. Sign message") r2, s2 = sign(msg2, q) print(f" r2: {hex(r2).upper()}") print(f" s2: {hex(s2).upper()}") print("3. Verify signature") print(verify(msg2, Q, (r2, s2))) print("4. Recover keys") keys = recover_pubkeys(msg2, (r2, s2)) for i, key in enumerate(keys): print( f" key#{i}: {'02' if key[1] % 2 == 0 else '03'} {hex(key[0]).upper()}") print("\n** Serialize signature") dersig = serialize(r2, s2) print(" bytes:", dersig) print("hex-string:", dersig.hex().upper()) r3, s3 = deserialize(dersig) if r2 == r3 and s2 == s3: print("Succesfully deserialized!")
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)