def borromean_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

    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 = point2octets(ec, pointMult(ec, k[i], ec.G), True)
        if start_idx != 0:
            for j in range(start_idx, keys_size):
                s[i][j] = random.getrandbits(256)
                temp = borromean_hash(m, R, i, j)
                e[i][j] = bits2int(ec, temp)
                assert e[i][j] != 0 and e[i][j] < ec.n, "sign fail"
                T = DblScalarMult(ec, s[i][j], ec.G, -e[i][j],
                                  pubk_rings[i][j])
                R = point2octets(ec, T, True)
        e0bytes += R
    e0 = sha256(e0bytes).digest()
    # step 2
    for i in range(ring_size):
        temp = borromean_hash(m, e0, i, 0)
        e[i][0] = bits2int(ec, temp)
        assert e[i][0] != 0 and e[i][0] < ec.n, "sign fail"
        j_star = sign_key_idx[i]
        for j in range(1, j_star + 1):
            s[i][j - 1] = random.getrandbits(256)
            T = DblScalarMult(ec, s[i][j - 1], ec.G, -e[i][j - 1],
                              pubk_rings[i][j - 1])
            R = point2octets(ec, T, True)
            e[i][j] = bits2int(ec, borromean_hash(m, R, i, j))
            assert e[i][j] != 0 and e[i][j] < ec.n, "sign fail"
        s[i][j_star] = k[i] + sign_keys[i] * e[i][j_star]
    return e0, s
Example #2
0
def _ecssa_e(ec: EC, hf, r: int, P: Point, m: bytes) -> int:
    # Let e = int(hf(bytes(x(R)) || bytes(dG) || m)) mod n.
    ebytes = int2octets(r, ec.psize) # FIXME: hsize, nsize ?
    ebytes += point2octets(ec, P, True)
    ebytes += m
    ebytes = hf(ebytes).digest()
    e = bits2int(ec, ebytes)
    return e
Example #3
0
def address_from_pubkey(Q: Point,
                        compressed: bool,
                        version: bytes = b'\x00') -> bytes:
    """Public key to (bytes) address"""

    pubkey = point2octets(ec, Q, compressed)

    # FIXME: this is mainnet only
    vh160 = version + h160(pubkey)
    return b58encode_check(vh160)
def get_msg_format(msg: bytes, pubk_rings: Dict[int, List[Point]]) -> bytes:
    m = msg
    rings = len(pubk_rings)
    for i in range(rings):
        keys = len(pubk_rings[i])
        for j in range(keys):
            P = pubk_rings[i][j]
            Pbytes = point2octets(ec, P, True)
            m += Pbytes
    return sha256(m).digest()
def _tweak(c: bytes, ec: EC, hf, k: int) -> Tuple[Point, int]:
    """tweak kG

    returns:
    - point kG to tweak
    - tweaked private key k + h(kG||c), the corresponding pubkey is a commitment to kG, c
    """
    R = pointMult(ec, k, ec.G)
    e = hf(point2octets(ec, R, True) + c).digest()
    e = int.from_bytes(e, 'big')
    return R, (e + k) % ec.n
Example #6
0
def ecssa_batch_validation(ec: EC,
                           hf,
                           ms: List[bytes],
                           P: List[Point],
                           a: List[int], 
                           sig: List[ECSS]) -> bool:
    # initialization
    mult = 0
    points = list()
    factors = list()

    u = len(P)
    for i in range(u):
        r, s = to_ssasig(ec, sig[i])
        ebytes = r.to_bytes(32, byteorder="big")
        ebytes += point2octets(ec, P[i], True)
        ebytes += ms[i]
        ebytes = hf(ebytes).digest()
        e = bits2int(ec, ebytes)

        y = ec.y(r)  # raises an error if y does not exist

        mult += a[i] * s % ec.n
        points.append(_jac_from_aff((r, y)))
        factors.append(a[i])
        points.append(_jac_from_aff(P[i]))
        factors.append(a[i] * e % ec.n)

    # Bos-coster's algorithm, source:
    # https://cr.yp.to/badbatch/boscoster2.py
    boscoster = list(zip([-n for n in factors], points))
    heapq.heapify(boscoster)
    while len(boscoster) > 1:
        aK1 = heapq.heappop(boscoster)
        aK2 = heapq.heappop(boscoster)
        a1, K1 = -aK1[0], aK1[1]
        a2, K2 = -aK2[0], aK2[1]
        K2 = ec._addJacobian(K1, K2)
        a1 -= a2
        if a1 > 0:
            heapq.heappush(boscoster, (-a1, K1))
        heapq.heappush(boscoster, (-a2, K2))
    aK = heapq.heappop(boscoster)

    RHSJ = _pointMultJacobian(ec, -aK[0], aK[1])
    TJ = _pointMultJacobian(ec, mult, _jac_from_aff(ec.G))
    RHS = ec._affine_from_jac(RHSJ)
    T = ec._affine_from_jac(TJ)

    return T == RHS
def verify_commit(c: bytes, ec: EC, 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(point2octets(ec, R, True) + ch).digest()
    e = bits2int(ec, e)
    W = ec.add(R, pointMult(ec, e, ec.G))
    # different verify functions?
    # return w == W[0] # ECSS
    return w == W[0] % ec.n  # ECDS
def secondGenerator(ec: EC, 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 = point2octets(ec, ec.G, False)
    hd = hf(G_bytes).digest()
    hx = bits2int(ec, hd)
    isCurvePoint = False
    while not isCurvePoint:
        try:
            hy = ec.yOdd(hx, False)
            isCurvePoint = True
        except:
            hx += 1
    return hx, hy
def _borromean_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] = bits2int(ec, borromean_hash(m, e0, i, 0))
        assert e[i][0] != 0, "how did you do that?!?"
        R = b'\0x00'
        for j in range(keys_size):
            T = DblScalarMult(ec, s[i][j], ec.G, -e[i][j], pubk_rings[i][j])
            R = point2octets(ec, T, True)
            if j != len(pubk_rings[i]) - 1:
                e[i][j + 1] = bits2int(ec, borromean_hash(m, R, i, j + 1))
                assert e[i][j + 1] != 0, "how did you do that?!?"
            else:
                e0bytes += R
    e0_prime = sha256(e0bytes).digest()
    return e0_prime == e0
def bip32_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 = b58decode_check(xprv, 78)
    assert xprv[45] == 0, "extended key is not a private one"

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

    # serialization data
    xpub = PUBLIC[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 = octets2int(xprv[46:])
    P = pointMult(ec, p, ec.G)
    xpub += point2octets(ec, P, True)  # public key
    return b58encode_check(xpub)
def bip32_ckd(xparentkey: octets, index: Union[octets, int]) -> bytes:
    """Child Key Derivation (CDK)

    Key derivation is normal if the extended parent key is public or
    child_index is less than 0x80000000.

    Key derivation is hardened if the extended parent key is private and
    child_index is not less than 0x80000000.
    """

    if isinstance(index, int):
        index = index.to_bytes(4, 'big')
    elif isinstance(index, str):  # hex string
        index = bytes.fromhex(index)

    if len(index) != 4:
        raise ValueError(f"a 4 bytes int is required, not {len(index)}")

    xparent = b58decode_check(xparentkey, 78)

    version = xparent[:4]

    # serialization data
    xkey = version  # version
    xkey += (xparent[4] + 1).to_bytes(1, 'big')  # (increased) depth

    if (version in PUBLIC):
        if xparent[45] not in (2, 3):  # not a compressed public key
            raise ValueError("version/key mismatch in extended parent key")
        Parent_bytes = xparent[45:]
        Parent = octets2point(ec, Parent_bytes)
        xkey += h160(Parent_bytes)[:4]  # parent pubkey fingerprint
        if index[0] >= 0x80:
            raise ValueError("no private/hardened derivation from pubkey")
        xkey += index  # child index
        parent_chain_code = xparent[13:45]  # normal derivation
        # actual extended key (key + chain code) derivation
        h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest()
        offset = int.from_bytes(h[:32], 'big')
        Offset = pointMult(ec, offset, ec.G)
        Child = ec.add(Parent, Offset)
        Child_bytes = point2octets(ec, Child, True)
        xkey += h[32:]  # chain code
        xkey += Child_bytes  # public key
    elif (version in PRIVATE):
        if xparent[45] != 0:  # not a private key
            raise ValueError("version/key mismatch in extended parent key")
        parent = int.from_bytes(xparent[46:], 'big')
        Parent = pointMult(ec, parent, ec.G)
        Parent_bytes = point2octets(ec, Parent, True)
        xkey += h160(Parent_bytes)[:4]  # parent pubkey fingerprint
        xkey += index  # child index
        # actual extended key (key + chain code) derivation
        parent_chain_code = xparent[13:45]
        if (index[0] < 0x80):  # normal derivation
            h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest()
        else:  # hardened derivation
            h = HMAC(parent_chain_code, xparent[45:] + index, sha512).digest()
        offset = int.from_bytes(h[:32], 'big')
        child = (parent + offset) % ec.n
        child_bytes = b'\x00' + child.to_bytes(32, 'big')
        xkey += h[32:]  # chain code
        xkey += child_bytes  # private key
    else:
        raise ValueError("invalid extended key version")

    return b58encode_check(xkey)