Example #1
0
    def test_bip340_vectors(self):
        """BIP340 (Schnorr) test vectors

        https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv
        """
        fname = "bip340_test_vectors.csv"
        filename = path.join(path.dirname(__file__), "test_data", fname)
        with open(filename, newline='') as csvfile:
            reader = csv.reader(csvfile)
            # skip column headers while checking that there are 7 columns
            _, _, _, _, _, _, _ = reader.__next__()
            for row in reader:
                (index, seckey, pubkey, mhd, sig, result, comment) = row
                errmsg = f"Test vector #{int(index)}"
                if seckey != '':
                    seckey = bytes.fromhex(seckey)
                    _, pubkey_actual = ssa.gen_keys(seckey)
                    self.assertEqual(pubkey,
                                     hex(pubkey_actual).upper()[2:], errmsg)

                    sig_actual = ssa.serialize(*ssa.sign(mhd, seckey))
                    self.assertEqual(sig, sig_actual.hex().upper(), errmsg)

                result = result == 'TRUE'
                if comment:
                    errmsg += ": " + comment
                result_actual = ssa.verify(mhd, pubkey, sig)
                self.assertEqual(result, result_actual, errmsg)
Example #2
0
    def test_ecssa(self):
        """Basic tests"""
        ec = secp256k1
        q = 0x1
        Q = mult(ec, q, ec.G)
        msg = hf('Satoshi Nakamoto'.encode()).digest()
        sig = ssa.sign(ec, hf, msg, q, None)
        # no source for the following... but
        # https://bitcointalk.org/index.php?topic=285142.40
        # same r because of rfc6979
        exp_sig = (
            0x934B1EA10A4B3C1757E2B0C017D0B6143CE3C9A7E6A4A49860D7A6AB210EE3D8,
            0x2DF2423F70563E3C4BD0E00BDEF658081613858F110ECF937A2ED9190BF4A01A)
        self.assertEqual(sig[0], exp_sig[0])
        self.assertEqual(sig[1], exp_sig[1])

        ssa._verify(ec, hf, msg, Q, sig)
        self.assertTrue(ssa.verify(ec, hf, msg, Q, sig))
        self.assertTrue(ssa._verify(ec, hf, msg, Q, sig))

        fmsg = hf('Craig Wright'.encode()).digest()
        self.assertFalse(ssa.verify(ec, hf, fmsg, Q, sig))
        self.assertFalse(ssa._verify(ec, hf, fmsg, Q, sig))

        fssasig = (sig[0], sig[1], sig[1])
        self.assertFalse(ssa.verify(ec, hf, msg, Q, fssasig))
        self.assertRaises(TypeError, ssa._verify, ec, hf, msg, Q, fssasig)

        # y(sG - eP) is not a quadratic residue
        fq = 0x2
        fQ = mult(ec, fq, ec.G)
        self.assertFalse(ssa.verify(ec, hf, msg, fQ, sig))
        self.assertRaises(ValueError, ssa._verify, ec, hf, msg, fQ, sig)

        fq = 0x4
        fQ = mult(ec, fq, ec.G)
        self.assertFalse(ssa.verify(ec, hf, msg, fQ, sig))
        self.assertFalse(ssa._verify(ec, hf, msg, fQ, sig))

        # not ec.pIsThreeModFour
        self.assertFalse(ssa.verify(secp224k1, hf, msg, Q, sig))
        self.assertRaises(ValueError, ssa._verify, secp224k1, hf, msg, Q, sig)

        # verify: message of wrong size
        wrongmsg = msg[:-1]
        self.assertFalse(ssa.verify(ec, hf, wrongmsg, Q, sig))
        self.assertRaises(ValueError, ssa._verify, ec, hf, wrongmsg, Q, sig)
        #ssa._verify(ec, hf, wrongmsg, Q, sig)

        # sign: message of wrong size
        self.assertRaises(ValueError, ssa.sign, ec, hf, wrongmsg, q, None)
        #ssa.sign(ec, hf, wrongmsg, q, None)

        # invalid (zero) challenge e
        self.assertRaises(ValueError, ssa._pubkey_recovery, ec, hf, 0, sig)
    def test_signtocontract(self):
        prv = 0x1
        pub = mult(ec, prv, ec.G)
        m = "to be signed".encode()
        c = "to be committed".encode()

        dsa_sig, dsa_receipt = ecdsa_commit_sign(c, ec, hf, m, prv, None)
        self.assertTrue(dsa.verify(ec, hf, m, pub, dsa_sig))
        self.assertTrue(verify_commit(c, ec, hf, dsa_receipt))

        # 32 bytes message for ECSSA
        m = hf(m).digest()
        ssa_sig, ssa_receipt = ecssa_commit_sign(c, ec, hf, m, prv, None)
        self.assertTrue(ssa.verify(ec, hf, m, pub, ssa_sig))
        self.assertTrue(verify_commit(c, ec, hf, ssa_receipt))
Example #4
0
    def test_signtocontract(self):
        prv = 0x1
        pub = mult(prv)
        m = b"to be signed"
        c = b"to be committed"

        dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, None)
        self.assertTrue(dsa.verify(m, pub, dsa_sig))
        self.assertTrue(verify_commit(c, dsa_receipt))

        # 32 bytes message for ECSSA
        m = sha256(m).digest()
        ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, None)
        self.assertTrue(ssa.verify(m, pub, ssa_sig))
        self.assertTrue(verify_commit(c, ssa_receipt))
Example #5
0
    def test_signature(self):
        """Basic tests"""

        q, x_Q = ssa.gen_keys()
        mhd = hf(b'Satoshi Nakamoto').digest()
        sig = ssa.sign(mhd, q, None)
        self.assertEqual(sig, ssa.deserialize(sig))
        ssa._verify(mhd, x_Q, sig, ec, hf)
        self.assertTrue(ssa.verify(mhd, x_Q, sig))

        fmhd = hf(b'Craig Wright').digest()
        self.assertRaises(AssertionError, ssa._verify, fmhd, x_Q, sig, ec, hf)

        fssasig = (sig[0], sig[1], sig[1])
        self.assertRaises(ValueError, ssa._verify, mhd, x_Q, fssasig, ec, hf)

        # y(sG - eP) is not a quadratic residue
        _, fQ = ssa.gen_keys(0x2)
        self.assertRaises(AssertionError, ssa._verify, mhd, fQ, sig, ec, hf)

        _, fQ = ssa.gen_keys(0x4)
        self.assertRaises(AssertionError, ssa._verify, mhd, fQ, sig, ec, hf)

        # not ec.pIsThreeModFour
        self.assertRaises(ValueError, ssa._verify, mhd, x_Q, sig, secp224k1,
                          hf)

        # verify: message of wrong size
        wrongmhd = mhd[:-1]
        self.assertRaises(ValueError, ssa._verify, wrongmhd, x_Q, sig, ec, hf)
        # ssa._verify(wrongmhd, x_Q, sig)

        # sign: message of wrong size
        self.assertRaises(ValueError, ssa.sign, wrongmhd, q, None)
        # ssa.sign(wrongmhd, q, None)

        # invalid (zero) challenge e
        self.assertRaises(ValueError, ssa._recover_pubkeys, 0, sig[0], sig[1],
                          ec)
        # ssa._recover_pubkeys(0, sig)

        # not a BIP340 public key
        self.assertRaises(ValueError, ssa._to_bip340_point,
                          ["not", "a BIP340", "public key"])
Example #6
0
    def test_ecssa(self):
        """Basic tests"""
        q = 0x1
        Q = mult(q)
        mhd = hf(b'Satoshi Nakamoto').digest()
        sig = ssa.sign(mhd, q, None)
        ssa._verify(mhd, Q, sig, ec, hf)
        self.assertTrue(ssa.verify(mhd, Q, sig))

        fmhd = hf(b'Craig Wright').digest()
        self.assertRaises(AssertionError, ssa._verify, fmhd, Q, sig, ec, hf)

        fssasig = (sig[0], sig[1], sig[1])
        self.assertRaises(ValueError, ssa._verify, mhd, Q, fssasig, ec, hf)

        # y(sG - eP) is not a quadratic residue
        fq = 0x2
        fQ = mult(fq)
        self.assertRaises(ValueError, ssa._verify, mhd, fQ, sig, ec, hf)

        fq = 0x4
        fQ = mult(fq)
        self.assertRaises(AssertionError, ssa._verify, mhd, fQ, sig, ec, hf)

        # not ec.pIsThreeModFour
        self.assertRaises(ValueError, ssa._verify, mhd, Q, sig, secp224k1, hf)

        # verify: message of wrong size
        wrongmhd = mhd[:-1]
        self.assertRaises(ValueError, ssa._verify, wrongmhd, Q, sig, ec, hf)
        #ssa._verify(wrongmhd, Q, sig)

        # sign: message of wrong size
        self.assertRaises(ValueError, ssa.sign, wrongmhd, q, None)
        #ssa.sign(wrongmhd, q, None)

        # invalid (zero) challenge e
        self.assertRaises(ValueError, ssa._recover_pubkeys, 0, sig[0], sig[1],
                          ec)
        #ssa._recover_pubkeys(0, sig)

        # not a BIP340 public key
        self.assertRaises(ValueError, ssa.to_bip340_pubkey_tuple,
                          ["not", "a BIP340", "public key"])
Example #7
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)
Example #8
0
    def test_musig(self):
        """testing 3-of-3 MuSig

            https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/musig/musig.md
            https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/
            https://eprint.iacr.org/2018/068
            https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html
            https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744
        """
        M = sha256(b'message to sign').digest()

        ec = secp256k1
        hf = sha256

        # key setup is not interactive

        # first signer
        q1 = int_from_octets(
            '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d92ad1d')
        Q1 = mult(q1)
        k1 = rfc6979(M, q1)
        K1 = mult(k1)

        # second signer
        q2 = int_from_octets(
            '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d')
        Q2 = mult(q2)
        k2 = rfc6979(M, q2)
        K2 = mult(k2)

        # third signer
        q3 = int_from_octets(
            '0c28fca386c7aff7600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d')
        Q3 = mult(q3)
        k3 = rfc6979(M, q3)
        K3 = mult(k3)

        # this is MuSig core: the rest is just Schnorr signature additivity
        L: List[Point] = list()  # multiset of public keys
        L.append(octets_from_point(Q1, False, ec))
        L.append(octets_from_point(Q2, False, ec))
        L.append(octets_from_point(Q3, False, ec))
        L.sort()  # using lexicographic ordering
        L_brackets = b''
        for i in range(len(L)):
            L_brackets += L[i]
        h1 = hf(L_brackets + octets_from_point(Q1, False, ec)).digest()
        a1 = int_from_bits(h1, ec)
        h2 = hf(L_brackets + octets_from_point(Q2, False, ec)).digest()
        a2 = int_from_bits(h2, ec)
        h3 = hf(L_brackets + octets_from_point(Q3, False, ec)).digest()
        a3 = int_from_bits(h3, ec)
        # aggregated public key
        Q = ec.add(double_mult(a1, Q1, a2, Q2), mult(a3, Q3))
        Q_bytes = octets_from_point(Q, True, ec)

        ########################
        # interactive signature: exchange K, compute s
        # WARNING: the signers should exchange commitments to the public
        #          nonces before sending the nonces themselves

        # first signer
        # K, r_bytes, and e as calculated by any signer
        # are the same as the ones by the other signers
        K = ec.add(ec.add(K1, K2), K3)
        r_bytes = K[0].to_bytes(32, byteorder='big')
        e = int_from_bits(hf(r_bytes + Q_bytes + M).digest(), ec)
        if legendre_symbol(K[1], ec._p) != 1:
            # no need to actually change K[1], as it is not used anymore
            # let's fix k1 instead, as it is used later
            # note that all other signers will change their k too
            k1 = ec.n - k1
        s1 = (k1 + e * a1 * q1) % ec.n

        # second signer
        # K, r_bytes, and e as calculated by any signer
        # are the same as the ones by the other signers
        if legendre_symbol(K[1], ec._p) != 1:
            # no need to actually change K[1], as it is not used anymore
            # let's fix k2 instead, as it is used later
            # note that all other signers will change their k too
            k2 = ec.n - k2
        s2 = (k2 + e * a2 * q2) % ec.n

        # third signer
        # K, r_bytes, and e as calculated by any signer
        # are the same as the ones by the other signers
        if legendre_symbol(K[1], ec._p) != 1:
            # no need to actually change K[1], as it is not used anymore
            # let's fix k3 instead, as it is used later
            # note that all other signers will change their k too
            k3 = ec.n - k3
        s3 = (k3 + e * a3 * q3) % ec.n

        ############################################
        # interactive signature: exchange signatures
        # combine all (K[0], s) signatures into a single signature
        # anyone can do the following
        sig = K[0], (s1 + s2 + s3) % ec.n

        self.assertTrue(ssa.verify(M, Q, sig))
Example #9
0
ec = secp256k1
hf = sha256
hsize = hf().digest_size
hlen = hsize * 8

# n = 1 loops forever and does not really test batch verify
n_sig = [2, 4, 8, 16, 32, 64, 128]
m = []
sig = []
Q = []
for j in range(max(n_sig)):
    m.append(random.getrandbits(hlen).to_bytes(hsize, 'big'))
    q = random.getrandbits(ec.nlen) % ec.n
    sig.append(sign(ec, hf, m[j], q))
    Q.append(mult(ec, q, ec.G))

for n in n_sig:

    # no batch
    start = time.time()
    for j in range(n):
        assert verify(ec, hf, m[j], Q[j], sig[j])
    elapsed1 = time.time() - start

    # batch
    start = time.time()
    assert batch_verify(ec, hf, m[:n], Q[:n], sig[:n])
    elapsed2 = time.time() - start

    print(n, elapsed2 / elapsed1)
Example #10
0
random.seed(42)

hsize = hf().digest_size
hlen = hsize * 8

# n = 1 loops forever and does not really test batch verify
n_sig = [2, 4, 8, 16, 32, 64, 128]
m = []
sig = []
Q = []
for j in range(max(n_sig)):
    m.append(random.getrandbits(hlen).to_bytes(hsize, 'big'))
    q = random.getrandbits(ec.nlen) % ec.n
    sig.append(sign(m[j], q))
    Q.append(mult(q, ec.G))

for n in n_sig:

    # no batch
    start = time.time()
    for j in range(n):
        assert verify(m[j], Q[j], sig[j])
    elapsed1 = time.time() - start

    # batch
    start = time.time()
    assert batch_verify(m[:n], Q[:n], sig[:n])
    elapsed2 = time.time() - start

    print(n, elapsed2 / elapsed1)
Example #11
0
    def test_musig(self):
        """ testing 3-of-3 MuSig
        
            https://eprint.iacr.org/2018/068
            https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html
            https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744
        """
        ec = secp256k1
        L: List[Point] = list()  # multiset of public keys
        M = hf('message to sign'.encode()).digest()

        # first signer
        q1 = int_from_octets(
            '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d92ad1d')
        Q1 = mult(ec, q1, ec.G)
        L.append(octets_from_point(ec, Q1, False))

        # ephemeral private nonce
        k1 = 0x012a2a833eac4e67e06611aba01345b85cdd4f5ad44f72e369ef0dd640424dbb
        K1 = mult(ec, k1, ec.G)
        K1_x = K1[0]
        if legendre_symbol(K1[1], ec._p) != 1:
            k1 = ec.n - k1
            K1 = K1_x, ec.y_quadratic_residue(K1_x, True)
            #K1 = mult(ec, k1, ec.G)

        # second signer
        q2 = int_from_octets(
            '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d')
        Q2 = mult(ec, q2, ec.G)
        L.append(octets_from_point(ec, Q2, False))

        k2 = 0x01a2a0d3eac4e67e06611aba01345b85cdd4f5ad44f72e369ef0dd640424dbdb
        K2 = mult(ec, k2, ec.G)
        K2_x = K2[0]
        if legendre_symbol(K2[1], ec._p) != 1:
            k2 = ec.n - k2
            K2 = K2_x, ec.y_quadratic_residue(K2_x, True)
            #K2 = mult(ec, k2, ec.G)

        # third signer
        q3 = random.getrandbits(ec.nlen) % ec.n
        Q3 = mult(ec, q3, ec.G)
        while Q3 == None:  # plausible only for small (test) cardinality groups
            q3 = random.getrandbits(ec.nlen) % ec.n
            Q3 = mult(ec, q3, ec.G)
        L.append(octets_from_point(ec, Q3, False))

        k3 = random.getrandbits(ec.nlen) % ec.n
        K3 = mult(ec, k3, ec.G)
        while K3 == None:  # plausible only for small (test) cardinality groups
            k3 = random.getrandbits(ec.nlen) % ec.n
            K3 = mult(ec, k3, ec.G)
        K3_x = K3[0]
        if legendre_symbol(K3[1], ec._p) != 1:
            k3 = ec.n - k3
            K3 = K3_x, ec.y_quadratic_residue(K3_x, True)
            #K3 = mult(ec, k3, ec.G)

        L.sort()  # using lexicographic ordering
        L_brackets = b''
        for i in range(len(L)):
            L_brackets += L[i]

        h1 = hf(L_brackets + octets_from_point(ec, Q1, False)).digest()
        a1 = int_from_bits(ec, h1)
        h2 = hf(L_brackets + octets_from_point(ec, Q2, False)).digest()
        a2 = int_from_bits(ec, h2)
        h3 = hf(L_brackets + octets_from_point(ec, Q3, False)).digest()
        a3 = int_from_bits(ec, h3)
        # aggregated public key
        Q_All = double_mult(ec, a1, Q1, a2, Q2)
        Q_All = ec.add(Q_All, mult(ec, a3, Q3))
        Q_All_bytes = octets_from_point(ec, Q_All, True)

        ########################
        # exchange K_x, compute s
        # WARNING: the signers should exchange commitments to the public
        #          nonces before sending the nonces themselves

        # first signer use K2_x and K3_x
        y = ec.y_quadratic_residue(K2_x, True)
        K2_recovered = (K2_x, y)
        y = ec.y_quadratic_residue(K3_x, True)
        K3_recovered = (K3_x, y)
        K1_All = ec.add(ec.add(K1, K2_recovered), K3_recovered)
        if legendre_symbol(K1_All[1], ec._p) != 1:
            # no need to actually change K1_All[1], as it is not used anymore
            # let's fix k1 instead, as it is used later
            k1 = ec.n - k1
        K1_All0_bytes = K1_All[0].to_bytes(32, byteorder="big")
        h1 = hf(K1_All0_bytes + Q_All_bytes + M).digest()
        c1 = int_from_bits(ec, h1)
        assert 0 < c1 and c1 < ec.n, "sign fail"
        s1 = (k1 + c1 * a1 * q1) % ec.n

        # second signer use K1_x and K3_x
        y = ec.y_quadratic_residue(K1_x, True)
        K1_recovered = (K1_x, y)
        y = ec.y_quadratic_residue(K3_x, True)
        K3_recovered = (K3_x, y)
        K2_All = ec.add(ec.add(K2, K1_recovered), K3_recovered)
        if legendre_symbol(K2_All[1], ec._p) != 1:
            # no need to actually change K2_All[1], as it is not used anymore
            # let's fix k2 instead, as it is used later
            k2 = ec.n - k2
        K2_All0_bytes = K2_All[0].to_bytes(32, byteorder="big")
        h2 = hf(K2_All0_bytes + Q_All_bytes + M).digest()
        c2 = int_from_bits(ec, h2)
        assert 0 < c2 and c2 < ec.n, "sign fail"
        s2 = (k2 + c2 * a2 * q2) % ec.n

        # third signer use K1_x and K2_x
        y = ec.y_quadratic_residue(K1_x, True)
        K1_recovered = (K1_x, y)
        y = ec.y_quadratic_residue(K2_x, True)
        K2_recovered = (K2_x, y)
        K3_All = ec.add(ec.add(K1_recovered, K2_recovered), K3)
        if legendre_symbol(K3_All[1], ec._p) != 1:
            # no need to actually change K3_All[1], as it is not used anymore
            # let's fix k3 instead, as it is used later
            k3 = ec.n - k3
        K3_All0_bytes = K3_All[0].to_bytes(32, byteorder="big")
        h3 = hf(K3_All0_bytes + Q_All_bytes + M).digest()
        c3 = int_from_bits(ec, h3)
        assert 0 < c3 and c3 < ec.n, "sign fail"
        s3 = (k3 + c3 * a3 * q3) % ec.n

        ############################################
        # combine signatures into a single signature

        # anyone can do the following
        assert K1_All[0] == K2_All[0], "sign fail"
        assert K2_All[0] == K3_All[0], "sign fail"
        s_All = (s1 + s2 + s3) % ec.n
        sig = (K1_All[0], s_All)

        self.assertTrue(ssa.verify(ec, hf, M, Q_All, sig))
Example #12
0
    def test_musig(self):
        """testing 3-of-3 MuSig

            https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/musig/musig.md
            https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/
            https://eprint.iacr.org/2018/068
            https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html
            https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744
        """
        mhd = hf(b'message to sign').digest()

        # the signers private and public keys,
        # including both the curve Point and the BIP340-Schnorr public key
        q1 = 0x010101
        Q1 = mult(q1)
        Q1_x = Q1[0].to_bytes(ec.psize, 'big')
        q2 = 0x020202
        Q2 = mult(q2)
        Q2_x = Q2[0].to_bytes(ec.psize, 'big')
        q3 = 0x030303
        Q3 = mult(q3)
        Q3_x = Q3[0].to_bytes(ec.psize, 'big')

        # ready to sign: nonces and nonce commitments
        k1 = 1 + secrets.randbelow(ec.n - 1)
        K1 = mult(k1)
        k2 = 1 + secrets.randbelow(ec.n - 1)
        K2 = mult(k2)
        k3 = 1 + secrets.randbelow(ec.n - 1)
        K3 = mult(k3)

        #### (non interactive) key setup
        # this is MuSig core: the rest is just Schnorr signature additivity
        # 1. lexicographic sorting of public keys
        keys: List[bytes] = list()
        keys.append(Q1_x)
        keys.append(Q2_x)
        keys.append(Q3_x)
        keys.sort()
        # 2. coefficients
        prefix = b''.join(keys)
        a1 = int_from_bits(hf(prefix + Q1_x).digest(), ec.nlen) % ec.n
        a2 = int_from_bits(hf(prefix + Q2_x).digest(), ec.nlen) % ec.n
        a3 = int_from_bits(hf(prefix + Q3_x).digest(), ec.nlen) % ec.n
        # 3. aggregated public key
        Q = ec.add(double_mult(a1, Q1, a2, Q2), mult(a3, Q3))

        #### exchange {K_i} (interactive)

        #### computes s_i (non interactive)
        # WARNING:
        # the signers must exchange the nonces
        # commitments {K_i} before sharing {s_i}

        # same for all signers
        K = ec.add(ec.add(K1, K2), K3)
        r = K[0]
        e = ssa._challenge(r, Q[0], mhd, ec, hf)

        # first signer
        if not ec.has_square_y(K):
            k1 = ec.n - k1
        s1 = (k1 + e * a1 * q1) % ec.n

        # second signer
        if not ec.has_square_y(K):
            k2 = ec.n - k2
        s2 = (k2 + e * a2 * q2) % ec.n

        # third signer
        if not ec.has_square_y(K):
            k3 = ec.n - k3
        s3 = (k3 + e * a3 * q3) % ec.n

        #### exchange s_i (interactive)

        #### finalize signature (non interactive)
        s = (s1 + s2 + s3) % ec.n
        sig = r, s
        # check signature is valid
        self.assertTrue(ssa.verify(mhd, Q, sig))
Example #13
0
    def test_threshold(self):
        """testing 2-of-3 threshold signature (Pedersen secret sharing)"""

        # parameters
        t = 2
        H = second_generator(ec, hf)
        mhd = hf(b'message to sign').digest()

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

        # signer one acting as the dealer
        commits1: List[Point] = list()
        q1 = (1 + random.getrandbits(ec.nlen)) % ec.n
        q1_prime = (1 + random.getrandbits(ec.nlen)) % ec.n
        commits1.append(double_mult(q1_prime, H, q1))

        # sharing polynomials
        f1: List[int] = list()
        f1.append(q1)
        f1_prime: List[int] = list()
        f1_prime.append(q1_prime)
        for i in range(1, t):
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f1.append(temp)
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f1_prime.append(temp)
            commits1.append(double_mult(f1_prime[i], H, f1[i]))

        # shares of the secret
        alpha12 = 0  # share of q1 belonging to P2
        alpha12_prime = 0
        alpha13 = 0  # share of q1 belonging to P3
        alpha13_prime = 0
        for i in range(t):
            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

        # player two verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(2, i), commits1[i]))
        assert double_mult(alpha12_prime, H,
                           alpha12) == RHS, 'player one is cheating'

        # player three verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(3, i), commits1[i]))
        assert double_mult(alpha13_prime, H,
                           alpha13) == RHS, 'player one is cheating'

        # signer two acting as the dealer
        commits2: List[Point] = list()
        q2 = (1 + random.getrandbits(ec.nlen)) % ec.n
        q2_prime = (1 + random.getrandbits(ec.nlen)) % ec.n
        commits2.append(double_mult(q2_prime, H, q2))

        # sharing polynomials
        f2: List[int] = list()
        f2.append(q2)
        f2_prime: List[int] = list()
        f2_prime.append(q2_prime)
        for i in range(1, t):
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f2.append(temp)
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f2_prime.append(temp)
            commits2.append(double_mult(f2_prime[i], H, f2[i]))

        # shares of the secret
        alpha21 = 0  # share of q2 belonging to P1
        alpha21_prime = 0
        alpha23 = 0  # share of q2 belonging to P3
        alpha23_prime = 0
        for i in range(t):
            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

        # player one verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(1, i), commits2[i]))
        assert double_mult(alpha21_prime, H,
                           alpha21) == RHS, 'player two is cheating'

        # player three verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(3, i), commits2[i]))
        assert double_mult(alpha23_prime, H,
                           alpha23) == RHS, 'player two is cheating'

        # signer three acting as the dealer
        commits3: List[Point] = list()
        q3 = (1 + random.getrandbits(ec.nlen)) % ec.n
        q3_prime = (1 + random.getrandbits(ec.nlen)) % ec.n
        commits3.append(double_mult(q3_prime, H, q3))

        # sharing polynomials
        f3: List[int] = list()
        f3.append(q3)
        f3_prime: List[int] = list()
        f3_prime.append(q3_prime)
        for i in range(1, t):
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f3.append(temp)
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f3_prime.append(temp)
            commits3.append(double_mult(f3_prime[i], H, f3[i]))

        # shares of the secret
        alpha31 = 0  # share of q3 belonging to P1
        alpha31_prime = 0
        alpha32 = 0  # share of q3 belonging to P2
        alpha32_prime = 0
        for i in range(t):
            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

        # player one verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(1, i), commits3[i]))
        assert double_mult(alpha31_prime, H,
                           alpha31) == RHS, 'player three is cheating'

        # player two verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(2, i), commits3[i]))
        assert double_mult(alpha32_prime, H,
                           alpha32) == RHS, 'player two 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(t):
            alpha1 += (f1[i] * pow(1, i)) % ec.n
            alpha2 += (f2[i] * pow(2, i)) % ec.n
            alpha3 += (f3[i] * pow(3, i)) % ec.n

        # it's time to recover the public key Q = Q1 + Q2 + Q3 = (q1 + q2 + q3)G
        A1: List[Point] = list()
        A2: List[Point] = list()
        A3: List[Point] = list()

        # each participant i = 1, 2, 3 shares Qi as follows

        # he broadcasts these values
        for i in range(t):
            A1.append(mult(f1[i]))
            A2.append(mult(f2[i]))
            A3.append(mult(f3[i]))

        # he checks the others' values
        # player one
        RHS2 = INF
        RHS3 = INF
        for i in range(t):
            RHS2 = ec.add(RHS2, mult(pow(1, i), A2[i]))
            RHS3 = ec.add(RHS3, mult(pow(1, i), A3[i]))
        assert mult(alpha21) == RHS2, 'player two is cheating'
        assert mult(alpha31) == RHS3, 'player three is cheating'

        # player two
        RHS1 = INF
        RHS3 = INF
        for i in range(t):
            RHS1 = ec.add(RHS1, mult(pow(2, i), A1[i]))
            RHS3 = ec.add(RHS3, mult(pow(2, i), A3[i]))
        assert mult(alpha12) == RHS1, 'player one is cheating'
        assert mult(alpha32) == RHS3, 'player three is cheating'

        # player three
        RHS1 = INF
        RHS2 = INF
        for i in range(t):
            RHS1 = ec.add(RHS1, mult(pow(3, i), A1[i]))
            RHS2 = ec.add(RHS2, mult(pow(3, i), A2[i]))
        assert mult(alpha13) == RHS1, 'player one is cheating'
        assert mult(alpha23) == RHS2, 'player two is cheating'

        A: List[Point] = list()  # commitment at the global sharing polynomial
        for i in range(t):
            A.append(ec.add(A1[i], ec.add(A2[i], A3[i])))

        Q = A[0]  # aggregated public key

        ### SECOND PHASE: generation of the nonces' pair ###
        # This phase follows exactly the key generation procedure
        # suppose that player one and three want to sign

        # signer one acting as the dealer
        commits1: List[Point] = list()
        k1 = (1 + random.getrandbits(ec.nlen)) % ec.n
        k1_prime = (1 + random.getrandbits(ec.nlen)) % ec.n
        commits1.append(double_mult(k1_prime, H, k1))

        # sharing polynomials
        f1: List[int] = list()
        f1.append(k1)
        f1_prime: List[int] = list()
        f1_prime.append(k1_prime)
        for i in range(1, t):
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f1.append(temp)
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f1_prime.append(temp)
            commits1.append(double_mult(f1_prime[i], H, f1[i]))

        # shares of the secret
        beta13 = 0  # share of k1 belonging to P3
        beta13_prime = 0
        for i in range(t):
            beta13 += (f1[i] * pow(3, i)) % ec.n
            beta13_prime += (f1_prime[i] * pow(3, i)) % ec.n

        # player three verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(3, i), commits1[i]))
        assert double_mult(beta13_prime, H,
                           beta13) == RHS, 'player one is cheating'

        # signer three acting as the dealer
        commits3: List[Point] = list()
        k3 = (1 + random.getrandbits(ec.nlen)) % ec.n
        k3_prime = (1 + random.getrandbits(ec.nlen)) % ec.n
        commits3.append(double_mult(k3_prime, H, k3))

        # sharing polynomials
        f3: List[int] = list()
        f3.append(k3)
        f3_prime: List[int] = list()
        f3_prime.append(k3_prime)
        for i in range(1, t):
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f3.append(temp)
            temp = (1 + random.getrandbits(ec.nlen)) % ec.n
            f3_prime.append(temp)
            commits3.append(double_mult(f3_prime[i], H, f3[i]))

        # shares of the secret
        beta31 = 0  # share of k3 belonging to P1
        beta31_prime = 0
        for i in range(t):
            beta31 += (f3[i] * pow(1, i)) % ec.n
            beta31_prime += (f3_prime[i] * pow(1, i)) % ec.n

        # player one verifies consistency of his share
        RHS = INF
        for i in range(t):
            RHS = ec.add(RHS, mult(pow(1, i), commits3[i]))
        assert double_mult(beta31_prime, H,
                           beta31) == RHS, 'player three is cheating'

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

        # it's time to recover the public nonce
        B1: List[Point] = list()
        B3: List[Point] = list()

        # each participant i = 1, 3 shares Qi as follows

        # he broadcasts these values
        for i in range(t):
            B1.append(mult(f1[i]))
            B3.append(mult(f3[i]))

        # he checks the others' values
        # player one
        RHS3 = INF
        for i in range(t):
            RHS3 = ec.add(RHS3, mult(pow(1, i), B3[i]))
        assert mult(beta31) == RHS3, 'player three is cheating'

        # player three
        RHS1 = INF
        for i in range(t):
            RHS1 = ec.add(RHS1, mult(pow(3, i), B1[i]))
        assert mult(beta13) == RHS1, 'player one is cheating'

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

        K = B[0]  # aggregated public nonce
        if not ec.has_square_y(K):
            beta1 = ec.n - beta1
            beta3 = ec.n - beta3

        ### PHASE THREE: signature generation ###

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

        # each participant verifies the other partial signatures

        # player one
        if ec.has_square_y(K):
            RHS3 = ec.add(K, mult(e, Q))
            for i in range(1, t):
                temp = double_mult(pow(3, i), B[i], e * pow(3, i), A[i])
                RHS3 = ec.add(RHS3, temp)
        else:
            RHS3 = ec.add(ec.opposite(K), mult(e, Q))
            for i in range(1, t):
                temp = double_mult(pow(3, i), ec.opposite(B[i]), e * pow(3, i),
                                   A[i])
                RHS3 = ec.add(RHS3, temp)

        assert mult(gamma3) == RHS3, 'player three is cheating'

        # player three
        if ec.has_square_y(K):
            RHS1 = ec.add(K, mult(e, Q))
            for i in range(1, t):
                temp = double_mult(pow(1, i), B[i], e * pow(1, i), A[i])
                RHS1 = ec.add(RHS1, temp)
        else:
            RHS1 = ec.add(ec.opposite(K), mult(e, Q))
            for i in range(1, t):
                temp = double_mult(pow(1, i), ec.opposite(B[i]), e * pow(1, i),
                                   A[i])
                RHS1 = ec.add(RHS1, temp)

        assert mult(gamma1) == RHS1, 'player two 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 = K[0], sigma

        self.assertTrue(ssa.verify(mhd, Q, sig))

        ### ADDITIONAL PHASE: reconstruction of the private key ###
        secret = (omega1 * alpha1 + omega3 * alpha3) % ec.n
        self.assertEqual((q1 + q2 + q3) % ec.n, secret)
Example #14
0
q = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725
q %= ec.n
print("1. Key generation")
print(f"prvkey: {hex(q).upper()}")

Q = mult(q, ec.G)[0]
print(f"PubKey: {hex(Q).upper()}")

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

print("3. Verify signature")
print(verify(msg, Q, (r, s)))

print("\n** Malleated signature")
sm = ec.n - s
print(f"    r: {hex(r).upper()}")
print(f"   sm: {hex(sm).upper()}")

print("** Verify malleated signature")
print(verify(msg, Q, (r, sm)))

print("\n0. Another message to sign")
orig_msg2 = "and Paolo is right to be afraid"
msg2 = sha256(orig_msg2.encode()).digest()
print(msg2.hex().upper())

print("2. Sign message")
Example #15
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"

    # 2.1 signer one acting as the dealer
    commits1 = []
    k1 = ssa.det_nonce(msg, q1, ec, hf)
    k1_prime = ssa.det_nonce(msg, q1_prime, 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, q3, ec, hf)
    k3_prime = ssa.det_nonce(msg, q3_prime, 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, 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 = K[0], sigma

    assert ssa.verify(msg, 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)
Example #16
0
    def test_musig(self):
        """testing 3-of-3 MuSig

            https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/musig/musig.md
            https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/
            https://eprint.iacr.org/2018/068
            https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html
            https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744
        """
        mhd = hf(b'message to sign').digest()

        # the signers private and public keys,
        # including both the curve Point and the BIP340-Schnorr public key
        q1, x_Q1 = ssa.gen_keys()
        x_Q1 = x_Q1.to_bytes(ec.psize, 'big')

        q2, x_Q2 = ssa.gen_keys()
        x_Q2 = x_Q2.to_bytes(ec.psize, 'big')

        q3, x_Q3 = ssa.gen_keys()
        x_Q3 = x_Q3.to_bytes(ec.psize, 'big')

        # (non interactive) key setup
        # this is MuSig core: the rest is just Schnorr signature additivity
        # 1. lexicographic sorting of public keys
        keys: List[bytes] = list()
        keys.append(x_Q1)
        keys.append(x_Q2)
        keys.append(x_Q3)
        keys.sort()
        # 2. coefficients
        prefix = b''.join(keys)
        a1 = int_from_bits(hf(prefix + x_Q1).digest(), ec.nlen) % ec.n
        a2 = int_from_bits(hf(prefix + x_Q2).digest(), ec.nlen) % ec.n
        a3 = int_from_bits(hf(prefix + x_Q3).digest(), ec.nlen) % ec.n
        # 3. aggregated public key
        Q1 = mult(q1)
        Q2 = mult(q2)
        Q3 = mult(q3)
        Q = ec.add(double_mult(a1, Q1, a2, Q2), mult(a3, Q3))
        if not ec.has_square_y(Q):
            # print("Q has been negated")
            a1 = ec.n - a1  # pragma: no cover
            a2 = ec.n - a2  # pragma: no cover
            a3 = ec.n - a3  # pragma: no cover

        # ready to sign: nonces and nonce commitments
        k1, _ = ssa.gen_keys()
        K1 = mult(k1)

        k2, _ = ssa.gen_keys()
        K2 = mult(k2)

        k3, _ = ssa.gen_keys()
        K3 = mult(k3)

        # exchange {K_i} (interactive)

        # computes s_i (non interactive)
        # WARNING: signers must exchange the nonces commitments {K_i}
        # before sharing {s_i}

        # same for all signers
        K = ec.add(ec.add(K1, K2), K3)
        if not ec.has_square_y(K):
            k1 = ec.n - k1  # pragma: no cover
            k2 = ec.n - k2  # pragma: no cover
            k3 = ec.n - k3  # pragma: no cover
        r = K[0]
        e = ssa._challenge(r, Q[0], mhd, ec, hf)
        s1 = (k1 + e * a1 * q1) % ec.n
        s2 = (k2 + e * a2 * q2) % ec.n
        s3 = (k3 + e * a3 * q3) % ec.n

        # exchange s_i (interactive)

        # finalize signature (non interactive)
        s = (s1 + s2 + s3) % ec.n
        sig = r, s
        # check signature is valid
        ssa._verify(mhd, Q, sig, ec, hf)
        self.assertTrue(ssa.verify(mhd, Q, sig))
Example #17
0
dsa_valid = dsa.verify(msg, dsa_pub, dsa_sig)
print("valid ECDSA sig:", dsa_valid)

# ECSSA
print("\n ECSSA")

ssa_prv, ssa_pub = ssa.gen_keys()
print("prv", hex(ssa_prv))
print("pub", hex(ssa_pub))

ssa_sig = ssa.sign(msg, ssa_prv)
print("r:", hex(ssa_sig[0]))
print("s:", hex(ssa_sig[1]))

ssa_valid = ssa.verify(msg, ssa_pub, ssa_sig)
print("valid ECSSA sig:", ssa_valid)

# ECBMS
print("\n ECBMS")

bms_prv, bms_pub = bms.gen_keys()
print("prv", bms_prv)
print("pub", bms_pub)

bms_sig = bms.sign(msg, bms_prv)
print("rf:", hex(bms_sig[0]))
print("r:", hex(bms_sig[1]))
print("s:", hex(bms_sig[2]))

bms_valid = bms.verify(msg, bms_pub, bms_sig)
Example #18
0
def test_signature():
    """Basic tests"""

    ec = secp256k1
    q, x_Q = ssa.gen_keys(0x1)
    mhd = hf(b"Satoshi Nakamoto").digest()
    sig = ssa.sign(mhd, q, None)
    ssa.assert_as_valid(mhd, x_Q, sig, ec, hf)
    assert ssa.verify(mhd, x_Q, sig)
    assert sig == ssa.deserialize(sig)

    fmhd = hf(b"Craig Wright").digest()
    assert not ssa.verify(fmhd, x_Q, sig, ec, hf)
    err_msg = "signature verification failed"
    with pytest.raises(AssertionError, match=err_msg):
        ssa.assert_as_valid(fmhd, x_Q, sig, ec, hf)

    _, x_fQ = ssa.gen_keys(0x2)
    assert not ssa.verify(mhd, x_fQ, sig, ec, hf)
    err_msg = "y_K is not a quadratic residue"
    with pytest.raises(RuntimeError, match=err_msg):
        ssa.assert_as_valid(mhd, x_fQ, sig, ec, hf)

    _, x_fQ = ssa.gen_keys(0x4)
    assert not ssa.verify(mhd, x_fQ, sig, ec, hf)
    err_msg = "signature verification failed"
    with pytest.raises(AssertionError, match=err_msg):
        ssa.assert_as_valid(mhd, x_fQ, sig, ec, hf)

    err_msg = "not a BIP340 public key"
    with pytest.raises(ValueError, match=err_msg):
        ssa.assert_as_valid(mhd, INF, sig, ec, hf)

    assert not ssa.verify(mhd, x_Q, sig, secp224k1, hf)
    err_msg = "field prime is not equal to 3 mod 4: "
    with pytest.raises(ValueError, match=err_msg):
        ssa.assert_as_valid(mhd, x_Q, sig, secp224k1, hf)

    wrongmhd = mhd[:-1]
    assert not ssa.verify(wrongmhd, x_Q, sig, ec, hf)
    err_msg = "invalid size: 31 bytes instead of 32"
    with pytest.raises(ValueError, match=err_msg):
        ssa.assert_as_valid(wrongmhd, x_Q, sig, ec, hf)

    fssasig = (sig[0], sig[1], sig[1])
    assert not ssa.verify(mhd, x_fQ, fssasig, ec, hf)
    err_msg = "too many values to unpack "
    with pytest.raises(ValueError, match=err_msg):
        ssa.assert_as_valid(mhd, x_Q, fssasig, ec, hf)

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

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

    err_msg = "invalid size: 31 bytes instead of 32"
    with pytest.raises(ValueError, match=err_msg):
        ssa.sign(wrongmhd, q, None)

    err_msg = "private key not in 1..n-1: "
    with pytest.raises(ValueError, match=err_msg):
        ssa.sign(mhd, 0)

    # ephemeral key not in 1..n-1
    err_msg = "private key not in 1..n-1: "
    with pytest.raises(ValueError, match=err_msg):
        ssa.sign(mhd, 1, 0)

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

    err_msg = "not a BIP340 public key"
    with pytest.raises(ValueError, match=err_msg):
        ssa._to_bip340_point(["not", "a BIP340", "public key"])