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_crack_prv_key() -> None: ec = CURVES["secp256k1"] q, _ = dsa.gen_keys(1) k = 1 + secrets.randbelow(ec.n - 1) msg1 = "Paolo is afraid of ephemeral random numbers".encode() m_1 = reduce_to_hlen(msg1) sig1 = dsa.sign_(m_1, q, k) msg2 = "and Paolo is right to be afraid".encode() m_2 = reduce_to_hlen(msg2) sig2 = dsa.sign_(m_2, q, k) q_cracked, k_cracked = dsa.crack_prv_key(msg1, sig1.serialize(), msg2, sig2) # if the lower_s convention has changed only one of s1 and s2 sig2 = dsa.Sig(sig2.r, ec.n - sig2.s) qc2, kc2 = dsa.crack_prv_key(msg1, sig1, msg2, sig2.serialize()) assert (q == q_cracked and k in (k_cracked, ec.n - k_cracked)) or ( q == qc2 and k in (kc2, ec.n - kc2)) with pytest.raises(BTClibValueError, match="not the same r in signatures"): dsa.crack_prv_key(msg1, sig1, msg2, dsa.Sig(16, sig1.s)) with pytest.raises(BTClibValueError, match="identical signatures"): dsa.crack_prv_key(msg1, sig1, msg1, sig1) a = ec._a # pylint: disable=protected-access b = ec._b # pylint: disable=protected-access alt_ec = Curve(ec.p, a, b, ec.double_aff(ec.G), ec.n, ec.cofactor) sig = dsa.Sig(sig1.r, sig1.s, alt_ec) with pytest.raises(BTClibValueError, match="not the same curve in signatures"): dsa.crack_prv_key(msg1, sig, msg2, sig2)
def test_rfc6979_tv() -> None: fname = "rfc6979.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r") as file_: test_dict = json.load(file_) lower_s = False 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) msg = msg.encode() m = reduce_to_hlen(msg, hf=getattr(hashlib, hf)) # test RFC6979 implementation k2 = rfc6979_(m, x, ec, getattr(hashlib, hf)) assert int(k, 16) == k2 # test RFC6979 usage in DSA sig = dsa.sign_(m, x, k2, lower_s, ec=ec, hf=getattr(hashlib, hf)) assert int(r, 16) == sig.r assert int(s, 16) == sig.s # test that RFC6979 is the default nonce for DSA sig = dsa.sign_(m, x, None, lower_s, ec=ec, hf=getattr(hashlib, hf)) assert int(r, 16) == sig.r assert int(s, 16) == sig.s # 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, lower_s, getattr(hashlib, hf))
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 dsa_commit_sign_( commit_hash: Octets, msg_hash: Octets, prv_key: PrvKey, nonce: Optional[PrvKey] = None, lower_s: bool = True, ec: Curve = secp256k1, hf: HashF = sha256, ) -> Tuple[dsa.Sig, Point]: "Include a commitment inside an EC DSA signature." nonce = (rfc6979_(msg_hash, prv_key, ec, hf) if nonce is None else int_from_prv_key(nonce, ec)) R = mult(nonce, ec.G, ec) tweaked_nonce = (nonce + _tweak(commit_hash, R, ec, hf)) % ec.n tweaked_sig = dsa.sign_(msg_hash, prv_key, tweaked_nonce, lower_s, ec=ec, hf=hf) return tweaked_sig, R
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)