def test_forge_hash_sig(self):
        """forging valid hash signatures"""

        ec = secp256k1
        # see https://twitter.com/pwuille/status/1063582706288586752
        # Satoshi's key
        P = point_from_octets(
            secp256k1,
            "0311db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"
        )

        u1 = 1
        u2 = 2  # pick them at will
        R = double_mult(ec, u1, ec.G, u2, P)
        r = R[0] % ec.n
        u2inv = mod_inv(u2, ec.n)
        s = r * u2inv % ec.n
        sig = r, s
        e = s * u1 % ec.n
        dsa._verhlp(ec, e, P, sig)

        u1 = 1234567890
        u2 = 987654321  # pick them at will
        R = double_mult(ec, u1, ec.G, u2, P)
        r = R[0] % ec.n
        u2inv = mod_inv(u2, ec.n)
        s = r * u2inv % ec.n
        sig = r, s
        e = s * u1 % ec.n
        dsa._verhlp(ec, e, P, sig)
示例#2
0
def _sign(ec: Curve, e: int, d: int, k: int) -> Tuple[int, int]:
    """Private function provided for testing purposes only."""
    # e is assumed to be valid
    # Steps numbering follows SEC 1 v.2 section 4.1.3

    # The secret key d: an integer in the range 1..n-1.
    # SEC 1 v.2 section 3.2.1
    if not 0 < d < ec.n:
        raise ValueError(f"private key {hex(d)} not in (0, n)")

    # Fail if k' = 0.
    if not 0 < k < ec.n:
        raise ValueError(f"ephemeral key {hex(k)} not in (0, n)")
    # Let R = k'G.
    RJ = _mult_jac(ec, k, ec.GJ)                      # 1

    Rx = (RJ[0]*mod_inv(RJ[2]*RJ[2], ec._p)) % ec._p
    r = Rx % ec.n                                     # 2, 3
    if r == 0:  # r≠0 required as it multiplies the public key
        raise ValueError("r = 0, failed to sign")

    s = mod_inv(k, ec.n) * (e + r*d) % ec.n           # 6
    if s == 0:  # s≠0 required as verify will need the inverse of s
        raise ValueError("s = 0, failed to sign")

    # bitcoin canonical 'low-s' encoding for ECDSA signatures
    # it removes signature malleability as cause of transaction malleability
    # see https://github.com/bitcoin/bitcoin/pull/6769
    if s > ec.n / 2:
        s = ec.n - s

    return r, s
示例#3
0
def _verhlp(ec: Curve, c: int, P: Point, sig: ECDS) -> bool:
    # Private function for test/dev purposes

    # Fail if r is not [1, n-1]
    # Fail if s is not [1, n-1]
    r, s = _to_sig(ec, sig)  # 1

    # Let P = point(pk); fail if point(pk) fails.
    ec.require_on_curve(P)
    if P[1] == 0:
        raise ValueError("public key is infinite")

    w = mod_inv(s, ec.n)
    u = c * w
    v = r * w  # 4
    # Let R = u*G + v*P.
    RJ = _double_mult(ec, u, ec.GJ, v, (P[0], P[1], 1))  # 5

    # Fail if infinite(R).
    assert RJ[2] != 0, "how did you do that?!?"  # 5

    Rx = (RJ[0] * mod_inv(RJ[2] * RJ[2], ec._p)) % ec._p
    x = Rx % ec.n  # 6, 7
    # Fail if r ≠ x(R) %n.
    return r == x  # 8
示例#4
0
    def test_low_cardinality(self):
        """test low-cardinality curves for all msg/key pairs."""

        # ec.n has to be prime to sign
        prime = [11, 13, 17, 19]

        for ec in low_card_curves:  # only low card or it would take forever
            if ec._p in prime:  # only few curves or it would take too long
                for q in range(1, ec.n):  # all possible private keys
                    PJ = _mult_jac(q, ec.GJ, ec)  # public key
                    for e in range(ec.n):  # all possible int from hash
                        for k in range(1, ec.n):  # all possible ephemeral keys
                            RJ = _mult_jac(k, ec.GJ, ec)
                            Rx = (RJ[0] *
                                  mod_inv(RJ[2] * RJ[2], ec._p)) % ec._p
                            r = Rx % ec.n
                            s = mod_inv(k, ec.n) * (e + q * r) % ec.n
                            # bitcoin canonical 'low-s' encoding for ECDSA
                            if s > ec.n / 2:
                                s = ec.n - s
                            if r == 0 or s == 0:
                                self.assertRaises(ValueError, dsa._sign, e, q,
                                                  k, ec)
                                continue

                            sig = dsa._sign(e, q, k, ec)
                            self.assertEqual((r, s), sig)
                            # valid signature must pass verification
                            self.assertIsNone(dsa._verhlp(e, PJ, r, s, ec))

                            JacobianKeys = dsa._recover_pubkeys(e, r, s, ec)
                            Qs = [
                                ec._aff_from_jac(key) for key in JacobianKeys
                            ]
                            self.assertIn(ec._aff_from_jac(PJ), Qs)
示例#5
0
def test_forge_hash_sig() -> None:
    """forging valid hash signatures"""

    ec = CURVES["secp256k1"]

    # see https://twitter.com/pwuille/status/1063582706288586752
    # Satoshi's key
    key = "03 11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"
    Q = point_from_octets(key, ec)

    # pick u1 and u2 at will
    u1 = 1
    u2 = 2
    R = double_mult(u2, Q, u1, ec.G, ec)
    r = R[0] % ec.n
    u2inv = mod_inv(u2, ec.n)
    s = r * u2inv % ec.n
    e = s * u1 % ec.n
    dsa.__assert_as_valid(e, (Q[0], Q[1], 1), r, s, ec)

    # pick u1 and u2 at will
    u1 = 1234567890
    u2 = 987654321
    R = double_mult(u2, Q, u1, ec.G, ec)
    r = R[0] % ec.n
    u2inv = mod_inv(u2, ec.n)
    s = r * u2inv % ec.n
    e = s * u1 % ec.n
    dsa.__assert_as_valid(e, (Q[0], Q[1], 1), r, s, ec)
示例#6
0
 def test_mod_inv_prime(self):
     for p in primes:
         # zero has no inverse
         self.assertRaises(ValueError, mod_inv, 0, p)
         for a in range(1, min(p, 500)):  # exhausted only for small p
             inv = mod_inv(a, p)
             self.assertEqual(a * inv % p, 1)
             inv = mod_inv(a + p, p)
             self.assertEqual(a * inv % p, 1)
 def _affine_from_jac(self, Q: _JacPoint) -> Point:
     # point is assumed to be on curve
     if Q[2] == 0:  # Infinity point in Jacobian coordinates
         return 1, 0
     else:
         Z2 = Q[2] * Q[2]
         x = (Q[0] * mod_inv(Z2, self._p)) % self._p
         y = (Q[1] * mod_inv(Z2 * Q[2], self._p)) % self._p
         return x, y
示例#8
0
def test_mod_inv_prime() -> None:
    for p in primes:
        with pytest.raises(ValueError, match="No inverse for 0 mod"):
            mod_inv(0, p)
        for a in range(1, min(p, 500)):  # exhausted only for small p
            inv = mod_inv(a, p)
            assert a * inv % p == 1
            inv = mod_inv(a + p, p)
            assert a * inv % p == 1
示例#9
0
 def test_mod_inv(self):
     max_m = 100
     for m in range(2, max_m):
         nums = list(range(m))
         for a in nums:
             mult = [a * i % m for i in nums]
             if 1 in mult:
                 inv = mod_inv(a, m)
                 self.assertEqual(a * inv % m, 1)
                 inv = mod_inv(a + m, m)
                 self.assertEqual(a * inv % m, 1)
             else:
                 self.assertRaises(ValueError, mod_inv, a, m)
示例#10
0
def test_mod_inv() -> None:
    max_m = 100
    for m in range(2, max_m):
        nums = list(range(m))
        for a in nums:
            mult = [a * i % m for i in nums]
            if 1 in mult:
                inv = mod_inv(a, m)
                assert a * inv % m == 1
                inv = mod_inv(a + m, m)
                assert a * inv % m == 1
            else:
                err_msg = "No inverse for "
                with pytest.raises(ValueError, match=err_msg):
                    mod_inv(a, m)
def _ecdsa_pubkey_recovery(ec: EC, e: int, sig: ECDS) -> List[Point]:
    """Private function provided for testing purposes only."""
    # ECDSA public key recovery operation according to SEC 1
    # http://www.secg.org/sec1-v2.pdf
    # See SEC 1 v.2 section 4.1.6

    r, s = to_dsasig(ec, sig)

    # precomputations
    r1 = mod_inv(r, ec.n)
    r1s = r1 * s
    r1e = -r1 * e
    keys = []
    for j in range(ec.h):  # 1
        x = r + j * ec.n  # 1.1
        try:
            R = (x % ec._p, ec.yOdd(x, 1))  # 1.2, 1.3, and 1.4
            # 1.5 already taken care outside this for loop
            Q = DblScalarMult(ec, r1s, R, r1e, ec.G)  # 1.6.1
            if Q[1] != 0 and _ecdsa_verhlp(ec, e, Q, sig):  # 1.6.2
                keys.append(Q)
            R = ec.opposite(R)  # 1.6.3
            Q = DblScalarMult(ec, r1s, R, r1e, ec.G)
            if Q[1] != 0 and _ecdsa_verhlp(ec, e, Q, sig):  # 1.6.2
                keys.append(Q)  # 1.6.2
        except Exception:  # R is not a curve point
            pass
    return keys
示例#12
0
def _pubkey_recovery(ec: Curve, c: int, sig: ECDS) -> Sequence[Point]:
    """Private function provided for testing purposes only."""
    # ECDSA public key recovery operation according to SEC 1
    # http://www.secg.org/sec1-v2.pdf
    # See SEC 1 v.2 section 4.1.6

    r, s = _to_sig(ec, sig)

    # precomputations
    r1 = mod_inv(r, ec.n)
    r1s = r1 * s
    r1e = -r1 * c
    keys: Sequence[Point] = list()
    for j in range(ec.h):  # 1
        x = r + j * ec.n  # 1.1
        try:  #TODO: check test reporting 1, 2, 3, or 4 keys
            x %= ec._p
            R = x, ec.y_odd(x, 1)  # 1.2, 1.3, and 1.4
            # skip 1.5: in this function, c is an input
            Q = double_mult(ec, r1s, R, r1e, ec.G)  # 1.6.1
            if Q[1] != 0 and _verhlp(ec, c, Q, sig):  # 1.6.2
                keys.append(Q)
            R = ec.opposite(R)  # 1.6.3
            Q = double_mult(ec, r1s, R, r1e, ec.G)
            if Q[1] != 0 and _verhlp(ec, c, Q, sig):  # 1.6.2
                keys.append(Q)  # 1.6.2
        except Exception:  # R is not a curve point
            pass
    return keys
示例#13
0
    def pointDouble(self, P: Optional[Point]) -> Optional[Point]:
        if P is None or P[1] == 0: return None

        f = ((3 * P[0] * P[0] + self.__a) *
             mod_inv(2 * P[1], self.__prime)) % self.__prime
        x = (f * f - 2 * P[0]) % self.__prime
        y = (f * (P[0] - x) - P[1]) % self.__prime
        return x, y
示例#14
0
 def pointAdd(self, Q: Optional[Point],
              R: Optional[Point]) -> Optional[Point]:
     if R is None:
         return Q
     if Q is None:
         return R
     if R[0] == Q[0]:
         if R[1] != Q[1] or Q[1] == 0:  # opposite points
             return None
         else:  # point doubling
             lam = ((3 * Q[0] * Q[0] + self.__a) *
                    mod_inv(2 * Q[1], self.__p)) % self.__p
     else:
         lam = ((R[1] - Q[1]) * mod_inv(R[0] - Q[0], self.__p)) % self.__p
     x = (lam * lam - Q[0] - R[0]) % self.__p
     y = (lam * (Q[0] - x) - Q[1]) % self.__p
     return x, y
示例#15
0
    def _add_aff(self, Q: Point, R: Point) -> Point:
        # points are assumed to be on curve
        if R[1] == 0:  # Infinity point in affine coordinates
            return Q
        if Q[1] == 0:  # Infinity point in affine coordinates
            return R

        if R[0] == Q[0]:
            if R[1] == Q[1]:  # point doubling
                lam = (3 * Q[0] * Q[0] + self._a) * mod_inv(2 * Q[1], self._p)
                lam %= self._p
            else:  # opposite points
                return Point()
        else:
            lam = ((R[1] - Q[1]) * mod_inv(R[0] - Q[0], self._p)) % self._p
        x = (lam * lam - Q[0] - R[0]) % self._p
        y = (lam * (Q[0] - x) - Q[1]) % self._p
        return Point(x, y)
 def _addAffine(self, Q: Point, R: Point) -> Point:
     # points are assumed to be on curve
     if R[1] == 0:  # Infinity point in affine coordinates
         return Q
     if Q[1] == 0:  # Infinity point in affine coordinates
         return R
     if R[0] == Q[0]:
         if R[1] == Q[1]:  # point doubling
             lam = ((3 * Q[0] * Q[0] + self._a) *
                    mod_inv(2 * Q[1], self._p)) % self._p
         else:  # must be opposite (points already checked to be on curve)
             # elif R[1] == self._p - Q[1]: # opposite points
             return 1, 0
         # else:
         #    raise ValueError("points are not on the same curve")
     else:
         lam = ((R[1] - Q[1]) * mod_inv(R[0] - Q[0], self._p)) % self._p
     x = (lam * lam - Q[0] - R[0]) % self._p
     y = (lam * (Q[0] - x) - Q[1]) % self._p
     return x, y
示例#17
0
def sign(ec: Curve,
         hf: Callable[[Any], Any],
         mhd: bytes,
         d: int,
         k: Optional[int] = None) -> ECSS:
    """ ECSSA signing operation according to bip-schnorr

        This signature scheme supports 32-byte messages.
        Differently from ECDSA, the 32-byte message can be a
        digest of other messages, but it does not need to.

        https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki
    """

    # the bitcoin proposed standard is only valid for curves
    # whose prime p = 3 % 4
    if not ec.pIsThreeModFour:
        errmsg = 'curve prime p must be equal to 3 (mod 4)'
        raise ValueError(errmsg)

    # The message mhd: a 32-byte array
    _ensure_msg_size(hf, mhd)

    # The secret key d: an integer in the range 1..n-1.
    if not 0 < d < ec.n:
        raise ValueError(f"private key {hex(d)} not in [1, n-1]")
    P = mult(ec, d, ec.G)

    # Fail if k' = 0.
    if k is None:
        k = rfc6979(ec, hf, mhd, d)
    if not 0 < k < ec.n:
        raise ValueError(f"ephemeral key {hex(k)} not in [1, n-1]")

    # Let R = k'G.
    RJ = _mult_jac(ec, k, ec.GJ)

    # break the simmetry: any criteria might have been used,
    # jacobi is the proposed bitcoin standard
    # Let k = k' if jacobi(y(R)) = 1, otherwise let k = n - k'.
    if legendre_symbol(RJ[1] * RJ[2] % ec._p, ec._p) != 1:
        k = ec.n - k

    Z2 = RJ[2] * RJ[2]
    r = (RJ[0] * mod_inv(Z2, ec._p)) % ec._p

    # Let e = int(hf(bytes(x(R)) || bytes(dG) || mhd)) mod n.
    e = _e(ec, hf, r, P, mhd)

    s = (k +
         e * d) % ec.n  # s=0 is ok: in verification there is no inverse of s
    # The signature is bytes(x(R) || bytes((k + ed) mod n)).
    return r, s
示例#18
0
    def pointAdd(self, P: Optional[Point],
                 Q: Optional[Point]) -> Optional[Point]:
        if Q is None: return P
        if P is None: return Q

        if Q[0] == P[0]:
            if Q[1] == P[1]: return self.pointDouble(P)
            else: return None

        lam = (
            (Q[1] - P[1]) * mod_inv(Q[0] - P[0], self.__prime)) % self.__prime
        x = (lam * lam - P[0] - Q[0]) % self.__prime
        y = (lam * (P[0] - x) - P[1]) % self.__prime
        return x, y
示例#19
0
def _ecssa_pubkey_recovery(ec: EC, hf, e: int, sig: ECSS) -> Point:
    """Private function provided for testing purposes only."""

    r, s = to_ssasig(ec, sig)

    # could be obtained from to_ssasig...
    K = r, ec.yQuadraticResidue(r, True)

    if e == 0:
        raise ValueError("invalid (zero) challenge e")
    e1 = mod_inv(e, ec.n)
    P = DblScalarMult(ec, e1*s, ec.G, -e1, K)
    assert P[1] != 0, "how did you do that?!?"
    return P
def _pubkey_recovery(ec: Curve, hf, e: int, sig: ECSS) -> Point:
    # Private function provided for testing purposes only.

    r, s = _to_sig(ec, sig)

    K = r, ec.y_quadratic_residue(r, True)
    # FIXME y_quadratic_residue in Jacobian coordinates?

    if e == 0:
        raise ValueError("invalid (zero) challenge e")
    e1 = mod_inv(e, ec.n)
    P = double_mult(ec, e1 * s, ec.G, -e1, K)
    assert P[1] != 0, "how did you do that?!?"
    return P
示例#21
0
def _verhlp(ec: Curve, e: int, P: Point, sig: ECDS) -> bool:
    """Private function provided for testing purposes only."""
    # Fail if r is not [1, n-1]
    # Fail if s is not [1, n-1]
    r, s = _to_sig(ec, sig)                                # 1

    # Let P = point(pk); fail if point(pk) fails.
    ec.require_on_curve(P)
    if P[1] == 0:
        raise ValueError("public key is infinite")

    s1 = mod_inv(s, ec.n)
    u1 = e*s1
    u2 = r*s1                                              # 4
    # Let R = u*G + v*P.
    RJ = _double_mult(ec, u1, ec.GJ, u2, (P[0], P[1], 1))  # 5

    # Fail if infinite(R).
    assert RJ[2] != 0, "how did you do that?!?"            # 5

    Rx = (RJ[0]*mod_inv(RJ[2]*RJ[2], ec._p)) % ec._p
    v = Rx % ec.n                                          # 6, 7
    # Fail if r ≠ x(R) %n.
    return r == v                                          # 8
示例#22
0
def _sign(ec: Curve, c: int, q: int, k: int) -> ECDS:
    # Private function for test/dev purposes
    # it is assumed that q, k, and c are in [1, n-1]

    # Steps numbering follows SEC 1 v.2 section 4.1.3

    RJ = _mult_jac(ec, k, ec.GJ)  # 1

    Rx = (RJ[0] * mod_inv(RJ[2] * RJ[2], ec._p)) % ec._p
    r = Rx % ec.n  # 2, 3
    if r == 0:  # r≠0 required as it multiplies the public key
        raise ValueError("r = 0, failed to sign")

    s = mod_inv(k, ec.n) * (c + r * q) % ec.n  # 6
    if s == 0:  # s≠0 required as verify will need the inverse of s
        raise ValueError("s = 0, failed to sign")

    # bitcoin canonical 'low-s' encoding for ECDSA signatures
    # it removes signature malleability as cause of transaction malleability
    # see https://github.com/bitcoin/bitcoin/pull/6769
    if s > ec.n / 2:
        s = ec.n - s

    return r, s
    def test_low_cardinality(self):
        """test all msg/key pairs of low cardinality elliptic curves"""

        # ec.n has to be prime to sign
        prime = [11, 13, 17, 19]

        for ec in low_card_curves:  # only low card or it would take forever
            if ec._p in prime:  # only few curves or it would take too long
                for d in range(ec.n):  # all possible private keys
                    if d == 0:  # invalid prvkey = 0
                        self.assertRaises(ValueError, dsa._sign, ec, 1, d, 1)
                        continue
                    P = mult(ec, d, ec.G)  # public key
                    for e in range(ec.n):  # all possible int from hash
                        for k in range(ec.n):  # all possible ephemeral keys

                            if k == 0:
                                self.assertRaises(ValueError, dsa._sign, ec, e,
                                                  d, k)
                                continue
                            R = mult(ec, k, ec.G)

                            r = R[0] % ec.n
                            if r == 0:
                                self.assertRaises(ValueError, dsa._sign, ec, e,
                                                  d, k)
                                continue

                            s = mod_inv(k, ec.n) * (e + d * r) % ec.n
                            if s == 0:
                                self.assertRaises(ValueError, dsa._sign, ec, e,
                                                  d, k)
                                continue

                            # bitcoin canonical 'low-s' encoding for ECDSA
                            if s > ec.n / 2:
                                s = ec.n - s

                            # valid signature
                            sig = dsa._sign(ec, e, d, k)
                            self.assertEqual((r, s), sig)
                            # valid signature must validate
                            self.assertTrue(dsa._verhlp(ec, e, P, sig))

                            keys = dsa._pubkey_recovery(ec, e, sig)
                            self.assertIn(P, keys)
                            for Q in keys:
                                self.assertTrue(dsa._verhlp(ec, e, Q, sig))
示例#24
0
def test_low_cardinality() -> None:
    """test low-cardinality curves for all msg/key pairs."""

    # ec.n has to be prime to sign
    test_curves = [
        low_card_curves["ec13_11"],
        # low_card_curves["ec13_19"],
        # low_card_curves["ec17_13"],
        low_card_curves["ec17_23"],
        low_card_curves["ec19_13"],
        # low_card_curves["ec19_23"],
        low_card_curves["ec23_19"],
        low_card_curves["ec23_31"],
    ]

    low_s = True
    # only low cardinality test curves or it would take forever
    for ec in test_curves:
        for q in range(1, ec.n):  # all possible private keys
            QJ = _mult(q, ec.GJ, ec)  # public key
            for k in range(1, ec.n):  # all possible ephemeral keys
                RJ = _mult(k, ec.GJ, ec)
                r = ec._x_aff_from_jac(RJ) % ec.n
                k_inv = mod_inv(k, ec.n)
                for e in range(ec.n):  # all possible challenges
                    s = k_inv * (e + q * r) % ec.n
                    # bitcoin canonical 'low-s' encoding for ECDSA
                    if low_s and s > ec.n / 2:
                        s = ec.n - s
                    if r == 0 or s == 0:
                        err_msg = "failed to sign: "
                        with pytest.raises(BTClibRuntimeError, match=err_msg):
                            dsa.__sign(e, q, k, low_s, ec)
                        continue

                    sig = dsa.__sign(e, q, k, low_s, ec)
                    assert (r, s) == sig
                    # valid signature must pass verification
                    dsa.__assert_as_valid(e, QJ, r, s, ec)

                    jacobian_keys = dsa.__recover_pubkeys(e, r, s, ec)
                    # FIXME speed this up
                    Qs = [ec._aff_from_jac(key) for key in jacobian_keys]
                    assert ec._aff_from_jac(QJ) in Qs
                    assert len(jacobian_keys) in (2, 4)
def _ecdsa_verhlp(ec: EC, e: int, P: Point, sig: ECDS) -> bool:
    """Private function provided for testing purposes only."""
    # Fail if r is not [1, n-1]
    # Fail if s is not [1, n-1]
    r, s = to_dsasig(ec, sig)  # 1

    # Let P = point(pk); fail if point(pk) fails.
    ec.requireOnCurve(P)
    if P[1] == 0:
        raise ValueError("public key is infinite")

    s1 = mod_inv(s, ec.n)
    u = e * s1
    v = r * s1  # 4
    # Let R = u*G + v*P.
    R = DblScalarMult(ec, u, ec.G, v, P)  # 5

    # Fail if infinite(R).
    assert R[1] != 0, "how did you do that?!?"  # 5

    v = R[0] % ec.n  # 6, 7
    # Fail if r ≠ x(R) %n.
    return r == v  # 8
def _ecdsa_sign(ec: EC, e: int, d: int, k: int) -> Tuple[int, int]:
    """Private function provided for testing purposes only."""
    # e is assumed to be valid
    # Steps numbering follows SEC 1 v.2 section 4.1.3

    # The secret key d: an integer in the range 1..n-1.
    if not 0 < d < ec.n:
        raise ValueError(f"private key {hex(d)} not in (0, n)")

    # Fail if k' = 0.
    if not 0 < k < ec.n:
        raise ValueError(f"ephemeral key {hex(k)} not in (0, n)")
    # Let R = k'G.
    R = pointMult(ec, k, ec.G)  # 1

    r = R[0] % ec.n  # 2, 3
    if r == 0:  # r≠0 required as it multiplies the public key
        raise ValueError("r = 0, failed to sign")

    s = mod_inv(k, ec.n) * (e + r * d) % ec.n  # 6
    if s == 0:  # required as the inverse of s is needed
        raise ValueError("s = 0, failed to sign")

    return r, s
# ephemeral key k must be kept secret and never reused !!!!!
# good choice: k = sha256(msg|q)
# different for each msg, private because of q
temp = msg1 + hex(q)
k_bytes = sha256(temp.encode()).digest()
k1 = int.from_bytes(k_bytes, 'big') % ec.n
assert k1 != 0
print("eph k1:", hex(k1))

K1 = ec.pointMultiply(k1, ec.G)

r = K1[0] % ec.n
# if r == 0 (extremely unlikely for large ec.n) go back to a different ephemeral key
assert r != 0

s1 = ((h1 + r * q) * mod_inv(k1, ec.n)) % ec.n
# if s1 == 0 (extremely unlikely for large ec.n) go back to a different ephemeral key
assert s1 != 0

print("     r:", hex(r))
print("    s1:", hex(s1))

print("*** Signature Verification")
w = mod_inv(s1, ec.n)
u = (h1 * w) % ec.n
v = (r * w) % ec.n
assert u != 0
assert v != 0
U = ec.pointMultiply(u, ec.G)
V = ec.pointMultiply(v, Q)
x, y = ec.pointAdd(U, V)
示例#28
0
# ephemeral key k must be kept secret and never reused !!!!!
# good choice: k = hf(q||c)
# different for each msg, private because of q
temp = q.to_bytes(32, 'big') + c.to_bytes(32, 'big')
k_bytes = hf(temp).digest()
k = int.from_bytes(k_bytes, 'big') % ec.n
assert 0 < k < ec.n, "Invalid ephemeral key"
print("eph k:", hex(k))

K = mult(ec, k, ec.G)

r = K[0] % ec.n
# if r == 0 (extremely unlikely for large ec.n) go back to a different k
assert r != 0

s = (c + r * q) * mod_inv(k, ec.n) % ec.n
# if s == 0 (extremely unlikely for large ec.n) go back to a different k
assert s != 0

print("    r:", hex(r))
print("    s:", hex(s))

print("*** Verify Signature")
w = mod_inv(s, ec.n)
u = (c * w) % ec.n
v = (r * w) % ec.n
assert u != 0
assert v != 0
U = mult(ec, u, ec.G)
V = mult(ec, v, Q)
x, y = ec.add(U, V)
示例#29
0
    def test_threshold(self):
        """testing 2-of-3 threshold signature (Pedersen secret sharing)"""

        ec = secp256k1
        hf = sha256
        # parameters
        t = 2
        H = second_generator(ec, hf)
        msg = 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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        RHS3 = 1, 0
        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 = 1, 0
        RHS3 = 1, 0
        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 = 1, 0
        RHS2 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 = 1, 0
        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 legendre_symbol(K[1], ec._p) != 1:
            beta1 = ec.n - beta1
            beta3 = ec.n - beta3

        ### PHASE THREE: signature generation ###

        # partial signatures
        ebytes = K[0].to_bytes(32, byteorder='big')
        ebytes += octets_from_point(Q, True, ec)
        ebytes += msg
        e = int_from_bits(hf(ebytes).digest(), ec)
        gamma1 = (beta1 + e * alpha1) % ec.n
        gamma3 = (beta3 + e * alpha3) % ec.n

        # each participant verifies the other partial signatures

        # player one
        if legendre_symbol(K[1], ec._p) == 1:
            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:
            assert legendre_symbol(K[1], ec._p) != 1
            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 legendre_symbol(K[1], ec._p) == 1:
            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:
            assert legendre_symbol(K[1], ec._p) != 1
            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(msg, Q, sig))

        ### ADDITIONAL PHASE: reconstruction of the private key ###
        secret = (omega1 * alpha1 + omega3 * alpha3) % ec.n
        self.assertEqual((q1 + q2 + q3) % ec.n, secret)
示例#30
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)