Exemplo n.º 1
0
def test_signature() -> None:
    msg = "test message".encode()

    wif, addr = bms.gen_keys()
    bms_sig = bms.sign(msg, wif)
    bms.assert_as_valid(msg, addr, bms_sig)
    assert bms.verify(msg, addr, bms_sig)
    assert bms_sig == bms.Sig.parse(bms_sig.serialize())
    assert bms_sig == bms.Sig.parse(bms_sig.serialize().hex())
    assert bms_sig == bms.Sig.b64decode(bms_sig.b64encode())
    assert bms_sig == bms.Sig.b64decode(bms_sig.b64encode().encode("ascii"))

    assert bms_sig == bms.sign(msg, wif.encode("ascii"))

    # malleated signature
    dsa_sig = dsa.Sig(bms_sig.dsa_sig.r,
                      bms_sig.dsa_sig.ec.n - bms_sig.dsa_sig.s)
    # without updating rf verification will fail, even with lower_s=False
    bms_sig = bms.Sig(bms_sig.rf, dsa_sig)
    err_msg = "invalid p2pkh address: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr, bms_sig, lower_s=False)
    # update rf to satisfy above malleation
    i = 1 if bms_sig.rf % 2 else -1
    bms_sig = bms.Sig(bms_sig.rf + i, dsa_sig)
    bms.assert_as_valid(msg, addr, bms_sig, lower_s=False)
    assert bms.verify(msg, addr, bms_sig, lower_s=False)
    # anyway, with lower_s=True malleation does fail verification
    err_msg = "not a low s"
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr, bms_sig, lower_s=True)

    # bms_sig taken from (Electrum and) Bitcoin Core
    wif, addr = bms.gen_keys(
        "5KMWWy2d3Mjc8LojNoj8Lcz9B1aWu8bRofUgGwQk959Dw5h2iyw")
    bms_sig = bms.sign(msg, wif)
    bms.assert_as_valid(msg, addr, bms_sig)
    assert bms.verify(msg, addr, bms_sig)
    exp_sig = "G/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4="
    assert bms_sig.b64encode() == exp_sig

    bms.assert_as_valid(msg, addr, exp_sig)
    bms.assert_as_valid(msg, addr, exp_sig.encode("ascii"))

    dsa_sig = dsa.Sig(bms_sig.dsa_sig.r, bms_sig.dsa_sig.s,
                      CURVES["secp256r1"])
    err_msg = "invalid curve: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms_sig = bms.Sig(bms_sig.rf, dsa_sig)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
def test_vector_python_bitcoinlib() -> None:
    """Test python-bitcoinlib test vectors

    https://github.com/petertodd/python-bitcoinlib/blob/master/bitcoin/tests/test_data/bms.json
    """

    fname = "bms.json"
    filename = path.join(path.dirname(__file__), "_data", fname)
    with open(filename, "r") as file_:
        test_vectors = json.load(file_)

    for vector in test_vectors[:10]:
        msg = vector["address"].encode()

        # btclib self-consistency check
        bms_sig = bms.sign(msg, vector["wif"])
        assert bms.verify(msg, vector["address"], bms_sig)
        bms_sig_encoded = bms_sig.b64encode()
        assert bms.verify(msg, vector["address"], bms_sig_encoded)

        # Core/Electrum/btclib provide identical signature
        # they use "low-s" canonical signature
        assert bms_sig.dsa_sig.s < ec.n - bms_sig.dsa_sig.s
        assert bms.verify(msg, vector["address"], bms_sig_encoded, lower_s=True)

        # python-bitcoinlib provides a valid signature
        # but does not respect low-s
        assert bms.verify(msg, vector["address"], vector["signature"], lower_s=False)

        # python-bitcoinlib has a signature different from Core/Electrum/btclib
        assert bms_sig_encoded != vector["signature"]

        # but the reason is not the low-s
        # here's the malleated Core/Electrum/btclib signature
        s = ec.n - bms_sig.dsa_sig.s
        dsa_sig = dsa.Sig(bms_sig.dsa_sig.r, s, bms_sig.dsa_sig.ec)
        # properly malleated fixing also rf
        i = 1 if bms_sig.rf % 2 else -1
        bms_sig_malleated = bms.Sig(bms_sig.rf + i, dsa_sig)
        assert bms.verify(msg, vector["address"], bms_sig_malleated, lower_s=False)
        bms_sig_encoded = bms_sig_malleated.b64encode()
        assert bms.verify(msg, vector["address"], bms_sig_encoded, lower_s=False)

        # the malleated signature is still not equal to the python-bitcoinlib one
        assert bms_sig_encoded != vector["signature"]

        # python-bitcoinlib does not use RFC6979 deterministic nonce
        # as proved by different r compared to Core/Electrum/btclib
        test_vector_sig = bms.Sig.b64decode(vector["signature"])
        assert bms_sig.dsa_sig.r != test_vector_sig.dsa_sig.r
Exemplo n.º 4
0
def test_msgsign_p2pkh() -> None:
    msg = "test message".encode()
    # sigs are taken from (Electrum and) Bitcoin Core

    q = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"

    # uncompressed
    wif1u = b58.wif_from_prv_key(q, "mainnet", False)
    assert wif1u == "5KMWWy2d3Mjc8LojNoj8Lcz9B1aWu8bRofUgGwQk959Dw5h2iyw"
    add1u = b58.p2pkh(wif1u)
    assert add1u == "1HUBHMij46Hae75JPdWjeZ5Q7KaL7EFRSD"
    bms_sig1u = bms.sign(msg, wif1u)
    assert bms.verify(msg, add1u, bms_sig1u)
    assert bms_sig1u.rf == 27
    exp_sig1u = "G/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4="
    assert bms_sig1u.b64encode() == exp_sig1u

    # compressed
    wif1c = b58.wif_from_prv_key(q, "mainnet", True)
    assert wif1c == "L41XHGJA5QX43QRG3FEwPbqD5BYvy6WxUxqAMM9oQdHJ5FcRHcGk"
    add1c = b58.p2pkh(wif1c)
    assert add1c == "14dD6ygPi5WXdwwBTt1FBZK3aD8uDem1FY"
    bms_sig1c = bms.sign(msg, wif1c)
    assert bms.verify(msg, add1c, bms_sig1c)
    assert bms_sig1c.rf == 31
    exp_sig1c = "H/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4="
    assert bms_sig1c.b64encode() == exp_sig1c

    assert not bms.verify(msg, add1c, bms_sig1u)
    assert not bms.verify(msg, add1u, bms_sig1c)

    bms_sig = bms.Sig(bms_sig1c.rf + 1, bms_sig1c.dsa_sig)
    assert not bms.verify(msg, add1c, bms_sig)

    # malleate s
    s = ec.n - bms_sig1c.dsa_sig.s
    dsa_sig = dsa.Sig(bms_sig1c.dsa_sig.r, s, bms_sig1c.dsa_sig.ec)
    # without updating rf verification will fail, even with lower_s=False
    bms_sig = bms.Sig(bms_sig1c.rf, dsa_sig)
    assert not bms.verify(msg, add1c, bms_sig, lower_s=False)

    # update rf to satisfy above malleation
    i = 1 if bms_sig1c.rf % 2 else -1
    bms_sig = bms.Sig(bms_sig1c.rf + i, dsa_sig)
    assert bms.verify(msg, add1c, bms_sig, lower_s=False)

    # anyway, with lower_s=True malleation does fail verification
    err_msg = "not a low s"
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, add1c, bms_sig, lower_s=True)
Exemplo n.º 5
0
    def parse(cls: Type["Sig"],
              data: BinaryData,
              check_validity: bool = True) -> "Sig":

        stream = bytesio_from_binarydata(data)
        sig_bin = stream.read(_REQUIRED_LENGHT)

        if check_validity and len(sig_bin) != _REQUIRED_LENGHT:
            err_msg = f"invalid decoded length: {len(sig_bin)}"
            err_msg += f" instead of {_REQUIRED_LENGHT}"
            raise BTClibValueError(err_msg)

        rf = sig_bin[0]
        ec = secp256k1
        n_size = ec.n_size
        r = int.from_bytes(sig_bin[1:1 + n_size], "big", signed=False)
        s = int.from_bytes(sig_bin[1 + n_size:1 + 2 * n_size],
                           "big",
                           signed=False)
        dsa_sig = dsa.Sig(r, s, ec, check_validity=False)

        return cls(rf, dsa_sig, check_validity)
Exemplo n.º 6
0
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)