Exemplo n.º 1
0
def assert_as_valid(msg: Octets, e0: bytes, s: SValues, pubk_rings: PubkeyRing) -> bool:

    msg = bytes_from_octets(msg)
    m = _get_msg_format(msg, pubk_rings)

    ring_size = len(pubk_rings)
    e: SValues = defaultdict(list)
    e0bytes = m
    for i in range(ring_size):
        keys_size = len(pubk_rings[i])
        e[i] = [0] * keys_size
        e[i][0] = int_from_bits(_hash(m, e0, i, 0), ec.nlen) % ec.n
        # edge case that cannot be reproduced in the test suite
        if e[i][0] == 0:
            err_msg = "implausibile signature failure"  # pragma: no cover
            raise BTClibRuntimeError(err_msg)  # pragma: no cover
        r = b"\0x00"
        for j in range(keys_size):
            t = double_mult(-e[i][j], pubk_rings[i][j], s[i][j], ec.G)
            r = bytes_from_point(t, ec)
            if j != len(pubk_rings[i]) - 1:
                h = _hash(m, r, i, j + 1)
                e[i][j + 1] = int_from_bits(h, ec.nlen) % ec.n
                # edge case that cannot be reproduced in the test suite
                if e[i][j + 1] == 0:
                    err_msg = "implausibile signature failure"  # pragma: no cover
                    raise BTClibRuntimeError(err_msg)  # pragma: no cover
            else:
                e0bytes += r
    e0_prime = hf(e0bytes).digest()
    return e0_prime == e0
def _verify(msg: bytes,
                      e0: bytes,
                      s: Dict[int, List[int]],
                      pubk_rings: Dict[int, List[Point]]) -> bool:

    ring_size = len(pubk_rings)
    m = _get_msg_format(msg, pubk_rings)
    e: Dict[int, List[int]] = defaultdict(list)
    e0bytes = m
    for i in range(ring_size):
        keys_size = len(pubk_rings[i])
        e[i] = [0]*keys_size
        e[i][0] = int_from_bits(ec, _hash(m, e0, i, 0))
        assert e[i][0] != 0, "invalid sig: how did you do that?!?"
        R = b'\0x00'
        for j in range(keys_size):
            T = double_mult(ec, s[i][j], ec.G, -e[i][j], pubk_rings[i][j])
            R = octets_from_point(ec, T, True)
            if j != len(pubk_rings[i])-1:
                e[i][j+1] = int_from_bits(ec, _hash(m, R, i, j+1))
                assert e[i][j+1] != 0, "invalid sig: how did you do that?!?"
            else:
                e0bytes += R
    e0_prime = hf(e0bytes).digest()
    return e0_prime == e0
def sign(msg: bytes,
                   k: List[int],
                   sign_key_idx: List[int],
                   sign_keys: List[int],
                   pubk_rings: Dict[int, List[Point]]) -> Tuple[bytes, Dict[int, List[int]]]:
    """ Borromean ring signature - signing algorithm

        https://github.com/ElementsProject/borromean-signatures-writeup
        https://github.com/Blockstream/borromean_paper/blob/master/borromean_draft_0.01_9ade1e49.pdf

        inputs:
        - msg: msg to be signed (bytes)
        - sign_key_idx: list of indexes representing each signing key per ring
        - sign_keys: list containing the whole set of signing keys (one per ring)
        - pubk_rings: dictionary of lists where internal lists represent single rings of pubkeys
    """

    s: Dict[int, List[int]] = defaultdict(list)
    e: Dict[int, List[int]] = defaultdict(list)
    m = _get_msg_format(msg, pubk_rings)
    e0bytes = m
    ring_size = len(pubk_rings)
    # step 1
    for i in range(ring_size):
        keys_size = len(pubk_rings[i])
        s[i] = [0]*keys_size
        e[i] = [0]*keys_size
        j_star = sign_key_idx[i]
        start_idx = (j_star + 1) % keys_size
        R = octets_from_point(ec, mult(ec, k[i], ec.G), True)
        if start_idx != 0:
            for j in range(start_idx, keys_size):
                s[i][j] = random.getrandbits(256)
                e[i][j] = int_from_bits(ec, _hash(m, R, i, j))
                assert 0 < e[i][j] < ec.n, "sign fail: how did you do that?!?"
                T = double_mult(ec, s[i][j], ec.G, -e[i][j], pubk_rings[i][j])
                R = octets_from_point(ec, T, True)
        e0bytes += R
    e0 = hf(e0bytes).digest()
    # step 2
    for i in range(ring_size):
        e[i][0] = int_from_bits(ec, _hash(m, e0, i, 0))
        assert 0 < e[i][0] < ec.n, "sign fail: how did you do that?!?"
        j_star = sign_key_idx[i]
        for j in range(1, j_star+1):
            s[i][j-1] = random.getrandbits(256)
            T = double_mult(ec, s[i][j-1], ec.G, -e[i][j-1], pubk_rings[i][j-1])
            R = octets_from_point(ec, T, True)
            e[i][j] = int_from_bits(ec, _hash(m, R, i, j))
            assert 0 < e[i][j] < ec.n, "sign fail: how did you do that?!?"
        s[i][j_star] = k[i] + sign_keys[i]*e[i][j_star]
    return e0, s
Exemplo n.º 4
0
def sign(ec: Curve, hf, msg: bytes, d: int,
                                 k: Optional[int] = None) -> Tuple[int, int]:
    """ECDSA signing operation according to SEC 1

       http://www.secg.org/sec1-v2.pdf
       Steps numbering follows SEC 1 v.2 section 4.1.3
    """

    # https://tools.ietf.org/html/rfc6979#section-3.2
    # The message msg is first processed by hf, yielding the value mhd=hf(msg),
    # a sequence of bits of length hlen.  Normally, hf is chosen such that
    # its output length hlen is roughly equal to nlen, since the overall
    # security of the signature scheme will depend on the smallest of hlen
    # and nlen; however, the (Curve)DSA standard support all combinations of
    # hlen and nlen.
    mhd = hf(msg).digest()                             # 4
    # H(m) is transformed into an integer modulo ec.n using int_from_bits:
    e = int_from_bits(ec, mhd)                         # 5

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

    # second part delegated to helper function used in testing
    return _sign(ec, e, d, k)
Exemplo n.º 5
0
def second_generator(ec: Curve = secp256k1, hf: HashF = sha256) -> Point:
    """Second (with respect to G) elliptic curve generator.

    Second (with respect to G) Nothing-Up-My-Sleeve (NUMS)
    elliptic curve generator.

    The hash of G is coerced it to a point (x_H, y_H).
    If the resulting point is not on the curve, keep on
    incrementing x_H until a valid curve point (x_H, y_H) is obtained.

    idea:
    https://crypto.stackexchange.com/questions/25581/second-generator-for-secp256k1-curve

    source:
    https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/rangeproof/main_impl.h
    """

    G_bytes = bytes_from_point(ec.G, ec, compressed=False)
    hash_ = hf()
    hash_.update(G_bytes)
    hash_digest = hash_.digest()
    x_H = int_from_bits(hash_digest, ec.nlen) % ec.n
    while True:
        try:
            y_H = ec.y_even(x_H)
            return x_H, y_H
        except BTClibValueError:
            x_H += 1
            x_H %= ec.p
def _e(ec: Curve, hf, r: int, P: Point, mhd: bytes) -> int:
    # Let e = int(hf(bytes(x(R)) || bytes(dG) || mhd)) mod n.
    ebytes = octets_from_int(r, ec.psize)  # FIXME: hsize, nsize ?
    ebytes += octets_from_point(ec, P, True)
    ebytes += mhd
    ebytes = hf(ebytes).digest()
    e = int_from_bits(ec, ebytes)
    return e
Exemplo n.º 7
0
def _e(ec: Curve, hf: Callable[[Any], Any], r: int, P: Point,
       mhd: bytes) -> int:
    # Let e = int(hf(bytes(x(R)) || bytes(dG) || mhd)) mod n.
    h = hf()
    h.update(octets_from_int(r, ec.psize))
    h.update(octets_from_point(ec, P, True))
    h.update(mhd)
    e = int_from_bits(ec, h.digest())
    return e
Exemplo n.º 8
0
def challenge_(msg_hash: Octets,
               ec: Curve = secp256k1,
               hf: HashF = hashlib.sha256) -> int:

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

    # leftmost ec.nlen bits %= ec.n
    return int_from_bits(msg_hash, ec.nlen) % ec.n
Exemplo n.º 9
0
def _verify(ec: Curve, hf: Callable[[Any], Any], msg: bytes, P: Point,
            sig: ECDS) -> bool:
    # Private function for test/dev purposes
    # It raises Errors, while verify should always return True or False

    # The message digest m: a 32-byte array
    msghd = hf(msg).digest()  # 2
    c = int_from_bits(ec, msghd)  # 3

    # second part delegated to helper function
    return _verhlp(ec, c, P, sig)
Exemplo n.º 10
0
def pubkey_recovery(ec: Curve, hf, msg: bytes, sig: ECDS) -> List[Point]:
    """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
    """

    # The message digest m: a 32-byte array
    mhd = hf(msg).digest()                                  # 1.5
    e = int_from_bits(ec, mhd)                              # 1.5

    return _pubkey_recovery(ec, e, sig)
Exemplo n.º 11
0
def pubkey_recovery(ec: Curve, hf: Callable[[Any], Any], msg: bytes,
                    sig: ECDS) -> Sequence[Point]:
    """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
    """

    # The message digest m: a 32-byte array
    msghd = hf(msg).digest()  # 1.5
    c = int_from_bits(ec, msghd)  # 1.5

    return _pubkey_recovery(ec, c, sig)
Exemplo n.º 12
0
def rfc6979(ec: Curve, hf: Callable[[Any], Any], mhd: bytes, x: int) -> int:
    """Return a deterministic ephemeral key following rfc6979"""

    if not 0 < x < ec.n:
        raise ValueError(f"private key {hex(x)} not in [1, n-1]")

    hsize = hf().digest_size
    if len(mhd) != hsize:
        errMsg = f"mismatch between hf digest size ({hsize}) and "
        errMsg += f"hashed message size ({len(mhd)})"
        raise ValueError(errMsg)

    h_int = int_from_bits(ec, mhd)  # leftmost ec.nlen bits %= ec.n
    return _rfc6979(ec, hf, h_int, x)
Exemplo n.º 13
0
def verify_commit(c: bytes, ec: Curve, hf, receipt: Receipt) -> bool:
    w, R = receipt
    # w in [1..n-1] dsa
    # w in [1..p-1] ssa
    # different verify functions?

    # verify R is a good point?

    ch = hf(c).digest()
    e = hf(octets_from_point(ec, R, True) + ch).digest()
    e = int_from_bits(ec, e)
    W = ec.add(R, mult(ec, e, ec.G))
    # different verify functions?
    # return w == W[0] # ECSS
    return w == W[0] % ec.n  # ECDS, FIXME: ECSSA
Exemplo n.º 14
0
def _verify(ec: Curve, hf, msg: bytes, P: Point, sig: ECDS) -> bool:
    """Private function provided for testing purposes only.
    
       It raises Errors, while verify should always return True or False

       See SEC 1 v.2 section 4.1.4
       http://www.secg.org/sec1-v2.pdf
    """

    # The message digest m: a 32-byte array
    mhd = hf(msg).digest()                                 # 2
    e = int_from_bits(ec, mhd)                             # 3

    # Let P = point(pk); fail if point(pk) fails.
    # P on point will be checked below by double_mult

    # second part delegated to helper function used in testing
    return _verhlp(ec, e, P, sig)
Exemplo n.º 15
0
def challenge_(msg_hash: Octets, x_Q: int, x_K: int, ec: Curve,
               hf: HashF) -> int:

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

    t = b"".join([
        x_K.to_bytes(ec.p_size, byteorder="big", signed=False),
        x_Q.to_bytes(ec.p_size, byteorder="big", signed=False),
        msg_hash,
    ])
    t = tagged_hash("BIP0340/challenge".encode(), t, hf)

    c = int_from_bits(t, ec.nlen) % ec.n
    if c == 0:
        raise BTClibRuntimeError("invalid zero challenge")  # pragma: no cover
    return c
Exemplo n.º 16
0
def _det_nonce_(
    msg_hash: bytes, q: int, Q: int, aux: bytes, ec: Curve, hf: HashF
) -> int:

    # assume the random oracle model for the hash function,
    # i.e. hash values can be considered uniformly random

    # Note that in general, taking a uniformly random integer
    # modulo the curve order n would produce a biased result.
    # However, if the order n is sufficiently close to 2^hf_len,
    # then the bias is not observable:
    # e.g. for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
    #
    # the unbiased implementation is provided here,
    # which works also for very-low-cardinality test curves

    randomizer = tagged_hash("BIP0340/aux".encode(), aux, hf)
    xor = q ^ int.from_bytes(randomizer, "big", signed=False)
    max_len = max(ec.n_size, hf().digest_size)
    t = b"".join(
        [
            xor.to_bytes(max_len, byteorder="big", signed=False),
            Q.to_bytes(ec.p_size, byteorder="big", signed=False),
            msg_hash,
        ]
    )

    nonce_tag = "BIP0340/nonce".encode()
    while True:
        t = tagged_hash(nonce_tag, t, hf)
        # The following lines would introduce a bias
        # nonce = int.from_bytes(t, 'big') % ec.n
        # nonce = int_from_bits(t, ec.nlen) % ec.n
        # In general, taking a uniformly random integer (like those
        # obtained from a hash function in the random oracle model)
        # modulo the curve order n would produce a biased result.
        # However, if the order n is sufficiently close to 2^hf_len,
        # then the bias is not observable: e.g.
        # for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
        nonce = int_from_bits(t, ec.nlen)  # candidate nonce
        if 0 < nonce < ec.n:  # acceptable value for nonce
            return nonce  # successful candidate
Exemplo n.º 17
0
def _tweak(commit_hash: Octets, R: Point, ec: Curve, hf: HashF) -> int:
    "Return the hash(R||commit_hash) tweak for the provided R."

    t = bytes_from_point(R, ec) + bytes_from_octets(commit_hash)
    while True:
        h = hf()
        h.update(t)
        t = h.digest()
        # The following lines would introduce a bias
        # nonce = int.from_bytes(t, 'big') % ec.n
        # nonce = int_from_bits(t, ec.nlen) % ec.n
        # In general, taking a uniformly random integer (like those
        # obtained from a hash function in the random oracle model)
        # modulo the curve order n would produce a biased result.
        # However, if the order n is sufficiently close to 2^hf_len,
        # then the bias is not observable: e.g.
        # for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
        tweak = int_from_bits(t, ec.nlen)  # candidate tweak
        if 0 < tweak < ec.n:  # acceptable value for tweak
            return tweak  # successful candidate
Exemplo n.º 18
0
def second_generator(ec: Curve, hf) -> Point:
    """Nothing-Up-My-Sleeve (NUMS) second generator H wrt ec.G 

       source: https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/rangeproof/main_impl.h
       idea: https://crypto.stackexchange.com/questions/25581/second-generator-for-secp256k1-curve
       Get the hash of G, then coerce it to a point (hx, hy).
       The resulting point could not be a curvepoint: in this case keep on
       incrementing hx until a valid curve point (hx, hy) is obtained.
    """
    G_bytes = octets_from_point(ec, ec.G, False)
    hd = hf(G_bytes).digest()
    hx = int_from_bits(ec, hd)
    isCurvePoint = False
    while not isCurvePoint:
        try:
            hy = ec.y_odd(hx, False)
            isCurvePoint = True
        except:
            hx += 1
    return Point(hx, hy)
Exemplo n.º 19
0
def _rfc6979_(c: int, q: int, ec: Curve, hf: HashF) -> int:
    # https://tools.ietf.org/html/rfc6979 section 3.2

    # convert the private key q to an octet sequence of size n_size
    q_bytes = q.to_bytes(ec.n_size, byteorder="big", signed=False)
    # truncate and/or expand c: encoding size is driven by n_size
    c_bytes = c.to_bytes(ec.n_size, byteorder="big", signed=False)
    bprvbm = q_bytes + c_bytes

    hf_size = hf().digest_size
    v = b"\x01" * hf_size  # 3.2.b
    k = b"\x00" * hf_size  # 3.2.c

    k = hmac.new(k, v + b"\x00" + bprvbm, hf).digest()  # 3.2.d
    v = hmac.new(k, v, hf).digest()  # 3.2.e
    k = hmac.new(k, v + b"\x01" + bprvbm, hf).digest()  # 3.2.f
    v = hmac.new(k, v, hf).digest()  # 3.2.g

    while True:  # 3.2.h
        t = b""  # 3.2.h.1
        while len(t) < ec.n_size:  # 3.2.h.2
            v = hmac.new(k, v, hf).digest()
            t += v
        # The following line would introduce a bias
        # det_nonce = int.from_bytes(t, 'big') % ec.n
        # det_nonce = int_from_bits(t, ec.nlen) % ec.n
        # In general, taking a uniformly random integer (like those
        # obtained from a hash function in the random oracle model)
        # modulo the curve order n would produce a biased result.
        # However, if the order n is sufficiently close to 2^hf_len,
        # then the bias is not observable: e.g.
        # for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
        det_nonce = int_from_bits(
            t, ec.nlen)  # candidate det_nonce           # 3.2.h.3
        if 0 < det_nonce < ec.n:  # acceptable values for det_nonce
            return det_nonce  # successful candidate
        k = hmac.new(k, v + b"\x00", hf).digest()
        v = hmac.new(k, v, hf).digest()
Exemplo n.º 20
0
def sign(ec: Curve,
         hf: Callable[[Any], Any],
         msg: bytes,
         q: int,
         k: Optional[int] = None) -> ECDS:
    """ECDSA signing operation according to SEC 1

       http://www.secg.org/sec1-v2.pdf
    """

    # https://tools.ietf.org/html/rfc6979#section-3.2
    # The message msg is first processed by hf, yielding the value
    # msghd = hf(msg), a sequence of bits of length hlen.
    # Normally, hf is chosen such that
    # its output length hlen is roughly equal to nlen, since the overall
    # security of the signature scheme will depend on the smallest of hlen
    # and nlen; however, the ECDSA standard support all combinations of
    # hlen and nlen.

    # Steps numbering follows SEC 1 v.2 section 4.1.3

    msghd = hf(msg).digest()  # 4
    # H(msg) is transformed into an integer modulo ec.n using int_from_bits:
    c = int_from_bits(ec, msghd)  # 5

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

    if k is None:
        k = _rfc6979(ec, hf, c, q)  # 1
    if not 0 < k < ec.n:
        raise ValueError(f"ephemeral key {hex(k)} not in [1, n-1]")

    # second part delegated to helper function
    return _sign(ec, c, q, k)
Exemplo n.º 21
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))
Exemplo n.º 22
0
#
# This file is part of btclib. It is subject to the license terms in the
# LICENSE file found in the top-level directory of this distribution.
#
# No part of btclib including this file, may be copied, modified, propagated,
# or distributed except according to the terms contained in the LICENSE file.
""" Deterministic Wallet (Type-1)
"""

import random
from hashlib import sha256 as hf

from btclib.curve import mult
from btclib.curves import secp256k1 as ec
from btclib.utils import int_from_bits

# master prvkey
mprvkey = random.getrandbits(ec.nlen) % ec.n
print('\nmaster private key =', hex(mprvkey))

mprvkey_bytes = mprvkey.to_bytes(ec.nlen, 'big')
nKeys = 3
for i in range(nKeys):
    ibytes = i.to_bytes(ec.nlen, 'big')
    hd = hf(ibytes + mprvkey_bytes).digest()
    q = int_from_bits(ec, hd)
    Q = mult(ec, q, ec.G)
    print('\nprvkey#', i, ':', hex(q))
    print('Pubkey#', i, ':', hex(Q[0]))
    print('           ', hex(Q[1]))
Exemplo n.º 23
0
mprvkey = random.getrandbits(ec.nlen) % ec.n
print('\nmaster private key:', hex(mprvkey))

# Master Pubkey:
mpubkey = mult(mprvkey, ec.G)
print('Master Public Key:', hex(mpubkey[0]))
print('                  ', hex(mpubkey[1]))

# public random number
r = random.getrandbits(ec.nlen)
print('\npublic ephemeral key:', format(r, '#064x'))

q = []
hint = []
rbytes = r.to_bytes(ec.nsize, 'big')
nKeys = 3
for i in range(nKeys):
    ibytes = i.to_bytes(ec.nsize, 'big')
    hd = hf(ibytes + rbytes).digest()
    hint.append(int_from_bits(hd))
    q.append((mprvkey + hint[i]) % ec.n)
    Q = mult(q[i], ec.G)
    print('\nprvkey#', i, ':', hex(q[i]))
    print('Pubkey#', i, ':', hex(Q[0]))
    print('           ', hex(Q[1]))

# Pubkeys could be calculated without using prvkeys
for i in range(nKeys):
    Q = ec.add(mpubkey, mult(hint[i], ec.G))
    assert Q == mult(q[i], ec.G)
Exemplo n.º 24
0
# Copyright (C) 2017-2019 The btclib developers
#
# This file is part of btclib. It is subject to the license terms in the
# LICENSE file found in the top-level directory of this distribution.
#
# No part of btclib including this file, may be copied, modified, propagated,
# or distributed except according to the terms contained in the LICENSE file.
""" Deterministic Wallet (Type-1)"""

import random
from hashlib import sha256 as hf

from btclib.curvemult import mult
from btclib.curves import secp256k1 as ec
from btclib.utils import int_from_bits

# master prvkey
mprvkey = random.getrandbits(ec.nlen) % ec.n
print('\nmaster private key =', hex(mprvkey))

mprvkey_bytes = mprvkey.to_bytes(ec.nlen, 'big')
nKeys = 3
for i in range(nKeys):
    ibytes = i.to_bytes(ec.nlen, 'big')
    hd = hf(ibytes + mprvkey_bytes).digest()
    q = int_from_bits(hd)
    Q = mult(q, ec.G)
    print('\nprvkey#', i, ':', hex(q))
    print('Pubkey#', i, ':', hex(Q[0]))
    print('           ', hex(Q[1]))
Exemplo n.º 25
0
print(f"prvkey: {hex(q).upper()}")
print(f"PubKey: {hex(Q[0]).upper()}")

print("\n0. Message to be signed")
orig_msg1 = "Paolo is afraid of ephemeral random numbers"
msg1 = sha256(orig_msg1.encode()).digest()
print(msg1.hex().upper())

print("\n*** Ephemeral key and challenge")
# ephemeral key k must be kept secret and never reused !!!!!
# good choice: k = hf(q||msg)
# different for each msg, private because of q
temp = q.to_bytes(32, "big") + msg1
k1_bytes = sha256(temp).digest()
k1 = int.from_bytes(k1_bytes, "big") % ec.n
k1 = int_from_bits(k1_bytes, ec.nlen) % ec.n
assert 0 < k1 < ec.n, "Invalid ephemeral key"
print(f"eph k: {hex(k1).upper()}")

K1 = mult(k1, ec.G)
c1 = _challenge(msg1, Q[0], K1[0], ec, sha256)
print(f"   c1: {hex(c1).upper()}")

print("2. Sign message")
r1 = K1[0]
s1 = (k1 + c1 * q) % ec.n
print(f"   r1: {hex(r1).upper()}")
print(f"   s1: {hex(s1).upper()}")

print("3. Verify signature")
K = double_mult(-c1, Q, s1, ec.G)
Exemplo n.º 26
0
def sign(
    msg: Octets,
    ks: Sequence[int],
    sign_key_idx: Sequence[int],
    sign_keys: Sequence[int],
    pubk_rings: PubkeyRing,
) -> Tuple[bytes, SValues]:
    """Borromean ring signature - signing algorithm

    https://github.com/ElementsProject/borromean-signatures-writeup
    https://github.com/Blockstream/borromean_paper/blob/master/borromean_draft_0.01_9ade1e49.pdf

    inputs:
    - msg: message to be signed (bytes)
    - sign_key_idx: list of indexes representing each signing key per ring
    - sign_keys: list containing the whole set of signing keys (one per ring)
    - pubk_rings: dictionary of sequences representing single rings of pub_keys
    """

    msg = bytes_from_octets(msg)
    m = _get_msg_format(msg, pubk_rings)

    e0bytes = m
    s: SValues = defaultdict(list)
    e: SValues = defaultdict(list)
    # step 1
    for i, (pubk_ring, j_star, k) in enumerate(
        zip(pubk_rings.values(), sign_key_idx, ks)
    ):
        keys_size = len(pubk_ring)
        s[i] = [0] * keys_size
        e[i] = [0] * keys_size
        start_idx = (j_star + 1) % keys_size
        r = bytes_from_point(mult(k), ec)
        if start_idx != 0:
            for j in range(start_idx, keys_size):
                s[i][j] = secrets.randbits(256)
                e[i][j] = int_from_bits(_hash(m, r, i, j), ec.nlen) % ec.n
                # edge case that cannot be reproduced in the test suite
                if not 0 < e[i][j] < ec.n:
                    err_msg = "implausibile signature failure"  # pragma: no cover
                    raise BTClibRuntimeError(err_msg)  # pragma: no cover
                t = double_mult(-e[i][j], pubk_ring[j], s[i][j], ec.G)
                r = bytes_from_point(t, ec)
        e0bytes += r
    e0 = hf(e0bytes).digest()
    # step 2
    for i, (j_star, k) in enumerate(zip(sign_key_idx, ks)):
        e[i][0] = int_from_bits(_hash(m, e0, i, 0), ec.nlen) % ec.n
        # edge case that cannot be reproduced in the test suite
        if not 0 < e[i][0] < ec.n:
            err_msg = "implausibile signature failure"  # pragma: no cover
            raise BTClibRuntimeError(err_msg)  # pragma: no cover
        for j in range(1, j_star + 1):
            s[i][j - 1] = secrets.randbits(256)
            t = double_mult(-e[i][j - 1], pubk_rings[i][j - 1], s[i][j - 1], ec.G)
            r = bytes_from_point(t, ec)
            e[i][j] = int_from_bits(_hash(m, r, i, j), ec.nlen) % ec.n
            # edge case that cannot be reproduced in the test suite
            if not 0 < e[i][j] < ec.n:
                err_msg = "implausibile signature failure"  # pragma: no cover
                raise BTClibRuntimeError(err_msg)  # pragma: no cover
        s[i][j_star] = k + sign_keys[i] * e[i][j_star]
    return e0, s
Exemplo n.º 27
0
def test_musig() -> None:
    """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
    """

    ec = CURVES["secp256k1"]

    m = 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_int = ssa.gen_keys()
    x_Q1 = x_Q1_int.to_bytes(ec.psize, "big")

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

    q3, x_Q3_int = ssa.gen_keys()
    x_Q3 = x_Q3_int.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] = []
    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 Q[1] % 2:
        # 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 K[1] % 2:
        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(m, Q[0], r, ec, hf)
    s_1 = (k1 + e * a1 * q1) % ec.n
    s_2 = (k2 + e * a2 * q2) % ec.n
    s3 = (k3 + e * a3 * q3) % ec.n

    # exchange s_i (interactive)

    # finalize signature (non interactive)
    s = (s_1 + s_2 + s3) % ec.n
    sig = r, s
    # check signature is valid
    ssa._assert_as_valid(m, Q[0], sig, ec, hf)
Exemplo n.º 28
0
    def test_schnorr_bip_tv(self):
        """Bip-Schnorr Test Vectors

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

        hf = sha256

        # test vector 1
        prv = int_from_bits(b'\x00' * 31 + b'\x01')
        pub = mult(prv)
        msg = b'\x00' * 32
        expected_sig = (
            0x787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF6,
            0x7031A98831859DC34DFFEEDDA86831842CCD0079E1F92AF177F7F22CC1DCED05)
        eph_prv = int.from_bytes(hf(prv.to_bytes(32, byteorder='big') +
                                    msg).digest(),
                                 byteorder='big')
        sig = ssa.sign(msg, prv, eph_prv)
        self.assertTrue(ssa._verify(msg, pub, sig))
        self.assertEqual(sig, expected_sig)
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # test vector 2
        prv = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
        pub = mult(prv)
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        expected_sig = (
            0x2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D,
            0x1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD)
        eph_prv = int.from_bytes(hf(prv.to_bytes(32, byteorder='big') +
                                    msg).digest(),
                                 byteorder='big')
        sig = ssa.sign(msg, prv, eph_prv)
        self.assertTrue(ssa._verify(msg, pub, sig))
        self.assertEqual(sig, expected_sig)
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # test vector 3
        prv = 0xC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7
        pub = mult(prv)
        msg = bytes.fromhex(
            "5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C")
        expected_sig = (
            0x00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE,
            0x00880371D01766935B92D2AB4CD5C8A2A5837EC57FED7660773A05F0DE142380)
        eph_prv = int.from_bytes(hf(prv.to_bytes(32, byteorder='big') +
                                    msg).digest(),
                                 byteorder='big')
        sig = ssa.sign(msg, prv, eph_prv)
        self.assertTrue(ssa._verify(msg, pub, sig))
        self.assertEqual(sig, expected_sig)
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # test vector 4
        pub = point_from_octets(
            "03DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34"
        )
        msg = bytes.fromhex(
            "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703")
        sig = (
            0x00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C63,
            0x02A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D)
        self.assertTrue(ssa._verify(msg, pub, sig))
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # test vector 5
        # test would fail if jacobi symbol of x(R) instead of y(R) is used
        pub = point_from_octets(
            "031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F"
        )
        msg = bytes.fromhex(
            "0000000000000000000000000000000000000000000000000000000000000000")
        sig = (
            0x52818579ACA59767E3291D91B76B637BEF062083284992F2D95F564CA6CB4E35,
            0x30B1DA849C8E8304ADC0CFE870660334B3CFC18E825EF1DB34CFAE3DFC5D8187)
        self.assertTrue(ssa._verify(msg, pub, sig))
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # test vector 6
        # test would fail if msg is reduced
        pub = point_from_octets(
            "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"
        )
        msg = bytes.fromhex(
            "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
        sig = (
            0x570DD4CA83D4E6317B8EE6BAE83467A1BF419D0767122DE409394414B05080DC,
            0xE9EE5F237CBD108EABAE1E37759AE47F8E4203DA3532EB28DB860F33D62D49BD)
        self.assertTrue(ssa._verify(msg, pub, sig))
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # new proposed test: test would fail if msg is reduced
        pub = point_from_octets(
            "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"
        )
        msg = bytes.fromhex(
            "000008D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A5000000")
        sig = (
            0x3598678C6C661F02557E2F5614440B53156997936FE54A90961CFCC092EF789D,
            0x41E4E4386E54C924251679ADD3D837367EECBFF248A3DE7C2DB4CE52A3D6192A)
        self.assertTrue(ssa._verify(msg, pub, sig))
        e = ssa._e(sig[0], pub, msg)
        self.assertEqual(ssa._pubkey_recovery(e, sig), pub)

        # new proposed test: genuine failure
        pub = point_from_octets(
            "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"
        )
        msg = bytes.fromhex(
            "0000000000000000000000000000000000000000000000000000000000000000")
        sig = (
            0x3598678C6C661F02557E2F5614440B53156997936FE54A90961CFCC092EF789D,
            0x41E4E4386E54C924251679ADD3D837367EECBFF248A3DE7C2DB4CE52A3D6192A)
        self.assertFalse(ssa._verify(msg, pub, sig))

        # new proposed test: P = infinite
        pub = 1, 0
        msg = bytes.fromhex(
            "5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C")
        sig = (
            0x00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE,
            0x00880371D01766935B92D2AB4CD5C8A2A5837EC57FED7660773A05F0DE142380)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 7
        # public key not on the curve
        # impossible to verify with btclib analytics as it at Point conversion
        self.assertRaises(
            ValueError, point_from_octets,
            "03EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34"
        )
        # msg = bytes.fromhex("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703")
        # sig = (0x00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C63, 0x02A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D)
        # self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 8
        # Incorrect sig: incorrect R residuosity
        pub = point_from_octets(
            "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0x2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D,
            0xFA16AEE06609280A19B67A24E1977E4697712B5FD2943914ECD5F730901B4AB7)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 9
        # Incorrect sig: negated message hash
        pub = point_from_octets(
            "03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B"
        )
        msg = bytes.fromhex(
            "5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C")
        sig = (
            0x00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE,
            0xD092F9D860F1776A1F7412AD8A1EB50DACCC222BC8C0E26B2056DF2F273EFDEC)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 10
        # Incorrect sig: negated s value
        pub = point_from_octets(
            "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
        )
        msg = b'\x00' * 32
        sig = (
            0x787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF6,
            0x8FCE5677CE7A623CB20011225797CE7A8DE1DC6CCD4F754A47DA6C600E59543C)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 11
        # Incorrect sig: negated public key
        pub = point_from_octets(
            "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0x2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D,
            0x1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 12
        # sG - eP is infinite.
        # Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 0
        pub = point_from_octets(
            "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0x0000000000000000000000000000000000000000000000000000000000000000,
            0x9E9D01AF988B5CEDCE47221BFA9B222721F3FA408915444A4B489021DB55775F)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 13
        # sG - eP is infinite.
        # Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 1"""
        pub = point_from_octets(
            "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0x0000000000000000000000000000000000000000000000000000000000000001,
            0xD37DDF0254351836D84B1BD6A795FD5D523048F298C4214D187FE4892947F728)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)

        # test vector 14
        # sig[0:32] is not an X coordinate on the curve
        pub = point_from_octets(
            "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0x4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D,
            0x1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD)
        self.assertFalse(ssa._verify(msg, pub, sig))

        # test vector 15
        # sig[0:32] is equal to field size
        pub = point_from_octets(
            "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2F,
            0x1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD)
        #self.assertRaises(ValueError, ssa._verify, msg, pub, sig)
        self.assertFalse(ssa._verify(msg, pub, sig))

        # test vector 16
        # sig[32:64] is equal to curve order
        pub = point_from_octets(
            "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        )
        msg = bytes.fromhex(
            "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
        sig = (
            0x2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D,
            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141)
        self.assertRaises(ValueError, ssa._verify, msg, pub, sig)
Exemplo n.º 29
0
mprvkey = 1 + secrets.randbelow(ec.n - 1)
print(f"\nmaster prvkey: {hex(mprvkey).upper()}")

# Master Pubkey:
mpubkey = mult(mprvkey, ec.G)
print(f"Master Pubkey: {hex(mpubkey[0]).upper()}")
print(f"               {hex(mpubkey[1]).upper()}")

r = secrets.randbits(ec.nlen)
print(f"\npublic random number: {hex(r).upper()}")

rbytes = r.to_bytes(ec.nsize, "big")
nKeys = 3
for i in range(nKeys):
    ibytes = i.to_bytes(ec.nsize, "big")
    hd = hf(ibytes + rbytes).digest()
    offset = int_from_bits(hd, ec.nlen) % ec.n
    q = (mprvkey + offset) % ec.n
    Q = mult(q, ec.G, ec)
    print(f"\nprvkey #{i}: {hex(q).upper()}")
    print(f"Pubkey #{i}: {hex(Q[0]).upper()}")
    print(f"           {hex(Q[1]).upper()}")

# Pubkeys could also be calculated without using prvkeys
for i in range(nKeys):
    ibytes = i.to_bytes(ec.nsize, "big")
    hd = hf(ibytes + rbytes).digest()
    offset = int_from_bits(hd, ec.nlen) % ec.n
    Q = ec.add(mpubkey, mult(offset, ec.G, ec))
    assert Q == mult((mprvkey + offset) % ec.n, ec.G, ec)
Exemplo n.º 30
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)