Exemplo n.º 1
0
def crack_prv_key_(
    msg_hash1: Octets,
    sig1: Union[Sig, Octets],
    msg_hash2: Octets,
    sig2: Union[Sig, Octets],
    hf: HashF = sha256,
) -> Tuple[int, int]:

    if isinstance(sig1, Sig):
        sig1.assert_valid()
    else:
        sig1 = Sig.parse(sig1)

    if isinstance(sig2, Sig):
        sig2.assert_valid()
    else:
        sig2 = Sig.parse(sig2)

    ec = sig2.ec
    if sig1.ec != ec:
        raise BTClibValueError("not the same curve in signatures")
    if sig1.r != sig2.r:
        raise BTClibValueError("not the same r in signatures")
    if sig1.s == sig2.s:
        raise BTClibValueError("identical signatures")

    c_1 = challenge_(msg_hash1, ec, hf)
    c_2 = challenge_(msg_hash2, ec, hf)

    nonce = (c_1 - c_2) * mod_inv(sig1.s - sig2.s, ec.n) % ec.n
    q = (sig2.s * nonce - c_2) * mod_inv(sig1.r, ec.n) % ec.n
    return q, nonce
Exemplo n.º 2
0
def recover_pub_key_(
    key_id: int,
    msg_hash: Octets,
    sig: Union[Sig, Octets],
    lower_s: bool = True,
    hf: HashF = sha256,
) -> Point:
    """ECDSA public key recovery (SEC 1 v.2 section 4.1.6).

    See also:
    https://crypto.stackexchange.com/questions/18105/how-does-recovering-the-public-key-from-an-ecdsa-signature-work/18106#18106
    """

    if isinstance(sig, Sig):
        sig.assert_valid()
    else:
        sig = Sig.parse(sig)

    # The message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    c = challenge_(msg_hash, sig.ec, hf)  # 1.5

    QJ = _recover_pub_key_(key_id, c, sig.r, sig.s, lower_s, sig.ec)
    return sig.ec.aff_from_jac(QJ)
Exemplo n.º 3
0
def test_der_size() -> None:

    sig8 = 1, 1
    sig72 = ec.n - 2, ec.n - 1
    sig71 = 2**255 - 4, ec.n - 1
    sig70 = 2**255 - 4, 2**255 - 1
    sig70b = 2**255 - 4, 2**248 - 1
    sig69 = 2**255 - 4, 2**247 - 1
    sig68 = 2**247 - 1, 2**247 - 1
    sigs = [sig8, sig72, sig71, sig70, sig70b, sig69, sig68]
    lenghts = [8, 72, 71, 70, 70, 69, 68]

    for length, (r, s) in zip(lenghts, sigs):
        sig = Sig(r, s)
        assert r == sig.r
        assert s == sig.s
        assert ec == sig.ec
        sig_bin = sig.serialize()
        assert len(sig_bin) == length
        assert sig == Sig.parse(sig_bin)
Exemplo n.º 4
0
def assert_as_valid_(
    msg_hash: Octets,
    key: Key,
    sig: Union[Sig, Octets],
    lower_s: bool = True,
    hf: HashF = sha256,
) -> None:
    # Private function for test/dev purposes
    # It raises Errors, while verify should always return True or False

    if isinstance(sig, Sig):
        sig.assert_valid()
    else:
        sig = Sig.parse(sig)

    c = challenge_(msg_hash, sig.ec, hf)  # 2, 3

    Q = point_from_key(key, sig.ec)
    QJ = Q[0], Q[1], 1

    # second part delegated to helper function
    _assert_as_valid_(c, QJ, sig.r, sig.s, lower_s, sig.ec)
Exemplo n.º 5
0
print("** Verify malleated signature")
print(verify(msg1, Q, Sig(sig1.r, sm)))
print(verify(msg1, Q, Sig(sig1.r, sm), False))

print("\n1. Another message to sign")
msg2 = "and Paolo is right to be afraid".encode()
print(msg2.decode())

print("2. Sign message")
sig2 = sign(msg2, q)
print(f"    r2:    {hex(sig2.r).upper()}")
print(f"    s2:    {hex(sig2.s).upper()}")

print("3. Verify signature")
print(verify(msg2, Q, sig2))

print("4. Recover keys")
keys = recover_pub_keys(msg2, sig2)
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 = sig2.serialize()
print("     bytes:", dersig)
print("hex-string:", dersig.hex().upper())
sig3 = Sig.parse(dersig)
if sig2.r == sig3.r and sig2.s == sig3.s:
    print("Succesfully parsed!")
Exemplo n.º 6
0
def test_der_deserialize() -> None:

    err_msg = "non-hexadecimal number found "
    with pytest.raises(ValueError, match=err_msg):
        Sig.parse("not a sig")

    sig = Sig(2**255 - 4, 2**247 - 1)
    sig_bin = sig.serialize()
    r_size = sig_bin[3]

    bad_sig_bin = b"\x31" + sig_bin[1:]
    err_msg = "invalid compound header: "
    with pytest.raises(BTClibValueError, match=err_msg):
        Sig.parse(bad_sig_bin)

    bad_sig_bin = sig_bin[:1] + b"\x41" + sig_bin[2:]
    err_msg = "not enough binary data"
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        Sig.parse(bad_sig_bin)

    # r and s scalars
    for offset in (4, 6 + r_size):
        bad_sig_bin = sig_bin[:offset - 2] + b"\x00" + sig_bin[offset - 1:]
        err_msg = "invalid value header: "
        with pytest.raises(BTClibValueError, match=err_msg):
            Sig.parse(bad_sig_bin)

        bad_sig_bin = sig_bin[:offset - 1] + b"\x00" + sig_bin[offset:]
        err_msg = "zero size"
        with pytest.raises(BTClibRuntimeError, match=err_msg):
            Sig.parse(bad_sig_bin)

        bad_sig_bin = sig_bin[:offset - 1] + b"\x80" + sig_bin[offset:]
        err_msg = "not enough binary data"
        with pytest.raises(BTClibRuntimeError, match=err_msg):
            Sig.parse(bad_sig_bin)

        bad_sig_bin = sig_bin[:offset] + b"\x80" + sig_bin[offset + 1:]
        err_msg = "invalid negative scalar"
        with pytest.raises(BTClibValueError, match=err_msg):
            Sig.parse(bad_sig_bin)

        bad_sig_bin = sig_bin[:offset] + b"\x00\x7f" + sig_bin[offset + 2:]
        err_msg = "invalid 'highest bit set' padding"
        with pytest.raises(BTClibValueError, match=err_msg):
            Sig.parse(bad_sig_bin)

    data_size = sig_bin[1]
    malleated_size = (data_size + 1).to_bytes(1, byteorder="big", signed=False)
    bad_sig_bin = sig_bin[:1] + malleated_size + sig_bin[2:] + b"\x01"
    err_msg = "invalid DER sequence length"
    with pytest.raises(BTClibValueError, match=err_msg):
        Sig.parse(bad_sig_bin)