Esempio n. 1
0
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)
Esempio n. 2
0
def test_crack_prvkey() -> None:

    ec = CURVES["secp256k1"]

    q, x_Q = ssa.gen_keys()

    msg1 = "Paolo is afraid of ephemeral random numbers"
    m_1 = reduce_to_hlen(msg1)
    k = ssa._det_nonce(m_1, q)
    sig1 = ssa._sign(m_1, q, k)

    msg2 = "and Paolo is right to be afraid"
    m_2 = reduce_to_hlen(msg2)
    # reuse same k
    sig2 = ssa._sign(m_2, q, k)

    qc, kc = ssa.crack_prvkey(msg1, sig1, msg2, sig2, x_Q)
    assert q == qc
    assert k in (kc, ec.n - kc)

    with pytest.raises(BTClibValueError, match="not the same r in signatures"):
        ssa._crack_prvkey(m_1, sig1, m_2, (16, sig1[1]), x_Q)

    with pytest.raises(BTClibValueError, match="identical signatures"):
        ssa._crack_prvkey(m_1, sig1, m_1, sig1, x_Q)
Esempio n. 3
0
def test_crack_prv_key() -> None:

    q, x_Q = ssa.gen_keys(19)  # remove any randomness

    msg1 = "Paolo is afraid of ephemeral random numbers".encode()
    m_1 = reduce_to_hlen(msg1)
    k = ssa.det_nonce_(m_1, q, aux=32 * b"\x01")  # remove any randomness
    sig1 = ssa.sign_(m_1, q, k)

    msg2 = "and Paolo is right to be afraid".encode()
    m_2 = reduce_to_hlen(msg2)
    # reuse same k
    sig2 = ssa.sign_(m_2, q, k)

    qc, kc = ssa.crack_prv_key(msg1, sig1, msg2, sig2, x_Q)
    assert q == qc
    assert k in (kc, sig1.ec.n - kc)

    qc, kc = ssa.crack_prv_key(msg1, sig1.serialize(), msg2, sig2.serialize(),
                               x_Q)
    assert q == qc
    assert k in (kc, sig1.ec.n - kc)

    sig = ssa.Sig(16, sig2.s, sig2.ec)
    with pytest.raises(BTClibValueError, match="not the same r in signatures"):
        ssa.crack_prv_key_(m_1, sig1, m_2, sig, x_Q)

    with pytest.raises(BTClibValueError, match="identical signatures"):
        ssa.crack_prv_key_(m_1, sig1, m_1, sig1, x_Q)

    sig = ssa.Sig(sig1.r, sig1.s, CURVES["secp256r1"])
    with pytest.raises(BTClibValueError,
                       match="not the same curve in signatures"):
        ssa.crack_prv_key_(m_1, sig, m_2, sig2, x_Q)
Esempio n. 4
0
def ssa_verify_commit(
    commit: Octets,
    receipt: Point,
    msg: Octets,
    pub_key: ssa.BIP340PubKey,
    sig: ssa.Sig,
    hf: HashF = sha256,
) -> bool:
    "Open the commitment associated to an EC SSA signature."
    commit_hash = reduce_to_hlen(commit, hf)
    msg_hash = reduce_to_hlen(msg, hf)
    return ssa_verify_commit_(commit_hash, receipt, msg_hash, pub_key, sig, hf)
Esempio n. 5
0
def crack_prv_key(
    msg1: Octets,
    sig1: Union[Sig, Octets],
    msg2: Octets,
    sig2: Union[Sig, Octets],
    hf: HashF = sha256,
) -> Tuple[int, int]:

    msg_hash1 = reduce_to_hlen(msg1, hf)
    msg_hash2 = reduce_to_hlen(msg2, hf)

    return crack_prv_key_(msg_hash1, sig1, msg_hash2, sig2, hf)
Esempio n. 6
0
def ssa_commit_sign(
    commit: Octets,
    msg: Octets,
    prv_key: PrvKey,
    nonce: Optional[PrvKey] = None,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> Tuple[ssa.Sig, Point]:
    "Include a commitment inside an EC SSA signature."

    commit_hash = reduce_to_hlen(commit, hf)
    msg_hash = reduce_to_hlen(msg, hf)
    return ssa_commit_sign_(commit_hash, msg_hash, prv_key, nonce, ec, hf)
Esempio n. 7
0
def dsa_verify_commit(
    commit: Octets,
    receipt: Point,
    msg: Octets,
    key: dsa.Key,
    sig: dsa.Sig,
    lower_s: bool = True,
    hf: HashF = sha256,
) -> bool:
    "Open the commitment associated to an EC DSA signature."
    commit_hash = reduce_to_hlen(commit, hf)
    msg_hash = reduce_to_hlen(msg, hf)
    return dsa_verify_commit_(commit_hash, receipt, msg_hash, key, sig,
                              lower_s, hf)
Esempio n. 8
0
def sign(
    msg: Octets,
    prv_key: PrvKey,
    nonce: Optional[PrvKey] = None,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> Sig:
    """Sign message according to BIP340 signature algorithm.

    The message msg is first processed by hf, yielding the value

        msg_hash = hf(msg),

    a sequence of bits of length *hf_len*.

    Normally, hf is chosen such that its output length *hf_len* is
    roughly equal to *nlen*, the bit-length of the group order *n*,
    since the overall security of the signature scheme will depend on
    the smallest of *hf_len* and *nlen*; however, ECSSA
    supports all combinations of *hf_len* and *nlen*.

    The BIP340 deterministic nonce (not RFC6979) is used.
    """

    msg_hash = reduce_to_hlen(msg, hf)
    return sign_(msg_hash, prv_key, nonce, ec, hf)
def test_libsecp256k1() -> None:
    msg = "Satoshi Nakamoto".encode()

    q, _ = dsa.gen_keys(0x1)
    sig = dsa.sign(msg, q)

    msg_hash = reduce_to_hlen(msg)
    secret = q.to_bytes(32, "big")

    c_sig = ffi.new("secp256k1_ecdsa_signature *")
    if not lib.secp256k1_ecdsa_sign(GLOBAL_CTX, c_sig, msg_hash, secret,
                                    ffi.NULL, ffi.NULL):
        raise RuntimeError("libsecp256k1 signature failed")

    output = ffi.new("unsigned char[%d]" % CDATA_SIG_LENGTH)
    if not lib.secp256k1_ecdsa_signature_serialize_compact(
            GLOBAL_CTX, output, c_sig):
        raise RuntimeError("libsecp256k1 signature serialization failed")

    c_sig_bytes = bytes(ffi.buffer(output, CDATA_SIG_LENGTH))

    r = c_sig_bytes[:32]
    s = c_sig_bytes[32:]

    assert r.hex() == sig.r.to_bytes(32, "big").hex()
    assert s.hex() == sig.s.to_bytes(32, "big").hex()
Esempio n. 10
0
def assert_as_valid(msg: Octets,
                    Q: BIP340PubKey,
                    sig: Union[Sig, Octets],
                    hf: HashF = sha256) -> None:

    msg_hash = reduce_to_hlen(msg, hf)
    assert_as_valid_(msg_hash, Q, sig, hf)
Esempio n. 11
0
def verify(
    msg: Octets, Q: BIP340PubKey, sig: Union[Sig, Octets], hf: HashF = sha256
) -> bool:
    "Verify the BIP340 signature of the provided message."

    msg_hash = reduce_to_hlen(msg, hf)
    return verify_(msg_hash, Q, sig, hf)
Esempio n. 12
0
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)
Esempio n. 13
0
def sign(
    msg: Octets,
    prv_key: PrvKey,
    nonce: Optional[PrvKey] = None,
    lower_s: bool = True,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> Sig:
    """ECDSA signature with canonical low-s preference.

    Implemented according to SEC 1 v.2
    The message msg is first processed by hf, yielding the value

        msg_hash = hf(msg),

    a sequence of bits of length *hf_len*.

    Normally, hf is chosen such that its output length *hf_len* is
    roughly equal to *nlen*, the bit-length of the group order *n*,
    since the overall security of the signature scheme will depend on
    the smallest of *hf_len* and *nlen*; however, the ECDSA standard
    supports all combinations of *hf_len* and *nlen*.

    RFC6979 is used for deterministic nonce.

    See https://tools.ietf.org/html/rfc6979#section-3.2
    """

    msg_hash = reduce_to_hlen(msg, hf)
    return sign_(msg_hash, prv_key, nonce, lower_s, ec, hf)
Esempio n. 14
0
def test_batch_validation() -> None:

    ms: List[String] = []
    Qs: List[int] = []
    sigs: List[ssa.SSASig] = []
    err_msg = "no signatures provided"
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_batch_as_valid(ms, Qs, sigs)
    assert not ssa.batch_verify(ms, Qs, sigs)

    # valid size for String input to sign, not for Octets input to _sign
    msg_size = 16
    ms.append(secrets.token_bytes(msg_size))
    q, Q = ssa.gen_keys()
    Qs.append(Q)
    sigs.append(ssa.sign(ms[0], q))
    # test with only 1 sig
    ssa.assert_batch_as_valid(ms, Qs, sigs)
    assert ssa.batch_verify(ms, Qs, sigs)
    for _ in range(3):
        m = secrets.token_bytes(msg_size)
        ms.append(m)
        q, Q = ssa.gen_keys()
        Qs.append(Q)
        sigs.append(ssa.sign(m, q))
    ssa.assert_batch_as_valid(ms, Qs, sigs)
    assert ssa.batch_verify(ms, Qs, sigs)

    ms.append(ms[0])
    sigs.append(sigs[1])
    Qs.append(Qs[0])
    err_msg = "signature verification failed"
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_batch_as_valid(ms, Qs, sigs)
    assert not ssa.batch_verify(ms, Qs, sigs)
    sigs[-1] = sigs[0]  # valid again

    ms.append(ms[0])  # add extra message
    err_msg = "mismatch between number of pubkeys "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_batch_as_valid(ms, Qs, sigs)
    assert not ssa.batch_verify(ms, Qs, sigs)
    ms.pop()  # valid again

    sigs.append(sigs[0])  # add extra sig
    err_msg = "mismatch between number of pubkeys "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_batch_as_valid(ms, Qs, sigs)
    assert not ssa.batch_verify(ms, Qs, sigs)
    sigs.pop()  # valid again

    ms = [reduce_to_hlen(m, hf) for m in ms]
    ms[0] = ms[0][:-1]
    err_msg = "invalid size: 31 bytes instead of 32"
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa._assert_batch_as_valid(ms, Qs, sigs)
    assert not ssa._batch_verify(ms, Qs, sigs)
Esempio n. 15
0
def assert_batch_as_valid(
    ms: Sequence[Octets],
    Qs: Sequence[BIP340PubKey],
    sigs: Sequence[Sig],
    hf: HashF = sha256,
) -> None:

    m_hashes = [reduce_to_hlen(msg, hf) for msg in ms]
    return assert_batch_as_valid_(m_hashes, Qs, sigs, hf)
Esempio n. 16
0
def batch_verify(
    ms: Sequence[Octets],
    Qs: Sequence[BIP340PubKey],
    sigs: Sequence[Sig],
    hf: HashF = sha256,
) -> bool:
    "Batch verification of BIP340 signatures."

    m_hashes = [reduce_to_hlen(msg, hf) for msg in ms]
    return batch_verify_(m_hashes, Qs, sigs, hf)
Esempio n. 17
0
def verify(
    msg: Octets,
    key: Key,
    sig: Union[Sig, Octets],
    lower_s: bool = True,
    hf: HashF = sha256,
) -> bool:
    """ECDSA signature verification (SEC 1 v.2 section 4.1.4)."""

    msg_hash = reduce_to_hlen(msg, hf)
    return verify_(msg_hash, key, sig, lower_s, hf)
Esempio n. 18
0
def recover_pub_keys(msg: Octets,
                     sig: Union[Sig, Octets],
                     lower_s: bool = True,
                     hf: HashF = sha256) -> List[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
    """

    msg_hash = reduce_to_hlen(msg, hf)
    return recover_pub_keys_(msg_hash, sig, lower_s, hf)
Esempio n. 19
0
def assert_as_valid(
    msg: 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

    msg_hash = reduce_to_hlen(msg, hf)
    assert_as_valid_(msg_hash, key, sig, lower_s, hf)
Esempio n. 20
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)
Esempio n. 21
0
def test_libsecp256k1() -> None:

    try:
        import btclib_libsecp256k1.ssa  # pylint: disable=import-outside-toplevel
    except ImportError:  # pragma: no cover
        pytest.skip()

    prvkey, X_Q = ssa.gen_keys(0x1)
    pubkey_bytes = X_Q.to_bytes(32, "big")
    msg = "Satoshi Nakamoto".encode()
    msg_hash = reduce_to_hlen(msg)

    libsecp256k1_sig = btclib_libsecp256k1.ssa.sign(msg_hash, prvkey)
    btclib_sig = ssa.sign_(msg_hash, prvkey)

    assert btclib_libsecp256k1.ssa.verify(msg_hash, pubkey_bytes,
                                          btclib_sig.serialize())
    assert ssa.verify(msg, X_Q, libsecp256k1_sig)
Esempio n. 22
0
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))
Esempio n. 23
0
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))
Esempio n. 24
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)
Esempio n. 25
0
def test_signature() -> None:
    msg = "Satoshi Nakamoto".encode()

    q, x_Q = ssa.gen_keys(0x01)
    sig = ssa.sign(msg, q)
    ssa.assert_as_valid(msg, x_Q, sig)
    assert ssa.verify(msg, x_Q, sig)
    assert sig == ssa.Sig.parse(sig.serialize())
    assert sig == ssa.Sig.parse(sig.serialize().hex())

    msg_fake = "Craig Wright".encode()
    assert not ssa.verify(msg_fake, x_Q, sig)
    err_msg = r"y_K is odd|signature verification failed"
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_as_valid(msg_fake, x_Q, sig)

    _, x_Q_fake = ssa.gen_keys(0x02)
    assert not ssa.verify(msg, x_Q_fake, sig)
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q_fake, sig)

    _, x_Q_fake = ssa.gen_keys(0x4)
    assert not ssa.verify(msg, x_Q_fake, sig)
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q_fake, sig)

    err_msg = "not a BIP340 public key"
    with pytest.raises(BTClibTypeError, match=err_msg):
        ssa.assert_as_valid(msg, INF, sig)  # type: ignore
    with pytest.raises(BTClibTypeError, match=err_msg):
        ssa.point_from_bip340pub_key(INF)  # type: ignore

    sig_invalid = ssa.Sig(sig.ec.p, sig.s, check_validity=False)
    assert not ssa.verify(msg, x_Q, sig_invalid)
    err_msg = "x-coordinate not in 0..p-1: "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q, sig_invalid)

    sig_invalid = ssa.Sig(sig.r, sig.ec.p, check_validity=False)
    assert not ssa.verify(msg, x_Q, sig_invalid)
    err_msg = "scalar s not in 0..n-1: "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q, sig_invalid)

    m_bytes = reduce_to_hlen(msg, hf)
    err_msg = "invalid size: 31 bytes instead of 32"
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_as_valid_(m_bytes[:31], x_Q, sig)

    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.sign_(m_bytes[:31], q)

    err_msg = "private key not in 1..n-1: "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.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):
        ssa.sign_(m_bytes, q, 0)
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.sign_(m_bytes, q, sig.ec.n)
Esempio n. 26
0
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)
Esempio n. 27
0
def test_threshold() -> None:
    "testing 2-of-3 threshold signature (Pedersen secret sharing)"

    ec = CURVES["secp256k1"]

    # parameters
    m = 2
    H = second_generator(ec)

    # FIRST PHASE: key pair generation ###################################

    # 1.1 signer one acting as the dealer
    commits1: List[Point] = []
    q1, _ = ssa.gen_keys()
    q1_prime, _ = ssa.gen_keys()
    commits1.append(double_mult(q1_prime, H, q1, ec.G))
    # sharing polynomials
    f1 = [q1]
    f1_prime = [q1_prime]
    for i in range(1, m):
        f1.append(ssa.gen_keys()[0])
        f1_prime.append(ssa.gen_keys()[0])
        commits1.append(double_mult(f1_prime[i], H, f1[i], ec.G))
    # shares of the secret
    alpha12 = 0  # share of q1 belonging to signer two
    alpha12_prime = 0
    alpha13 = 0  # share of q1 belonging to signer three
    alpha13_prime = 0
    for i in range(m):
        alpha12 += (f1[i] * pow(2, i)) % ec.n
        alpha12_prime += (f1_prime[i] * pow(2, i)) % ec.n
        alpha13 += (f1[i] * pow(3, i)) % ec.n
        alpha13_prime += (f1_prime[i] * pow(3, i)) % ec.n
    # signer two verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(2, i), commits1[i]))
    t = double_mult(alpha12_prime, H, alpha12, ec.G)
    assert t == RHS, "signer one is cheating"
    # signer three verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(3, i), commits1[i]))
    t = double_mult(alpha13_prime, H, alpha13, ec.G)
    assert t == RHS, "signer one is cheating"

    # 1.2 signer two acting as the dealer
    commits2: List[Point] = []
    q2, _ = ssa.gen_keys()
    q2_prime, _ = ssa.gen_keys()
    commits2.append(double_mult(q2_prime, H, q2, ec.G))
    # sharing polynomials
    f2 = [q2]
    f2_prime = [q2_prime]
    for i in range(1, m):
        f2.append(ssa.gen_keys()[0])
        f2_prime.append(ssa.gen_keys()[0])
        commits2.append(double_mult(f2_prime[i], H, f2[i], ec.G))
    # shares of the secret
    alpha21 = 0  # share of q2 belonging to signer one
    alpha21_prime = 0
    alpha23 = 0  # share of q2 belonging to signer three
    alpha23_prime = 0
    for i in range(m):
        alpha21 += (f2[i] * pow(1, i)) % ec.n
        alpha21_prime += (f2_prime[i] * pow(1, i)) % ec.n
        alpha23 += (f2[i] * pow(3, i)) % ec.n
        alpha23_prime += (f2_prime[i] * pow(3, i)) % ec.n
    # signer one verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(1, i), commits2[i]))
    t = double_mult(alpha21_prime, H, alpha21, ec.G)
    assert t == RHS, "signer two is cheating"
    # signer three verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(3, i), commits2[i]))
    t = double_mult(alpha23_prime, H, alpha23, ec.G)
    assert t == RHS, "signer two is cheating"

    # 1.3 signer three acting as the dealer
    commits3: List[Point] = []
    q3, _ = ssa.gen_keys()
    q3_prime, _ = ssa.gen_keys()
    commits3.append(double_mult(q3_prime, H, q3, ec.G))
    # sharing polynomials
    f3 = [q3]
    f3_prime = [q3_prime]
    for i in range(1, m):
        f3.append(ssa.gen_keys()[0])
        f3_prime.append(ssa.gen_keys()[0])
        commits3.append(double_mult(f3_prime[i], H, f3[i], ec.G))
    # shares of the secret
    alpha31 = 0  # share of q3 belonging to signer one
    alpha31_prime = 0
    alpha32 = 0  # share of q3 belonging to signer two
    alpha32_prime = 0
    for i in range(m):
        alpha31 += (f3[i] * pow(1, i)) % ec.n
        alpha31_prime += (f3_prime[i] * pow(1, i)) % ec.n
        alpha32 += (f3[i] * pow(2, i)) % ec.n
        alpha32_prime += (f3_prime[i] * pow(2, i)) % ec.n
    # signer one verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(1, i), commits3[i]))
    t = double_mult(alpha31_prime, H, alpha31, ec.G)
    assert t == RHS, "signer three is cheating"
    # signer two verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(2, i), commits3[i]))
    t = double_mult(alpha32_prime, H, alpha32, ec.G)
    assert t == RHS, "signer three is cheating"
    # shares of the secret key q = q1 + q2 + q3
    alpha1 = (alpha21 + alpha31) % ec.n
    alpha2 = (alpha12 + alpha32) % ec.n
    alpha3 = (alpha13 + alpha23) % ec.n
    for i in range(m):
        alpha1 += (f1[i] * pow(1, i)) % ec.n
        alpha2 += (f2[i] * pow(2, i)) % ec.n
        alpha3 += (f3[i] * pow(3, i)) % ec.n

    # 1.4 it's time to recover the public key
    # each participant i = 1, 2, 3 shares Qi as follows
    # Q = Q1 + Q2 + Q3 = (q1 + q2 + q3) G
    A1: List[Point] = []
    A2: List[Point] = []
    A3: List[Point] = []
    for i in range(m):
        A1.append(mult(f1[i]))
        A2.append(mult(f2[i]))
        A3.append(mult(f3[i]))
    # signer one checks others' values
    RHS2 = INF
    RHS3 = INF
    for i in range(m):
        RHS2 = ec.add(RHS2, mult(pow(1, i), A2[i]))
        RHS3 = ec.add(RHS3, mult(pow(1, i), A3[i]))
    assert mult(alpha21) == RHS2, "signer two is cheating"
    assert mult(alpha31) == RHS3, "signer three is cheating"
    # signer two checks others' values
    RHS1 = INF
    RHS3 = INF
    for i in range(m):
        RHS1 = ec.add(RHS1, mult(pow(2, i), A1[i]))
        RHS3 = ec.add(RHS3, mult(pow(2, i), A3[i]))
    assert mult(alpha12) == RHS1, "signer one is cheating"
    assert mult(alpha32) == RHS3, "signer three is cheating"
    # signer three checks others' values
    RHS1 = INF
    RHS2 = INF
    for i in range(m):
        RHS1 = ec.add(RHS1, mult(pow(3, i), A1[i]))
        RHS2 = ec.add(RHS2, mult(pow(3, i), A2[i]))
    assert mult(alpha13) == RHS1, "signer one is cheating"
    assert mult(alpha23) == RHS2, "signer two is cheating"
    # commitment at the global sharing polynomial
    A: List[Point] = []
    for i in range(m):
        A.append(ec.add(A1[i], ec.add(A2[i], A3[i])))

    # aggregated public key
    Q = A[0]
    if Q[1] % 2:
        # print('Q has been negated')
        A[1] = ec.negate(A[1])  # pragma: no cover
        alpha1 = ec.n - alpha1  # pragma: no cover
        alpha2 = ec.n - alpha2  # pragma: no cover
        alpha3 = ec.n - alpha3  # pragma: no cover
        Q = ec.negate(Q)  # pragma: no cover

    # SECOND PHASE: generation of the nonces' pair  ######################
    # Assume signer one and three want to sign

    msg = "message to sign".encode()
    msg_hash = reduce_to_hlen(msg, hf)

    # 2.1 signer one acting as the dealer
    commits1 = []
    k1 = ssa.det_nonce_(msg_hash, q1, None, ec, hf)
    k1_prime = ssa.det_nonce_(msg_hash, q1_prime, None, ec, hf)
    commits1.append(double_mult(k1_prime, H, k1, ec.G))
    # sharing polynomials
    f1 = [k1]
    f1_prime = [k1_prime]
    for i in range(1, m):
        f1.append(ssa.gen_keys()[0])
        f1_prime.append(ssa.gen_keys()[0])
        commits1.append(double_mult(f1_prime[i], H, f1[i], ec.G))
    # shares of the secret
    beta13 = 0  # share of k1 belonging to signer three
    beta13_prime = 0
    for i in range(m):
        beta13 += (f1[i] * pow(3, i)) % ec.n
        beta13_prime += (f1_prime[i] * pow(3, i)) % ec.n
    # signer three verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(3, i), commits1[i]))
    t = double_mult(beta13_prime, H, beta13, ec.G)
    assert t == RHS, "signer one is cheating"

    # 2.2 signer three acting as the dealer
    commits3 = []
    k3 = ssa.det_nonce_(msg_hash, q3, None, ec, hf)
    k3_prime = ssa.det_nonce_(msg_hash, q3_prime, None, ec, hf)
    commits3.append(double_mult(k3_prime, H, k3, ec.G))
    # sharing polynomials
    f3 = [k3]
    f3_prime = [k3_prime]
    for i in range(1, m):
        f3.append(ssa.gen_keys()[0])
        f3_prime.append(ssa.gen_keys()[0])
        commits3.append(double_mult(f3_prime[i], H, f3[i], ec.G))
    # shares of the secret
    beta31 = 0  # share of k3 belonging to signer one
    beta31_prime = 0
    for i in range(m):
        beta31 += (f3[i] * pow(1, i)) % ec.n
        beta31_prime += (f3_prime[i] * pow(1, i)) % ec.n
    # signer one verifies consistency of his share
    RHS = INF
    for i in range(m):
        RHS = ec.add(RHS, mult(pow(1, i), commits3[i]))
    t = double_mult(beta31_prime, H, beta31, ec.G)
    assert t == RHS, "signer three is cheating"

    # 2.3 shares of the secret nonce
    beta1 = beta31 % ec.n
    beta3 = beta13 % ec.n
    for i in range(m):
        beta1 += (f1[i] * pow(1, i)) % ec.n
        beta3 += (f3[i] * pow(3, i)) % ec.n

    # 2.4 it's time to recover the public nonce
    # each participant i = 1, 3 shares Qi as follows
    B1: List[Point] = []
    B3: List[Point] = []
    for i in range(m):
        B1.append(mult(f1[i]))
        B3.append(mult(f3[i]))

    # signer one checks values from signer three
    RHS3 = INF
    for i in range(m):
        RHS3 = ec.add(RHS3, mult(pow(1, i), B3[i]))
    assert mult(beta31) == RHS3, "signer three is cheating"

    # signer three checks values from signer one
    RHS1 = INF
    for i in range(m):
        RHS1 = ec.add(RHS1, mult(pow(3, i), B1[i]))
    assert mult(beta13) == RHS1, "signer one is cheating"

    # commitment at the global sharing polynomial
    B: List[Point] = []
    for i in range(m):
        B.append(ec.add(B1[i], B3[i]))

    # aggregated public nonce
    K = B[0]
    if K[1] % 2:
        # print('K has been negated')
        B[1] = ec.negate(B[1])  # pragma: no cover
        beta1 = ec.n - beta1  # pragma: no cover
        beta3 = ec.n - beta3  # pragma: no cover
        K = ec.negate(K)  # pragma: no cover

    # PHASE THREE: signature generation ###

    # partial signatures
    e = ssa.challenge_(msg_hash, Q[0], K[0], ec, hf)
    gamma1 = (beta1 + e * alpha1) % ec.n
    gamma3 = (beta3 + e * alpha3) % ec.n

    # each participant verifies the other partial signatures

    # signer one
    RHS3 = ec.add(K, mult(e, Q))
    for i in range(1, m):
        temp = double_mult(pow(3, i), B[i], e * pow(3, i), A[i])
        RHS3 = ec.add(RHS3, temp)
    assert mult(gamma3) == RHS3, "signer three is cheating"

    # signer three
    RHS1 = ec.add(K, mult(e, Q))
    for i in range(1, m):
        temp = double_mult(pow(1, i), B[i], e * pow(1, i), A[i])
        RHS1 = ec.add(RHS1, temp)
    assert mult(gamma1) == RHS1, "signer one is cheating"

    # PHASE FOUR: aggregating the signature ###
    omega1 = 3 * mod_inv(3 - 1, ec.n) % ec.n
    omega3 = 1 * mod_inv(1 - 3, ec.n) % ec.n
    sigma = (gamma1 * omega1 + gamma3 * omega3) % ec.n

    sig = ssa.Sig(K[0], sigma, ec)

    assert ssa.verify_(msg_hash, Q[0], sig)

    # ADDITIONAL PHASE: reconstruction of the private key ###
    secret = (omega1 * alpha1 + omega3 * alpha3) % ec.n
    assert (q1 + q2 + q3) % ec.n in (secret, ec.n - secret)
Esempio n. 28
0
def test_signature() -> None:
    ec = CURVES["secp256k1"]
    msg = "Satoshi Nakamoto"

    q, x_Q = ssa.gen_keys(0x01)
    sig = ssa.sign(msg, q)
    ssa.assert_as_valid(msg, x_Q, sig)
    assert ssa.verify(msg, x_Q, sig)

    assert sig == ssa.deserialize(sig)

    ssa.assert_as_valid(msg, x_Q, sig)
    ssa.assert_as_valid(msg, x_Q, ssa.serialize(*sig))
    ssa.assert_as_valid(msg, x_Q, ssa.serialize(*sig).hex())

    msg_fake = "Craig Wright"
    assert not ssa.verify(msg_fake, x_Q, sig)
    err_msg = r"y_K is odd|signature verification failed"
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_as_valid(msg_fake, x_Q, sig)

    _, x_Q_fake = ssa.gen_keys(0x02)
    assert not ssa.verify(msg, x_Q_fake, sig)
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q_fake, sig)

    _, x_Q_fake = ssa.gen_keys(0x4)
    assert not ssa.verify(msg, x_Q_fake, sig)
    with pytest.raises(BTClibRuntimeError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q_fake, sig)

    err_msg = "not a BIP340 public key"
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_as_valid(msg, INF, sig)  # type: ignore
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.point_from_bip340pubkey(INF)  # type: ignore

    sig_fake = (sig[0], sig[1], sig[1])
    assert not ssa.verify(msg, x_Q, sig_fake)  # type: ignore
    err_msg = "too many values to unpack "
    with pytest.raises(ValueError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q, sig_fake)  # type: ignore

    sig_invalid = ec.p, sig[1]
    assert not ssa.verify(msg, x_Q, sig_invalid)
    err_msg = "x-coordinate not in 0..p-1: "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q, sig_invalid)

    sig_invalid = sig[0], ec.p
    assert not ssa.verify(msg, x_Q, sig_invalid)
    err_msg = "scalar s not in 0..n-1: "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.assert_as_valid(msg, x_Q, sig_invalid)

    m_fake = b"\x00" * 31
    err_msg = "invalid size: 31 bytes instead of 32"
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa._assert_as_valid(m_fake, x_Q, sig)

    with pytest.raises(BTClibValueError, match=err_msg):
        ssa._sign(m_fake, q)

    err_msg = "private key not in 1..n-1: "
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.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):
        ssa._sign(reduce_to_hlen(msg, hf), q, 0)
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa._sign(reduce_to_hlen(msg, hf), q, ec.n)

    err_msg = "invalid zero challenge"
    with pytest.raises(BTClibValueError, match=err_msg):
        ssa.__recover_pubkey(0, sig[0], sig[1], ec)