Beispiel #1
0
    def gen_clsag_sig(self, ring_size=11, index=None):
        msg = random.bytes(32)
        amnt = crypto.sc_init(random.uniform(0xFFFFFF) + 12)
        priv = crypto.random_scalar()
        msk = crypto.random_scalar()
        alpha = crypto.random_scalar()
        P = crypto.scalarmult_base(priv)
        C = crypto.add_keys2(msk, amnt, crypto.xmr_H())
        Cp = crypto.add_keys2(alpha, amnt, crypto.xmr_H())

        ring = []
        for i in range(ring_size - 1):
            tk = TmpKey(
                crypto.encodepoint(
                    crypto.scalarmult_base(crypto.random_scalar())),
                crypto.encodepoint(
                    crypto.scalarmult_base(crypto.random_scalar())),
            )
            ring.append(tk)

        index = index if index is not None else random.uniform(len(ring))
        ring.insert(index, TmpKey(crypto.encodepoint(P),
                                  crypto.encodepoint(C)))
        ring2 = list(ring)
        mg_buffer = []

        self.assertTrue(
            crypto.point_eq(crypto.scalarmult_base(priv),
                            crypto.decodepoint(ring[index].dest)))
        self.assertTrue(
            crypto.point_eq(
                crypto.scalarmult_base(crypto.sc_sub(msk, alpha)),
                crypto.point_sub(crypto.decodepoint(ring[index].commitment),
                                 Cp),
            ))

        mlsag.generate_clsag_simple(
            msg,
            ring,
            CtKey(priv, msk),
            alpha,
            Cp,
            index,
            mg_buffer,
        )

        sD = crypto.decodepoint(mg_buffer[-1])
        sc1 = crypto.decodeint(mg_buffer[-2])
        scalars = [crypto.decodeint(x) for x in mg_buffer[1:-2]]
        H = crypto.new_point()
        sI = crypto.new_point()

        crypto.hash_to_point_into(H, crypto.encodepoint(P))
        crypto.scalarmult_into(sI, H, priv)  # I = p*H
        return msg, scalars, sc1, sI, sD, ring2, Cp
    def verify_batch(self, proofs, single_optim=True, proof_v8=False):
        """
        BP batch verification
        :param proofs:
        :param single_optim: single proof memory optimization
        :param proof_v8: previous testnet version
        :return:
        """
        max_length = 0
        for proof in proofs:
            utils.ensure(is_reduced(proof.taux), "Input scalar not in range")
            utils.ensure(is_reduced(proof.mu), "Input scalar not in range")
            utils.ensure(is_reduced(proof.a), "Input scalar not in range")
            utils.ensure(is_reduced(proof.b), "Input scalar not in range")
            utils.ensure(is_reduced(proof.t), "Input scalar not in range")
            utils.ensure(len(proof.V) >= 1, "V does not have at least one element")
            utils.ensure(len(proof.L) == len(proof.R), "|L| != |R|")
            utils.ensure(len(proof.L) > 0, "Empty proof")
            max_length = max(max_length, len(proof.L))

        utils.ensure(max_length < 32, "At least one proof is too large")

        maxMN = 1 << max_length
        logN = 6
        N = 1 << logN
        tmp = _ensure_dst_key()

        # setup weighted aggregates
        is_single = len(proofs) == 1 and single_optim  # ph4
        z1 = init_key(_ZERO)
        z3 = init_key(_ZERO)
        m_z4 = vector_dup(_ZERO, maxMN) if not is_single else None
        m_z5 = vector_dup(_ZERO, maxMN) if not is_single else None
        m_y0 = init_key(_ZERO)
        y1 = init_key(_ZERO)
        muex_acc = init_key(_ONE)

        Gprec = self._gprec_aux(maxMN)
        Hprec = self._hprec_aux(maxMN)

        for proof in proofs:
            M = 1
            logM = 0
            while M <= _BP_M and M < len(proof.V):
                logM += 1
                M = 1 << logM

            utils.ensure(len(proof.L) == 6 + logM, "Proof is not the expected size")
            MN = M * N
            weight_y = crypto.encodeint(crypto.random_scalar())
            weight_z = crypto.encodeint(crypto.random_scalar())

            # Reconstruct the challenges
            hash_cache = hash_vct_to_scalar(None, proof.V)
            y = hash_cache_mash(None, hash_cache, proof.A, proof.S)
            utils.ensure(y != _ZERO, "y == 0")
            z = hash_to_scalar(None, y)
            copy_key(hash_cache, z)
            utils.ensure(z != _ZERO, "z == 0")

            x = hash_cache_mash(None, hash_cache, z, proof.T1, proof.T2)
            utils.ensure(x != _ZERO, "x == 0")
            x_ip = hash_cache_mash(None, hash_cache, x, proof.taux, proof.mu, proof.t)
            utils.ensure(x_ip != _ZERO, "x_ip == 0")

            # PAPER LINE 61
            sc_mulsub(m_y0, proof.taux, weight_y, m_y0)
            zpow = vector_powers(z, M + 3)

            k = _ensure_dst_key()
            ip1y = vector_power_sum(y, MN)
            sc_mulsub(k, zpow[2], ip1y, _ZERO)
            for j in range(1, M + 1):
                utils.ensure(j + 2 < len(zpow), "invalid zpow index")
                sc_mulsub(k, zpow.to(j + 2), _BP_IP12, k)

            # VERIFY_line_61rl_new
            sc_muladd(tmp, z, ip1y, k)
            sc_sub(tmp, proof.t, tmp)

            sc_muladd(y1, tmp, weight_y, y1)
            weight_y8 = init_key(weight_y)
            if not proof_v8:
                weight_y8 = sc_mul(None, weight_y, _EIGHT)

            muex = MultiExpSequential(points=[pt for pt in proof.V])
            for j in range(len(proof.V)):
                sc_mul(tmp, zpow[j + 2], weight_y8)
                muex.add_scalar(init_key(tmp))

            sc_mul(tmp, x, weight_y8)
            muex.add_pair(init_key(tmp), proof.T1)

            xsq = _ensure_dst_key()
            sc_mul(xsq, x, x)

            sc_mul(tmp, xsq, weight_y8)
            muex.add_pair(init_key(tmp), proof.T2)

            weight_z8 = init_key(weight_z)
            if not proof_v8:
                weight_z8 = sc_mul(None, weight_z, _EIGHT)

            muex.add_pair(weight_z8, proof.A)
            sc_mul(tmp, x, weight_z8)
            muex.add_pair(init_key(tmp), proof.S)

            multiexp(tmp, muex, False)
            add_keys(muex_acc, muex_acc, tmp)
            del muex

            # Compute the number of rounds for the inner product
            rounds = logM + logN
            utils.ensure(rounds > 0, "Zero rounds")

            # PAPER LINES 21-22
            # The inner product challenges are computed per round
            w = _ensure_dst_keyvect(None, rounds)
            for i in range(rounds):
                hash_cache_mash(tmp_bf_0, hash_cache, proof.L[i], proof.R[i])
                w.read(i, tmp_bf_0)
                utils.ensure(w[i] != _ZERO, "w[i] == 0")

            # Basically PAPER LINES 24-25
            # Compute the curvepoints from G[i] and H[i]
            yinvpow = init_key(_ONE)
            ypow = init_key(_ONE)
            yinv = invert(None, y)
            self.gc(61)

            winv = _ensure_dst_keyvect(None, rounds)
            for i in range(rounds):
                invert(tmp_bf_0, w.to(i))
                winv.read(i, tmp_bf_0)
                self.gc(62)

            g_scalar = _ensure_dst_key()
            h_scalar = _ensure_dst_key()
            twoN = self._two_aux(N)
            for i in range(MN):
                copy_key(g_scalar, proof.a)
                sc_mul(h_scalar, proof.b, yinvpow)

                for j in range(rounds - 1, -1, -1):
                    J = len(w) - j - 1

                    if (i & (1 << j)) == 0:
                        sc_mul(g_scalar, g_scalar, winv.to(J))
                        sc_mul(h_scalar, h_scalar, w.to(J))
                    else:
                        sc_mul(g_scalar, g_scalar, w.to(J))
                        sc_mul(h_scalar, h_scalar, winv.to(J))

                # Adjust the scalars using the exponents from PAPER LINE 62
                sc_add(g_scalar, g_scalar, z)
                utils.ensure(2 + i // N < len(zpow), "invalid zpow index")
                utils.ensure(i % N < len(twoN), "invalid twoN index")
                sc_mul(tmp, zpow.to(2 + i // N), twoN.to(i % N))
                sc_muladd(tmp, z, ypow, tmp)
                sc_mulsub(h_scalar, tmp, yinvpow, h_scalar)

                if not is_single:  # ph4
                    sc_mulsub(m_z4[i], g_scalar, weight_z, m_z4[i])
                    sc_mulsub(m_z5[i], h_scalar, weight_z, m_z5[i])
                else:
                    sc_mul(tmp, g_scalar, weight_z)
                    sub_keys(muex_acc, muex_acc, scalarmult_key(tmp, Gprec.to(i), tmp))

                    sc_mul(tmp, h_scalar, weight_z)
                    sub_keys(muex_acc, muex_acc, scalarmult_key(tmp, Hprec.to(i), tmp))

                if i != MN - 1:
                    sc_mul(yinvpow, yinvpow, yinv)
                    sc_mul(ypow, ypow, y)
                if i & 15 == 0:
                    self.gc(62)

            del (g_scalar, h_scalar, twoN)
            self.gc(63)

            sc_muladd(z1, proof.mu, weight_z, z1)
            muex = MultiExpSequential(
                point_fnc=lambda i, d: proof.L[i // 2]
                if i & 1 == 0
                else proof.R[i // 2]
            )
            for i in range(rounds):
                sc_mul(tmp, w[i], w[i])
                sc_mul(tmp, tmp, weight_z8)
                muex.add_scalar(tmp)
                sc_mul(tmp, winv[i], winv[i])
                sc_mul(tmp, tmp, weight_z8)
                muex.add_scalar(tmp)

            acc = multiexp(None, muex, False)
            add_keys(muex_acc, muex_acc, acc)

            sc_mulsub(tmp, proof.a, proof.b, proof.t)
            sc_mul(tmp, tmp, x_ip)
            sc_muladd(z3, tmp, weight_z, z3)

        sc_sub(tmp, m_y0, z1)
        z3p = sc_sub(None, z3, y1)

        check2 = crypto.encodepoint(
            crypto.ge25519_double_scalarmult_base_vartime(
                crypto.decodeint(z3p), crypto.xmr_H(), crypto.decodeint(tmp)
            )
        )
        add_keys(muex_acc, muex_acc, check2)

        if not is_single:  # ph4
            muex = MultiExpSequential(
                point_fnc=lambda i, d: Gprec.to(i // 2)
                if i & 1 == 0
                else Hprec.to(i // 2)
            )
            for i in range(maxMN):
                muex.add_scalar(m_z4[i])
                muex.add_scalar(m_z5[i])
            add_keys(muex_acc, muex_acc, multiexp(None, muex, True))

        if muex_acc != _ONE:
            raise ValueError("Verification failure at step 2")
        return True
_BP_LOG_N = const(6)
_BP_N = const(64)  # 1 << _BP_LOG_N
_BP_M = const(16)  # maximal number of bulletproofs

_ZERO = b"\x00" * 32
_ONE = b"\x01" + b"\x00" * 31
# _TWO = b"\x02" + b"\x00" * 31
_EIGHT = b"\x08" + b"\x00" * 31
_INV_EIGHT = crypto.INV_EIGHT
_MINUS_ONE = b"\xec\xd3\xf5\x5c\x1a\x63\x12\x58\xd6\x9c\xf7\xa2\xde\xf9\xde\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10"
# _MINUS_INV_EIGHT = b"\x74\xa4\x19\x7a\xf0\x7d\x0b\xf7\x05\xc2\xda\x25\x2b\x5c\x0b\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a"

# Monero H point
_XMR_H = b"\x8b\x65\x59\x70\x15\x37\x99\xaf\x2a\xea\xdc\x9f\xf1\xad\xd0\xea\x6c\x72\x51\xd5\x41\x54\xcf\xa9\x2c\x17\x3a\x0d\xd3\x9c\x1f\x94"
_XMR_HP = crypto.xmr_H()

# ip12 = inner_product(oneN, twoN);
_BP_IP12 = b"\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"


#
# Rct keys operations
# tmp_x are global working registers to minimize memory allocations / heap fragmentation.
# Caution has to be exercised when using the registers and operations using the registers
#

tmp_bf_0 = bytearray(32)
tmp_bf_1 = bytearray(32)
tmp_bf_2 = bytearray(32)
tmp_bf_exp = bytearray(11 + 32 + 4)
 def test_h(self):
     H = unhexlify(
         b"8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"
     )
     self.assertEqual(crypto.encodepoint(crypto.xmr_H()), H)
Beispiel #5
0
def prove_range_mem(amount, last_mask=None):
    """
    Memory optimized range proof.

    Gives C, and mask such that \sumCi = C
    c.f. http:#eprint.iacr.org/2015/1098 section 5.1

    Ci is a commitment to either 0 or 2^i, i=0,...,63
    thus this proves that "amount" is in [0, 2^ATOMS]
    mask is a such that C = aG + bH, and b = amount
    :param amount:
    :param last_mask: ai[ATOMS-1] will be computed as \sum_{i=0}^{ATOMS-2} a_i - last_mask
    :param use_asnl: use ASNL, used before Borromean
    :return: sumCi, mask, RangeSig.
        sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H
        mask is "a" from the Pedersent commitment above.
    """
    res = bytearray(32 * (64 + 64 + 64 + 1))
    mv = memoryview(res)
    gc.collect()

    def as0(mv, x, i):
        crypto.encodeint_into(mv[32 * i :], x)

    def as1(mv, x, i):
        crypto.encodeint_into(mv[32 * 64 + 32 * i :], x)

    def aci(mv, x, i):
        crypto.encodepoint_into(mv[32 * 64 * 2 + 32 + 32 * i :], x)

    n = 64
    bb = d2b(amount, n)  # gives binary form of bb in "digits" binary digits
    ai = key_zero_vector(n)
    a = crypto.sc_0()

    C = crypto.identity()
    alpha = key_zero_vector(n)
    c_H = crypto.xmr_H()
    kck = crypto.get_keccak()  # ee computation

    # First pass, generates: ai, alpha, Ci, ee, s1
    for ii in range(n):
        ai[ii] = crypto.random_scalar()
        if last_mask is not None and ii == 64 - 1:
            ai[ii] = crypto.sc_sub(last_mask, a)

        a = crypto.sc_add(
            a, ai[ii]
        )  # creating the total mask since you have to pass this to receiver...

        alpha[ii] = crypto.random_scalar()
        L = crypto.scalarmult_base(alpha[ii])

        if bb[ii] == 0:
            Ctmp = crypto.scalarmult_base(ai[ii])
        else:
            Ctmp = crypto.point_add(crypto.scalarmult_base(ai[ii]), c_H)
        C = crypto.point_add(C, Ctmp)
        aci(mv, Ctmp, ii)

        if bb[ii] == 0:
            si = crypto.random_scalar()
            c = crypto.hash_to_scalar(crypto.encodepoint(L))
            L = crypto.add_keys2(si, c, crypto.point_sub(Ctmp, c_H))
            kck.update(crypto.encodepoint(L))
            as1(mv, si, ii)

        else:
            kck.update(crypto.encodepoint(L))

        c_H = crypto.point_double(c_H)

    # Compute ee, memory cleanup
    ee = crypto.decodeint(kck.digest())
    crypto.encodeint_into(ee, mv[64 * 32 * 2 :])
    del kck
    gc.collect()

    # Second phase computes: s0, s1
    c_H = crypto.xmr_H()

    for jj in range(n):
        if not bb[jj]:
            s0 = crypto.sc_mulsub(ai[jj], ee, alpha[jj])

        else:
            s0 = crypto.random_scalar()
            Ctmp = crypto.decodepoint(
                mv[32 * 64 * 2 + 32 + 32 * jj : 32 * 64 * 2 + 32 + 32 * jj + 32]
            )
            LL = crypto.add_keys2(s0, ee, Ctmp)
            cc = crypto.hash_to_scalar(crypto.encodepoint(LL))
            si = crypto.sc_mulsub(ai[jj], cc, alpha[jj])
            as1(mv, si, jj)
        as0(mv, s0, jj)

        c_H = crypto.point_double(c_H)

    gc.collect()
    return C, a, res
Beispiel #6
0
def prove_range_borromean(amount, last_mask):
    """Calculates Borromean range proof"""
    # The large chunks allocated first to avoid potential memory fragmentation issues.
    ai = bytearray(32 * 64)
    alphai = bytearray(32 * 64)
    Cis = bytearray(32 * 64)
    s0s = bytearray(32 * 64)
    s1s = bytearray(32 * 64)
    buff = bytearray(32)
    ee_bin = bytearray(32)

    a = crypto.sc_init(0)
    si = crypto.sc_init(0)
    c = crypto.sc_init(0)
    ee = crypto.sc_init(0)
    tmp_ai = crypto.sc_init(0)
    tmp_alpha = crypto.sc_init(0)

    C_acc = crypto.identity()
    C_h = crypto.xmr_H()
    C_tmp = crypto.identity()
    L = crypto.identity()
    kck = crypto.get_keccak()

    for ii in range(64):
        crypto.random_scalar(tmp_ai)
        if last_mask is not None and ii == 63:
            crypto.sc_sub_into(tmp_ai, last_mask, a)

        crypto.sc_add_into(a, a, tmp_ai)
        crypto.random_scalar(tmp_alpha)

        crypto.scalarmult_base_into(L, tmp_alpha)
        crypto.scalarmult_base_into(C_tmp, tmp_ai)

        # if 0: C_tmp += Zero (nothing is added)
        # if 1: C_tmp += 2^i*H
        # 2^i*H is already stored in C_h
        if (amount >> ii) & 1 == 1:
            crypto.point_add_into(C_tmp, C_tmp, C_h)

        crypto.point_add_into(C_acc, C_acc, C_tmp)

        # Set Ci[ii] to sigs
        crypto.encodepoint_into(Cis, C_tmp, ii << 5)
        crypto.encodeint_into(ai, tmp_ai, ii << 5)
        crypto.encodeint_into(alphai, tmp_alpha, ii << 5)

        if ((amount >> ii) & 1) == 0:
            crypto.random_scalar(si)
            crypto.encodepoint_into(buff, L)
            crypto.hash_to_scalar_into(c, buff)

            crypto.point_sub_into(C_tmp, C_tmp, C_h)
            crypto.add_keys2_into(L, si, c, C_tmp)

            crypto.encodeint_into(s1s, si, ii << 5)

        crypto.encodepoint_into(buff, L)
        kck.update(buff)

        crypto.point_double_into(C_h, C_h)

    # Compute ee
    tmp_ee = kck.digest()
    crypto.decodeint_into(ee, tmp_ee)
    del (tmp_ee, kck)

    C_h = crypto.xmr_H()
    gc.collect()

    # Second pass, s0, s1
    for ii in range(64):
        crypto.decodeint_into(tmp_alpha, alphai, ii << 5)
        crypto.decodeint_into(tmp_ai, ai, ii << 5)

        if ((amount >> ii) & 1) == 0:
            crypto.sc_mulsub_into(si, tmp_ai, ee, tmp_alpha)
            crypto.encodeint_into(s0s, si, ii << 5)

        else:
            crypto.random_scalar(si)
            crypto.encodeint_into(s0s, si, ii << 5)

            crypto.decodepoint_into(C_tmp, Cis, ii << 5)
            crypto.add_keys2_into(L, si, ee, C_tmp)
            crypto.encodepoint_into(buff, L)
            crypto.hash_to_scalar_into(c, buff)

            crypto.sc_mulsub_into(si, tmp_ai, c, tmp_alpha)
            crypto.encodeint_into(s1s, si, ii << 5)

        crypto.point_double_into(C_h, C_h)

    crypto.encodeint_into(ee_bin, ee)

    del (ai, alphai, buff, tmp_ai, tmp_alpha, si, c, ee, C_tmp, C_h, L)
    gc.collect()

    return C_acc, a, [s0s, s1s, ee_bin, Cis]