Пример #1
0
def ver_asnl(P1, P2, L1, s2, s):
    """
    Aggregate Schnorr Non-Linkable
    :param P1:
    :param P2:
    :param L1:
    :param s2:
    :param s:
    :return:
    """
    logger.info("Verifying Aggregate Schnorr Non-linkable Ring Signature")

    n = len(P1)
    LHS = crypto.scalarmult_base(crypto.sc_0())
    RHS = crypto.scalarmult_base(s)
    for j in range(0, n):
        c2 = crypto.hash_to_scalar(crypto.encodepoint(L1[j]))
        L2 = crypto.point_add(crypto.scalarmult_base(s2[j]),
                              crypto.scalarmult(P2[j], c2))
        LHS = crypto.point_add(LHS, L1[j])
        c1 = crypto.hash_to_scalar(crypto.encodepoint(L2))
        RHS = crypto.point_add(RHS, crypto.scalarmult(P1[j], c1))

    if crypto.point_eq(LHS, RHS):
        return 1
    else:
        logger.warning("Didn't verify L1: %s, L1p: %s" %
                       (crypto.encodepoint(LHS), crypto.encodepoint(RHS)))
        return 0
Пример #2
0
    def test_pointadd(self):
        a = crypto.random_scalar()
        A = crypto.scalarmult_base(a)
        A2 = crypto.point_add(A, A)
        A3 = crypto.point_add(A2, A)
        A4 = crypto.point_add(A3, A)
        A8 = crypto.scalarmult(A4, crypto.sc_init(2))

        A8p = crypto.point_mul8(A)
        self.assertTrue(crypto.point_eq(A8p, A8))
        self.assertTrue(
            crypto.point_eq(A4, crypto.scalarmult(A, crypto.sc_init(4))))
        self.assertTrue(
            crypto.point_eq(A3, crypto.scalarmult(A, crypto.sc_init(3))))
Пример #3
0
def ver_range(C=None, rsig=None, use_asnl=False, decode=True):
    """
    Verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
    :param C:
    :param rsig:
    :param use_asnl: use ASNL, used before Borromean, insecure!
    :param decode: decodes encoded range proof
    :return:
    """
    n = ATOMS
    CiH = [None] * n
    C_tmp = crypto.identity()
    c_H = crypto.gen_H()

    if decode:
        rsig = monero.recode_rangesig(rsig, encode=False, copy=True)

    for i in range(0, n):
        CiH[i] = crypto.point_sub(rsig.Ci[i], c_H)
        C_tmp = crypto.point_add(C_tmp, rsig.Ci[i])
        c_H = crypto.point_double(c_H)

    if C is not None and not crypto.point_eq(C_tmp, C):
        return 0

    if use_asnl:
        return asnl.ver_asnl(rsig.Ci, CiH, rsig.asig.s0, rsig.asig.s1,
                             rsig.asig.ee)
    else:
        return mlsag2.ver_borromean(rsig.Ci, CiH, rsig.asig.s0, rsig.asig.s1,
                                    rsig.asig.ee)
Пример #4
0
def ver_range(C=None, rsig=None, use_bulletproof=False, decode=True):
    """
    Verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
    :param C:
    :param rsig:
    :param use_bulletproof: bulletproof
    :param decode: decodes encoded range proof
    :return:
    """
    n = ATOMS
    CiH = [None] * n
    C_tmp = crypto.identity()
    c_H = crypto.xmr_H()

    if decode and not use_bulletproof:
        rsig = monero.recode_rangesig(rsig, encode=False, copy=True)

    if not use_bulletproof:
        for i in range(0, n):
            CiH[i] = crypto.point_sub(rsig.Ci[i], c_H)
            C_tmp = crypto.point_add(C_tmp, rsig.Ci[i])
            c_H = crypto.point_double(c_H)

        if C is not None and not crypto.point_eq(C_tmp, C):
            return 0

    if use_bulletproof:
        bp = bulletproof.BulletProofBuilder()
        return bp.verify(rsig)

    return mlsag2.ver_borromean(rsig.Ci, CiH, rsig.asig.s0, rsig.asig.s1,
                                rsig.asig.ee)
Пример #5
0
 def test_clsag_invalid_Cp(self):
     res = self.gen_clsag_sig(ring_size=11, index=5)
     msg, scalars, sc1, sI, sD, ring2, Cp = res
     with self.assertRaises(ValueError):
         Cp = crypto.point_add(Cp,
                               crypto.scalarmult_base(crypto.sc_init(1)))
         mlsag2.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
Пример #6
0
def generate_key_image_helper_precomp(
    ack, out_key, recv_derivation, real_output_index, received_index
):
    """
    Generates UTXO spending key and key image.

    :param ack: sender credentials
    :type ack: AccountCreds
    :param out_key: real output (from input RCT) destination key
    :param recv_derivation:
    :param real_output_index:
    :param received_index: subaddress index this payment was received to
    :return:
    """
    if ack.spend_key_private == 0:
        raise ValueError("Watch-only wallet not supported")

    # derive secret key with subaddress - step 1: original CN derivation
    scalar_step1 = crypto.derive_secret_key(
        recv_derivation, real_output_index, ack.spend_key_private
    )

    # step 2: add Hs(SubAddr || a || index_major || index_minor)
    subaddr_sk = None
    scalar_step2 = None
    if received_index == (0, 0):
        scalar_step2 = scalar_step1
    else:
        subaddr_sk = get_subaddress_secret_key(
            ack.view_key_private, major=received_index[0], minor=received_index[1]
        )
        scalar_step2 = crypto.sc_add(scalar_step1, subaddr_sk)

    # when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
    if len(ack.multisig_keys) == 0:
        pub_ver = crypto.scalarmult_base(scalar_step2)

    else:
        # When in multisig, we only know the partial spend secret key. But we do know the full spend public key,
        # so the output pubkey can be obtained by using the standard CN key derivation.
        pub_ver = crypto.derive_public_key(
            recv_derivation, real_output_index, ack.spend_key_public
        )

        # Add the contribution from the subaddress part
        if received_index != (0, 0):
            subaddr_pk = crypto.scalarmult_base(subaddr_sk)
            pub_ver = crypto.point_add(pub_ver, subaddr_pk)

    if not crypto.point_eq(pub_ver, out_key):
        raise ValueError(
            "key image helper precomp: given output pubkey doesn't match the derived one"
        )

    ki = generate_key_image(crypto.encodepoint(pub_ver), scalar_step2)
    return scalar_step2, ki
Пример #7
0
def sum_Ci(Cis):
    """
    Sums points
    :param Cis:
    :return:
    """
    CSum = crypto.identity()
    for i in Cis:
        CSum = crypto.point_add(CSum, i)
    return CSum
Пример #8
0
    def test_ge25519_double_scalarmult_vartime(self):
        for i in range(10):
            ap = crypto.random_scalar()
            A = crypto.scalarmult_base(ap)
            a = crypto.random_scalar()
            b = crypto.random_scalar()

            R = crypto.ge_double_scalarmult_base_vartime(a, A, b)
            R_exp = crypto.point_add(crypto.scalarmult(A, a), crypto.scalarmult_base(b))
            self.assertTrue(crypto.point_eq(R, R_exp))
Пример #9
0
    def test_range_proof2_back(self):
        proof = ring_ct.prove_range(123456789, backend_impl=True)
        res = ring_ct.ver_range(proof[0], proof[2])
        self.assertTrue(res)

        res = ring_ct.ver_range(
            crypto.point_add(proof[0], crypto.scalarmult_base(crypto.sc_init(4))),
            proof[2],
        )
        self.assertFalse(res)
Пример #10
0
    def test_range_proof(self):
        proof = ring_ct.prove_range(0)
        res = ring_ct.ver_range(proof[0], proof[2])
        self.assertTrue(res)
        self.assertTrue(
            crypto.point_eq(
                proof[0],
                crypto.point_add(crypto.scalarmult_base(proof[1]),
                                 crypto.scalarmult_h(0)),
            ))

        proof = ring_ct.prove_range(0, mem_opt=False)
        res = ring_ct.ver_range(proof[0], proof[2])
        self.assertTrue(res)
        self.assertTrue(
            crypto.point_eq(
                proof[0],
                crypto.point_add(crypto.scalarmult_base(proof[1]),
                                 crypto.scalarmult_h(0)),
            ))
Пример #11
0
def get_subaddress_spend_public_key(view_private, spend_public, major, minor):
    """
    Generates subaddress spend public key D_{major, minor}
    """
    if major == 0 and minor == 0:
        return spend_public

    m = get_subaddress_secret_key(view_private, major=major, minor=minor)
    M = crypto.scalarmult_base(m)
    D = crypto.point_add(spend_public, M)
    return D
Пример #12
0
def get_subaddress_spend_public_key(view_private, spend_public, major, minor):
    """
    Generates subaddress spend public key D_{major, minor}
    :param view_private:
    :param spend_public:
    :param major:
    :param minor:
    :return:
    """
    m = get_subaddress_secret_key(view_private, major=major, minor=minor)
    M = crypto.scalarmult_base(m)
    D = crypto.point_add(spend_public, M)
    return D
Пример #13
0
    async def mlsag_prehash_update(self):
        is_subaddress = 0
        Aout = None
        Bout = None
        C = None
        v = None
        k = None
        changed = 0

        is_subaddress = bool(self._fetch_u8())
        Aout = crypto.decodepoint(self._fetch())
        Bout = crypto.decodepoint(self._fetch())
        if self.sig_mode == TRANSACTION_CREATE_REAL:
            if crypto.point_eq(Aout, self.A) and crypto.point_eq(Bout, self.B):
                changed = 1

        AKout = self._fetch_decrypt()
        C = self._fetch()
        k = self._fetch()
        v = self._fetch()

        self.ctx_h.update(k)
        self.ctx_h.update(v)

        self.ctx_amount.update(AKout)

        v, k, AKout = self.unblind_int(v, k, AKout)
        self.ctx_amount.update(crypto.encodeint(k))
        self.ctx_amount.update(crypto.encodeint(v))

        vH = crypto.scalarmult_h(v)
        kG = crypto.scalarmult_base(k)
        k = crypto.point_add(kG, vH)

        if not crypto.point_eq(k, crypto.decodepoint(C)):
            raise ValueError(SW_SECURITY_COMMITMENT_CONTROL)

        self.ctx_commitment.update(C)
        if self.options & IN_OPTION_MORE_COMMAND == 0:
            k = self.ctx_amount.digest()
            if not common.ct_equal(k, self.KV):
                raise ValueError(SW_SECURITY_AMOUNT_CHAIN_CONTROL)

            self.C = self.ctx_commitment.digest()
            self.ctx_commitment = sha256()

        if self.sig_mode == TRANSACTION_CREATE_REAL:
            if not changed:
                await self._req_dst(Aout, Bout, v, is_subaddress)

        return SW_OK
Пример #14
0
def prove_range_orig(amount, last_mask=None, use_asnl=False):
    """
    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, insecure
    :return: sumCi, mask, RangeSig.
        sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H
        mask is "a" from the Pedersent commitment above.
    """
    bb = d2b(amount,
             ATOMS)  # gives binary form of bb in "digits" binary digits
    logger.info("amount, amount in binary %s %s" % (amount, bb))
    ai = [None] * len(bb)
    Ci = [None] * len(bb)
    CiH = [None] * len(bb)  # this is like Ci - 2^i H
    H2 = crypto.gen_Hpow(ATOMS)
    a = crypto.sc_0()
    for i in range(0, ATOMS):
        ai[i] = crypto.random_scalar()
        if last_mask is not None and i == ATOMS - 1:
            ai[i] = crypto.sc_sub(last_mask, a)

        a = crypto.sc_add(
            a, ai[i]
        )  # creating the total mask since you have to pass this to receiver...
        if bb[i] == 0:
            Ci[i] = crypto.scalarmult_base(ai[i])
        if bb[i] == 1:
            Ci[i] = crypto.point_add(crypto.scalarmult_base(ai[i]), H2[i])
        CiH[i] = crypto.point_sub(Ci[i], H2[i])

    A = xmrtypes.BoroSig()

    if use_asnl:
        A.s0, A.s1, A.ee = asnl.gen_asnl(ai, Ci, CiH, bb)
    else:
        A.s0, A.s1, A.ee = mlsag2.gen_borromean(ai, Ci, CiH, bb)

    R = xmrtypes.RangeSig()
    R.asig = A
    R.Ci = Ci

    C = sum_Ci(Ci)
    return C, a, R
Пример #15
0
def ver_schnorr_non_linkable(P1, P2, L1, s1, s2):
    """
    Verifies Schnorr signature generated by gen_schnorr_non_linkable
    :param P1:
    :param P2:
    :param L1:
    :param s1:
    :param s2:
    :return:
    """
    c2 = crypto.cn_fast_hash(crypto.encodepoint(L1))
    L2 = crypto.point_add(crypto.scalarmult_base(s2),
                          crypto.scalarmult(P2, crypto.decodeint(c2)))
    c1 = crypto.cn_fast_hash(L2)
    L1p = crypto.point_add(crypto.scalarmult_base(s1),
                           crypto.scalarmult(P1, crypto.decodeint(c1)))

    if L1 == L1p:
        return 0

    else:
        logger.warning("Didn't verify L1: %s, L1p: %s" % (L1, L1p))
        return -1
Пример #16
0
    def test_range_proof2(self):
        amount = 17 + (1 << 60)
        proof = ring_ct.prove_range(amount)
        res = ring_ct.ver_range(proof[0], proof[2])
        self.assertTrue(res)
        self.assertTrue(
            crypto.point_eq(
                proof[0],
                crypto.point_add(crypto.scalarmult_base(proof[1]),
                                 crypto.scalarmult_h(amount)),
            ))

        proof = ring_ct.prove_range(amount, mem_opt=False, decode=True)
        res = ring_ct.ver_range(proof[0], proof[2], decode=False)
        self.assertTrue(res)

        res = ring_ct.ver_range(
            crypto.point_add(proof[0],
                             crypto.scalarmult_base(crypto.sc_init(4))),
            proof[2],
            decode=False,
        )
        self.assertFalse(res)
Пример #17
0
def generate_sub_address_keys(view_sec, spend_pub, major, minor):
    """
    Computes generic public sub-address
    :param view_sec:
    :param spend_pub:
    :param major:
    :param minor:
    :return: spend public, view public
    """
    if major == 0 and minor == 0:  # special case, Monero-defined
        return spend_pub, crypto.scalarmult_base(view_sec)

    m = get_subaddress_secret_key(view_sec, major=major, minor=minor)
    M = crypto.scalarmult_base(m)
    D = crypto.point_add(spend_pub, M)
    C = crypto.scalarmult(D, view_sec)
    return D, C
Пример #18
0
 def test_range_proof2_old(self):
     proof = ring_ct.prove_range(123456789,
                                 use_asnl=True,
                                 mem_opt=False,
                                 decode=True)
     res = ring_ct.ver_range(proof[0],
                             proof[2],
                             use_asnl=True,
                             decode=False)
     self.assertTrue(res)
     res = ring_ct.ver_range(
         crypto.point_add(proof[0],
                          crypto.scalarmult_base(crypto.sc_init(4))),
         proof[2],
         use_asnl=True,
         decode=False,
     )
     self.assertFalse(res)
Пример #19
0
def decode_rct(rv, sk, i):
    """
    c.f. http:#eprint.iacr.org/2015/1098 section 5.1.1
    Uses the attached ecdh info to find the amounts represented by each output commitment
    must know the destination private key to find the correct amount, else will return a random number

    :param rv:
    :param sk:
    :param i:
    :return:
    """
    decodedTuple = ecdh_decode(rv.ecdhInfo[i], sk)
    mask = decodedTuple.mask
    amount = decodedTuple.amount
    C = rv.outPk[i].mask
    H = crypto.xmr_H()
    Ctmp = crypto.point_add(crypto.scalarmult_base(mask), crypto.scalarmult(H, amount))
    if not crypto.point_eq(crypto.point_sub(C, Ctmp), crypto.identity()):
        logger.warning("warning, amount decoded incorrectly, will be unable to spend")
    return amount
Пример #20
0
def ver_rct_mg(mg, pubs, out_pk, txn_fee_key, message):
    """
    Verifies the above sig is created corretly
    :param mg:
    :param pubs: matrix of EC points, encoded
    :param out_pk:
    :param txn_fee_key:
    :param message:
    :return:
    """
    cols = len(pubs)
    if cols == 0:
        raise ValueError("Empty pubs")
    rows = len(pubs[0])
    if rows == 0:
        raise ValueError("Empty pubs[0]")
    for i in range(cols):
        if len(pubs[i]) != rows:
            raise ValueError("pubs is not rectangular")

    M = key_matrix(rows + 1, cols)
    for i in range(cols):
        M[i][rows] = crypto.identity()

    for j in range(rows):
        for i in range(cols):
            M[i][j] = crypto.decodepoint(pubs[i][j].dest)
            M[i][rows] = crypto.point_add(
                M[i][rows], crypto.decodepoint(
                    pubs[i][j].commitment))  # add Ci in last row

    for i in range(cols):
        for j in range(len(out_pk)):
            M[i][rows] = crypto.point_sub(
                M[i][rows], crypto.decodepoint(
                    out_pk[j].mask))  # subtract output Ci's in last row

        # subtract txn fee output in last row
        M[i][rows] = crypto.point_sub(M[i][rows], txn_fee_key)

    return ver_mlsag_ext(message, M, mg, rows)
Пример #21
0
def gen_schnorr_non_linkable(x, P1, P2, index):
    """
    Generates simple Schnorr
    :param x:
    :param P1:
    :param P2:
    :param index:
    :return:
    """
    a = crypto.random_scalar()
    L1 = crypto.scalarmult_base(a)
    s2 = crypto.random_scalar()
    c2 = crypto.cn_fast_hash(crypto.encodepoint(L1))
    L2 = crypto.point_add(
        crypto.scalarmult_base(s2),
        crypto.scalarmult(P2 if index == 0 else P1, crypto.decodeint(c2)),
    )
    c1 = crypto.cn_fast_hash(crypto.encodepoint(L2))
    s1 = crypto.sc_mulsub(x, crypto.decodeint(c1), a)

    return (L1, s1, s2) if index == 0 else (L2, s2, s1)
Пример #22
0
    async def test_node_transaction(self):
        tx_j = pkg_resources.resource_string(
            __name__, os.path.join("data", "tsx_01.json"))
        tx_c = pkg_resources.resource_string(
            __name__, os.path.join("data", "tsx_01_plain.txt"))
        tx_u_c = pkg_resources.resource_string(
            __name__, os.path.join("data", "tsx_01_uns.txt"))
        tx_js = json.loads(tx_j.decode("utf8"))

        reader = xmrserialize.MemoryReaderWriter(
            bytearray(binascii.unhexlify(tx_c)))
        ar = xmrserialize.Archive(reader, False, self._get_bc_ver())
        tx = xmrtypes.Transaction()
        await ar.message(tx)

        reader = xmrserialize.MemoryReaderWriter(
            bytearray(binascii.unhexlify(tx_u_c)))
        ar = xmrserialize.Archive(reader, False, self._get_bc_ver())
        uns = xmrtypes.UnsignedTxSet()
        await ar.message(uns)

        # Test message hash computation
        tx_prefix_hash = await monero.get_transaction_prefix_hash(tx)
        message = binascii.unhexlify(tx_js["tx_prefix_hash"])
        self.assertEqual(tx_prefix_hash, message)

        # RingCT, range sigs, hash
        rv = tx.rct_signatures
        rv.message = message
        rv.mixRing = self.mixring(tx_js)
        digest = await monero.get_pre_mlsag_hash(rv)
        full_message = binascii.unhexlify(tx_js["pre_mlsag_hash"])
        self.assertEqual(digest, full_message)

        # Recompute missing data
        monero.expand_transaction(tx)

        # Unmask ECDH data, check range proofs
        for i in range(len(tx_js["amount_keys"])):
            ecdh = monero.copy_ecdh(rv.ecdhInfo[i])
            monero.recode_ecdh(ecdh, encode=False)

            ecdh = ring_ct.ecdh_decode(ecdh,
                                       derivation=binascii.unhexlify(
                                           tx_js["amount_keys"][i]))
            self.assertEqual(crypto.sc_get64(ecdh.amount),
                             tx_js["outamounts"][i])
            self.assertTrue(
                crypto.sc_eq(
                    ecdh.mask,
                    crypto.decodeint(
                        binascii.unhexlify(tx_js["outSk"][i])[32:]),
                ))

            C = crypto.decodepoint(rv.outPk[i].mask)
            rsig = rv.p.rangeSigs[i]

            res = ring_ct.ver_range(C, rsig)
            self.assertTrue(res)

            res = ring_ct.ver_range(
                crypto.point_add(C, crypto.scalarmult_base(crypto.sc_init(3))),
                rsig)
            self.assertFalse(res)

        is_simple = len(tx.vin) > 1
        monero.recode_rct(rv, encode=False)

        if is_simple:
            for index in range(len(rv.p.MGs)):
                pseudo_out = crypto.decodepoint(
                    binascii.unhexlify(
                        tx_js["tx"]["rct_signatures"]["pseudoOuts"][index]))
                r = mlsag2.ver_rct_mg_simple(full_message, rv.p.MGs[index],
                                             rv.mixRing[index], pseudo_out)
                self.assertTrue(r)

                r = mlsag2.ver_rct_mg_simple(full_message, rv.p.MGs[index],
                                             rv.mixRing[index - 1], pseudo_out)
                self.assertFalse(r)

        else:
            txn_fee_key = crypto.scalarmult_h(rv.txnFee)
            r = mlsag2.ver_rct_mg(rv.p.MGs[0], rv.mixRing, rv.outPk,
                                  txn_fee_key, digest)
            self.assertTrue(r)

            r = mlsag2.ver_rct_mg(
                rv.p.MGs[0],
                rv.mixRing,
                rv.outPk,
                crypto.scalarmult_h(rv.txnFee - 100),
                digest,
            )
            self.assertFalse(r)
Пример #23
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
    :return: sumCi, mask, RangeSig.
        sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H
        mask is "a" from the Pedersent commitment above.
    """
    n = ATOMS
    bb = d2b(amount, n)  # gives binary form of bb in "digits" binary digits
    ai = [None] * len(bb)
    Ci = [None] * len(bb)
    a = crypto.sc_0()

    C = crypto.identity()
    alpha = mlsag2.key_zero_vector(n)
    s1 = mlsag2.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 == ATOMS - 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:
            Ci[ii] = crypto.scalarmult_base(ai[ii])
        else:
            Ci[ii] = crypto.point_add(crypto.scalarmult_base(ai[ii]), c_H)
        C = crypto.point_add(C, Ci[ii])

        if bb[ii] == 0:
            s1[ii] = crypto.random_scalar()
            c = crypto.hash_to_scalar(crypto.encodepoint(L))
            L = crypto.add_keys2(s1[ii], c, crypto.point_sub(Ci[ii], c_H))
            kck.update(crypto.encodepoint(L))

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

        c_H = crypto.point_double(c_H)

    # Compute ee, memory cleanup
    ee = crypto.decodeint(kck.digest())
    del kck

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

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

        else:
            s0[jj] = crypto.random_scalar()
            LL = crypto.add_keys2(s0[jj], ee, Ci[jj])
            cc = crypto.hash_to_scalar(crypto.encodepoint(LL))
            s1[jj] = crypto.sc_mulsub(ai[jj], cc, alpha[jj])
        c_H = crypto.point_double(c_H)

    A = xmrtypes.BoroSig()
    A.s0, A.s1, A.ee = s0, s1, ee

    R = xmrtypes.RangeSig()
    R.asig = A
    R.Ci = Ci

    return C, a, R
Пример #24
0
def prove_rct_mg(message, pubs, in_sk, out_sk, out_pk, kLRki, mscout, index,
                 txn_fee_key):
    """
    c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
    This does the MG sig on the "dest" part of the given key matrix, and
    the last row is the sum of input commitments from that column - sum output commitments
    this shows that sum inputs = sum outputs
    :param message:
    :param pubs: matrix of CtKeys. points are encoded.
    :param in_sk:
    :param out_sk:
    :param out_pk:
    :param kLRki:
    :param mscout:
    :param index:
    :param txn_fee_key:
    :return:
    """
    cols = len(pubs)
    if cols == 0:
        raise ValueError("Empty pubs")
    rows = len(pubs[0])
    if rows == 0:
        raise ValueError("Empty pub row")
    for i in range(cols):
        if len(pubs[i]) != rows:
            raise ValueError("pub is not rectangular")

    if len(in_sk) != rows:
        raise ValueError("Bad inSk size")
    if len(out_sk) != len(out_pk):
        raise ValueError("Bad outsk/putpk size")
    if (not kLRki or not mscout) and (kLRki and mscout):
        raise ValueError("Only one of kLRki/mscout is present")

    sk = key_vector(rows + 1)
    M = key_matrix(rows + 1, cols)
    for i in range(rows + 1):
        sk[i] = crypto.sc_0()

    for i in range(cols):
        M[i][rows] = crypto.identity()
        for j in range(rows):
            M[i][j] = crypto.decodepoint(pubs[i][j].dest)
            M[i][rows] = crypto.point_add(
                M[i][rows], crypto.decodepoint(pubs[i][j].commitment))

    sk[rows] = crypto.sc_0()
    for j in range(rows):
        sk[j] = in_sk[j].dest
        sk[rows] = crypto.sc_add(sk[rows],
                                 in_sk[j].mask)  # add masks in last row

    for i in range(cols):
        for j in range(len(out_pk)):
            M[i][rows] = crypto.point_sub(
                M[i][rows], crypto.decodepoint(
                    out_pk[j].mask))  # subtract output Ci's in last row

        # Subtract txn fee output in last row
        M[i][rows] = crypto.point_sub(M[i][rows], txn_fee_key)

    for j in range(len(out_pk)):
        sk[rows] = crypto.sc_sub(
            sk[rows], out_sk[j].mask)  # subtract output masks in last row

    return gen_mlsag_ext(message, M, sk, kLRki, mscout, index, rows)
Пример #25
0
def generate_sub_address_keys(view_sec, spend_pub, major, minor):
    m = get_subaddress_secret_key(view_sec, major=major, minor=minor)
    M = crypto.scalarmult_base(m)
    D = crypto.point_add(spend_pub, M)
    C = crypto.ge_scalarmult(view_sec, D)
    return D, C
Пример #26
0
    async def gen_rct_header(self, destinations, outamounts):
        """
        Initializes RV RctSig structure, processes outputs, computes range proofs, ecdh info masking.
        Common to gen_rct and gen_rct_simple.

        :param destinations:
        :param outamounts:
        :return:
        """
        rv = xmrtypes.RctSig()
        rv.p = xmrtypes.RctSigPrunable()

        rv.message = self.tx_prefix_hash
        rv.outPk = [None] * len(destinations)

        if self.use_bulletproof:
            rv.p.bulletproofs = [None] * len(destinations)
        else:
            rv.p.rangeSigs = [None] * len(destinations)
        rv.ecdhInfo = [None] * len(destinations)

        # Output processing
        sumout = crypto.sc_0()
        out_sk = [None] * len(destinations)
        for idx in range(len(destinations)):
            rv.outPk[idx] = xmrtypes.CtKey(dest=crypto.encodepoint(destinations[idx]))
            C, mask, rsig = None, 0, None

            # Rangeproof
            if self.use_bulletproof:
                raise ValueError("Bulletproof not yet supported")

            else:
                C, mask, rsig = ring_ct.prove_range(outamounts[idx])
                rv.p.rangeSigs[idx] = rsig
                if __debug__:
                    assert ring_ct.ver_range(C, rsig)
                    assert crypto.point_eq(
                        C,
                        crypto.point_add(
                            crypto.scalarmult_base(mask),
                            crypto.scalarmult_h(outamounts[idx]),
                        ),
                    )

            # Mask sum
            rv.outPk[idx].mask = crypto.encodepoint(C)
            sumout = crypto.sc_add(sumout, mask)
            out_sk[idx] = xmrtypes.CtKey(mask=mask)

            # ECDH masking
            amount_key = crypto.encodeint(self.output_secrets[idx][0])
            rv.ecdhInfo[idx] = xmrtypes.EcdhTuple(
                mask=mask, amount=crypto.sc_init(outamounts[idx])
            )
            rv.ecdhInfo[idx] = ring_ct.ecdh_encode(
                rv.ecdhInfo[idx], derivation=amount_key
            )
            monero.recode_ecdh(rv.ecdhInfo[idx], encode=True)

        return rv, sumout, out_sk
Пример #27
0
    def rekey_input(self, inp, keys, subs=None, new_keys=None, new_subs=None, mixin_change=None):
        subs = subs if subs else {}
        real_out_key = inp.outputs[inp.real_output][1]
        out_key = crypto.decodepoint(real_out_key.dest)
        tx_key = crypto.decodepoint(inp.real_out_tx_key)
        additional_keys = [
            crypto.decodepoint(x) for x in inp.real_out_additional_tx_keys
        ]

        logger.debug("Current out key: %s" % binascii.hexlify(real_out_key.dest))
        secs = monero.generate_key_image_helper(
            keys, subs, out_key, tx_key, additional_keys, inp.real_output_in_tx_index
        )
        xi, ki, di = secs

        need_additional = additional_keys is not None and len(additional_keys) > 0
        is_dst_sub = self.dest_sub_major != 0 and (
            self.args.minors[0] != 0 or len(self.args.minors) > 1
        )
        logger.debug(
            "Is dst sub: %s, need additional: %s" % (is_dst_sub, need_additional)
        )

        if is_dst_sub and self.add_additionals:
            need_additional = True

        if is_dst_sub:
            rand_minor = random.choice(self.args.minors)
            m = monero.get_subaddress_secret_key(
                new_keys.view_key_private, major=self.dest_sub_major, minor=rand_minor
            )
            M = crypto.scalarmult_base(m)
            d = crypto.sc_add(m, new_keys.spend_key_private)
            D = crypto.point_add(new_keys.spend_key_public, M)
            C = crypto.scalarmult(D, new_keys.view_key_private)

        if not need_additional and not is_dst_sub:
            # real_out_key.dst = Hs(R*new_a || idx)G + newB
            r = crypto.random_scalar()
            tx_key = crypto.scalarmult_base(r)
            new_deriv = crypto.generate_key_derivation(new_keys.view_key_public, r)
            new_out_pr = crypto.derive_secret_key(
                new_deriv, inp.real_output_in_tx_index, new_keys.spend_key_private
            )
            new_out = crypto.scalarmult_base(new_out_pr)
            real_out_key.dest = crypto.encodepoint(new_out)

        elif not need_additional and is_dst_sub:
            # real_out_key.dst = Hs(r*C || idx)G + newB, R=rD
            r = crypto.random_scalar()
            tx_key = crypto.scalarmult(D, r)
            new_deriv = crypto.generate_key_derivation(C, r)
            new_out_pr = crypto.derive_secret_key(
                new_deriv, inp.real_output_in_tx_index, d
            )
            new_out = crypto.scalarmult_base(new_out_pr)
            real_out_key.dest = crypto.encodepoint(new_out)

        else:
            r = crypto.random_scalar()
            tx_key = crypto.scalarmult_base(r)

            gen_additionals = min(2, inp.real_output_in_tx_index + 1)
            if additional_keys is None or len(additional_keys) < gen_additionals:
                additional_keys = [
                    crypto.scalarmult_base(crypto.random_scalar())
                    for _ in range(gen_additionals)
                ]

            ri = crypto.random_scalar()
            if is_dst_sub:
                add_tx = crypto.scalarmult(D, ri)
                new_deriv = crypto.generate_key_derivation(C, ri)
                new_out_pr = crypto.derive_secret_key(
                    new_deriv, inp.real_output_in_tx_index, d
                )
                new_out = crypto.scalarmult_base(new_out_pr)
                if not crypto.point_eq(
                    new_out,
                    crypto.derive_public_key(new_deriv, inp.real_output_in_tx_index, D),
                ):
                    raise ValueError("Invalid txout computation")

            else:
                add_tx = crypto.scalarmult_base(ri)
                new_deriv = crypto.generate_key_derivation(new_keys.view_key_public, r)
                new_out_pr = crypto.derive_secret_key(
                    new_deriv, inp.real_output_in_tx_index, new_keys.spend_key_private
                )
                new_out = crypto.scalarmult_base(new_out_pr)

            additional_keys[inp.real_output_in_tx_index] = add_tx
            real_out_key.dest = crypto.encodepoint(new_out)

        # Increasing the size of the mixin
        if mixin_change and len(inp.outputs) < mixin_change:
            for i in range(mixin_change - len(inp.outputs)):
                inp.outputs.append((0, CtKey(
                    mask=crypto.encodepoint(self.random_pub()),
                    dest=crypto.encodepoint(self.random_pub()))))
                if additional_keys:
                    additional_keys.append(self.random_pub())

        inp.real_out_tx_key = crypto.encodepoint(tx_key)
        inp.real_out_additional_tx_keys = [
            crypto.encodepoint(x) for x in additional_keys
        ]

        logger.debug("New pub: %s" % binascii.hexlify(real_out_key.dest))

        # Self-check
        self.check_input(inp, new_keys, new_subs)
        return inp
Пример #28
0
    async def range_proof(self, idx, dest_pub_key, amount, amount_key):
        """
        Computes rangeproof and related information - out_sk, out_pk, ecdh_info.
        In order to optimize incremental transaction build, the mask computation is changed compared
        to the official Monero code. In the official code, the input pedersen commitments are computed
        after range proof in such a way summed masks for commitments (alpha) and rangeproofs (ai) are equal.

        In order to save roundtrips we compute commitments randomly and then for the last rangeproof
        a[63] = (\\sum_{i=0}^{num_inp}alpha_i - \\sum_{i=0}^{num_outs-1} amasks_i) - \\sum_{i=0}^{62}a_i

        The range proof is incrementally hashed to the final_message.

        :param idx:
        :param dest_pub_key:
        :param amount:
        :param amount_key:
        :return:
        """
        from monero_glue.xmr import ring_ct

        rsig = bytearray(32 * (64 + 64 + 64 + 1))
        rsig_mv = memoryview(rsig)

        out_pk = misc.StdObj(dest=dest_pub_key, mask=None)
        is_last = idx + 1 == self.num_dests()
        last_mask = (
            None
            if not is_last or not self.use_simple_rct
            else crypto.sc_sub(self.sumpouts_alphas, self.sumout)
        )

        # Pedersen commitment on the value, mask from the commitment, range signature.
        C, mask, rsig = None, 0, None

        # Rangeproof
        gc.collect()
        if self.use_bulletproof:
            raise ValueError("Bulletproof not yet supported")

        else:
            C, mask, rsig = ring_ct.prove_range(
                amount, last_mask, backend_impl=True, byte_enc=True, rsig=rsig_mv
            )
            rsig = memoryview(rsig)

            if __debug__:
                rsig_bytes = monero.inflate_rsig(rsig)
                self.assrt(ring_ct.ver_range(C, rsig_bytes))

            self.assrt(
                crypto.point_eq(
                    C,
                    crypto.point_add(
                        crypto.scalarmult_base(mask), crypto.scalarmult_h(amount)
                    ),
                ),
                "rproof",
            )

            # Incremental hashing
            await self.full_message_hasher.rsig_val(
                rsig, self.use_bulletproof, raw=True
            )
        gc.collect()
        self._log_trace("rproof")

        # Mask sum
        out_pk.mask = crypto.encodepoint(C)
        self.sumout = crypto.sc_add(self.sumout, mask)
        self.output_sk.append(misc.StdObj(mask=mask))

        # ECDH masking
        from monero_glue.xmr.sub.recode import recode_ecdh
        from monero_serialize.xmrtypes import EcdhTuple

        ecdh_info = EcdhTuple(mask=mask, amount=crypto.sc_init(amount))
        ecdh_info = ring_ct.ecdh_encode(
            ecdh_info, derivation=crypto.encodeint(amount_key)
        )
        recode_ecdh(ecdh_info, encode=True)
        gc.collect()

        return rsig, out_pk, ecdh_info