def xpub_from_xprv(xprv: octets) -> bytes:
    """Neutered Derivation (ND)

    Computation of the extended public key corresponding to an extended
    private key (“neutered” as it removes the ability to sign transactions)
    """

    xprv = base58.decode_check(xprv, 78)
    if xprv[45] != 0:
        raise ValueError("extended key is not a private one")

    i = PRV.index(xprv[:4])

    # serialization data
    xpub = PUB[i]  # version
    # unchanged serialization data
    xpub += xprv[4:5]  # depth
    xpub += xprv[5:9]  # parent pubkey fingerprint
    xpub += xprv[9:13]  # child index
    xpub += xprv[13:45]  # chain code

    p = int_from_octets(xprv[46:])
    P = mult(ec, p, ec.G)
    xpub += octets_from_point(ec, P, True)  # public key
    return base58.encode_check(xpub)
def prvkey_from_wif(wif: octets) -> Tuple[int, bool]:
    """Wallet Import Format to (bytes) private key"""

    payload = base58.decode_check(wif)
    if payload[0] != 0x80:
        raise ValueError("Not a private key WIF: missing leading 0x80")

    if len(payload) == ec.psize + 2:       # compressed WIF
        compressed = True
        if payload[ec.psize + 1] != 0x01:  # must have a trailing 0x01
            raise ValueError("Not a compressed WIF: missing trailing 0x01")
        prvkey = int_from_octets(payload[1:-1])
    elif len(payload) == ec.psize + 1:     # uncompressed WIF
        compressed = False
        prvkey = int_from_octets(payload[1:])
    else:
        raise ValueError(f"Not a WIF: wrong size ({len(payload)})")
    
    if not 0 < prvkey < ec.n:
        raise ValueError(f"Not a WIF: private key {hex(prvkey)} not in (0, n)")

    return prvkey, compressed
def kdf(zbytes: bytes, keydatasize: int, ec: Curve, hf) -> bytes:
    """ ANS-X9.63-KDF - SEC 1 specification

    source: http://www.secg.org/sec1-v2.pdf, section 3.6.1
    """
    hsize = hf().digest_size
    assert keydatasize < hsize * (2**32 - 1), "invalid"
    counter = 1
    counter_bytes = counter.to_bytes(4, 'big')
    K_temp = []
    for i in range((keydatasize + 1) // hsize):
        K_temp.append(hf(zbytes + counter_bytes).digest())
        counter += 1
        counter_bytes = counter.to_bytes(4, 'big')
        i += 1
    K_bytes = b''.join(K_temp[i] for i in range(keydatasize // hsize))
    K = int_from_octets(K_bytes) >> (keydatasize - hsize)
    return octets_from_int(K, ec.psize)
def mprv_from_seed(seed: octets, version: octets) -> bytes:
    """derive the master extended private key from the seed"""

    if isinstance(version, str):  # hex string
        version = bytes.fromhex(version)
    if version not in PRV:
        m = f"invalid private version ({version})"
        raise ValueError(m)

    # serialization data
    xmprv = version  # version
    xmprv += b'\x00'  # depth
    xmprv += b'\x00\x00\x00\x00'  # parent pubkey fingerprint
    xmprv += b'\x00\x00\x00\x00'  # child index

    # actual extended key (key + chain code) derivation
    if isinstance(seed, str):  # hex string
        seed = bytes.fromhex(seed)
    hd = HMAC(b"Bitcoin seed", seed, sha512).digest()
    mprv = int_from_octets(hd[:32])
    xmprv += hd[32:]  # chain code
    xmprv += b'\x00' + mprv.to_bytes(32, 'big')  # private key

    return base58.encode_check(xmprv)
Example #5
0
    def test_musig(self):
        """testing 3-of-3 MuSig

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

        ec = secp256k1
        hf = sha256

        # key setup is not interactive

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.assertTrue(ssa.verify(ec, hf, M, Q_All, sig))