def test_crack_prvkey() -> None: ec = CURVES["secp256k1"] # FIXME: make it random q = 0x17E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 msg1 = "Paolo is afraid of ephemeral random numbers" m_1 = reduce_to_hlen(msg1) k = _rfc6979(m_1, q) sig1 = dsa._sign(m_1, q, k) msg2 = "and Paolo is right to be afraid" m_2 = reduce_to_hlen(msg2) # reuse same k sig2 = dsa._sign(m_2, q, k) qc, kc = dsa.crack_prvkey(msg1, sig1, msg2, sig2) assert q in (qc, ec.n - qc) assert q == qc assert k in (kc, ec.n - kc) # assert k == kc with pytest.raises(BTClibValueError, match="not the same r in signatures"): dsa.crack_prvkey(msg1, sig1, msg2, (16, sig1[1])) with pytest.raises(BTClibValueError, match="identical signatures"): dsa.crack_prvkey(msg1, sig1, msg1, sig1)
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 sign(ec: Curve, hf: Callable[[Any], Any], msg: bytes, q: int, k: Optional[int] = None) -> ECDS: """ECDSA signing operation according to SEC 1 http://www.secg.org/sec1-v2.pdf """ # https://tools.ietf.org/html/rfc6979#section-3.2 # The message msg is first processed by hf, yielding the value # msghd = hf(msg), a sequence of bits of length hlen. # Normally, hf is chosen such that # its output length hlen is roughly equal to nlen, since the overall # security of the signature scheme will depend on the smallest of hlen # and nlen; however, the ECDSA standard support all combinations of # hlen and nlen. # Steps numbering follows SEC 1 v.2 section 4.1.3 msghd = hf(msg).digest() # 4 # H(msg) is transformed into an integer modulo ec.n using int_from_bits: c = int_from_bits(ec, msghd) # 5 # The secret key q: an integer in the range 1..n-1. # SEC 1 v.2 section 3.2.1 if not 0 < q < ec.n: raise ValueError(f"private key {hex(q)} not in [1, n-1]") if k is None: k = _rfc6979(ec, hf, c, q) # 1 if not 0 < k < ec.n: raise ValueError(f"ephemeral key {hex(k)} not in [1, n-1]") # second part delegated to helper function return _sign(ec, c, q, k)
return r == v # 8 random.seed(42) # setup qs = [] es = [] Qs = [] sigs = [] for _ in range(5): q = random.getrandbits(ec.nlen) % ec.n qs.append(q) Qs.append(ec.mult(q, ec.G)) e = random.getrandbits(ec.nlen) % ec.n es.append(e) k = _rfc6979(e, q) sigs.append(_sign(e, q, k)) start = time.time() for i in range(len(qs)): assert _verhlp(ec, es[i], Qs[i], sigs[i], True) elapsed1 = time.time() - start start = time.time() for i in range(len(qs)): assert _verhlp(ec, es[i], Qs[i], sigs[i], False) elapsed2 = time.time() - start print(elapsed2 / elapsed1)