Example #1
0
def ver_borromean(P1, P2, s0, s1, ee):
    """
    Verify range proof signature, Borromean
    (c.f. gmax/andytoshi's paper)
    :param P1:
    :param P2:
    :param s0:
    :param s1:
    :param ee:
    :return:
    """
    n = len(P1)
    Lv1 = key_vector(n)
    for ii in range(n):
        LL = crypto.add_keys2(s0[ii], ee, P1[ii])
        chash = crypto.hash_to_scalar(crypto.encodepoint(LL))
        Lv1[ii] = crypto.add_keys2(s1[ii], chash, P2[ii])

    kck = crypto.get_keccak()
    for ii in range(n):
        kck.update(crypto.encodepoint(Lv1[ii]))

    # ee_computed = crypto.hash_to_scalar(crypto.encodepoint(Lv1))
    ee_computed = crypto.decodeint(kck.digest())

    return crypto.sc_eq(ee_computed, ee)
Example #2
0
 def test_hash_to_scalar(self):
     inp = unhexlify(
         b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff6405"
     )
     res = crypto.hash_to_scalar(inp)
     exp = crypto.decodeint(
         binascii.unhexlify(
             b"9907925b254e12162609fc0dfd0fef2aa4d605b0d10e6507cac253dd31a3ec06"
         ))
     self.assertTrue(crypto.sc_eq(res, exp))
Example #3
0
 def test_hash_to_scalar(self):
     inp = bytes(
         [
             0x25,
             0x9e,
             0xf2,
             0xab,
             0xa8,
             0xfe,
             0xb4,
             0x73,
             0xcf,
             0x39,
             0x05,
             0x8a,
             0x0f,
             0xe3,
             0x0b,
             0x9f,
             0xf6,
             0xd2,
             0x45,
             0xb4,
             0x2b,
             0x68,
             0x26,
             0x68,
             0x7e,
             0xbd,
             0x6b,
             0x63,
             0x12,
             0x8a,
             0xff,
             0x64,
             0x05,
         ]
     )
     res = crypto.hash_to_scalar(inp)
     exp = self.init_sc(
         0x6eca331dd53c2ca07650ed1b005d6a42aef0ffd0dfc092616124e255b920799
     )
     self.assertTrue(crypto.sc_eq(res, exp))
Example #4
0
def verify_clsag(msg, ss, sc1, sI, sD, pubs, C_offset):
    n = len(pubs)
    c = crypto.new_scalar()
    D_8 = crypto.new_point()
    tmp_bf = bytearray(32)
    C_offset_bf = crypto.encodepoint(C_offset)

    crypto.sc_copy(c, sc1)
    crypto.point_mul8_into(D_8, sD)

    hsh_P = crypto.get_keccak()  # domain, I, D, P, C, C_offset
    hsh_C = crypto.get_keccak()  # domain, I, D, P, C, C_offset
    hsh_P.update(_HASH_KEY_CLSAG_AGG_0)
    hsh_C.update(_HASH_KEY_CLSAG_AGG_1)

    def hsh_PC(x):
        hsh_P.update(x)
        hsh_C.update(x)

    for x in pubs:
        hsh_PC(x.dest)

    for x in pubs:
        hsh_PC(x.commitment)

    hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
    hsh_PC(crypto.encodepoint_into(tmp_bf, sD))
    hsh_PC(C_offset_bf)
    mu_P = crypto.decodeint(hsh_P.digest())
    mu_C = crypto.decodeint(hsh_C.digest())

    c_to_hash = crypto.get_keccak()  # domain, P, C, C_offset, message, L, R
    c_to_hash.update(_HASH_KEY_CLSAG_ROUND)
    for i in range(len(pubs)):
        c_to_hash.update(pubs[i].dest)
    for i in range(len(pubs)):
        c_to_hash.update(pubs[i].commitment)
    c_to_hash.update(C_offset_bf)
    c_to_hash.update(msg)

    c_p = crypto.new_scalar()
    c_c = crypto.new_scalar()
    L = crypto.new_point()
    R = crypto.new_point()
    tmp_pt = crypto.new_point()
    i = 0
    while i < n:
        crypto.sc_mul_into(c_p, mu_P, c)
        crypto.sc_mul_into(c_c, mu_C, c)

        C_P = crypto.point_sub(
            crypto.decodepoint_into(tmp_pt, pubs[i].commitment), C_offset)
        crypto.add_keys2_into(L, ss[i], c_p,
                              crypto.decodepoint_into(tmp_pt, pubs[i].dest))
        crypto.point_add_into(L, L, crypto.scalarmult_into(tmp_pt, C_P, c_c))

        HP = crypto.hash_to_point(pubs[i].dest)
        crypto.add_keys3_into(R, ss[i], HP, c_p, sI)
        crypto.point_add_into(R, R, crypto.scalarmult_into(tmp_pt, D_8, c_c))

        chasher = c_to_hash.copy()
        chasher.update(crypto.encodepoint_into(tmp_bf, L))
        chasher.update(crypto.encodepoint_into(tmp_bf, R))
        crypto.decodeint_into(c, chasher.digest())
        i += 1
    res = crypto.sc_sub(c, sc1)
    if not crypto.sc_eq(res, crypto.sc_0()):
        raise ValueError("Signature error")
Example #5
0
 def __eq__(self, other):
     return crypto.sc_eq(self._key, other._key)
    async def all_out1_set(self):
        """
        All outputs were set in this phase. Computes additional public keys (if needed), tx.extra and
        transaction prefix hash.
        Adds additional public keys to the tx.extra

        :return: tx.extra, tx_prefix_hash
        """
        self._log_trace(0)
        self.state.set_output_done()
        await self.trezor.iface.transaction_step(self.STEP_ALL_OUT)
        self._log_trace(1)

        if self.out_idx + 1 != self.num_dests():
            raise ValueError("Invalid out num")

        # Test if \sum Alpha == \sum A
        if self.use_simple_rct:
            self.assrt(crypto.sc_eq(self.sumout, self.sumpouts_alphas))

        # Fee test
        if self.fee != (self.summary_inputs_money - self.summary_outs_money):
            raise ValueError(
                "Fee invalid %s vs %s, out: %s"
                % (
                    self.fee,
                    self.summary_inputs_money - self.summary_outs_money,
                    self.summary_outs_money,
                )
            )
        self._log_trace(2)

        # Set public key to the extra
        # Not needed to remove - extra is clean
        await self.all_out1_set_tx_extra()
        self.additional_tx_public_keys = None

        gc.collect()
        self._log_trace(3)

        if self.summary_outs_money > self.summary_inputs_money:
            raise ValueError(
                "Transaction inputs money (%s) less than outputs money (%s)"
                % (self.summary_inputs_money, self.summary_outs_money)
            )

        # Hashing transaction prefix
        await self.all_out1_set_tx_prefix()
        extra_b = self.tx.extra
        self.tx = None
        gc.collect()
        self._log_trace(4)

        # Txprefix match check for multisig
        if not common.is_empty(self.exp_tx_prefix_hash) and not common.ct_equal(
            self.exp_tx_prefix_hash, self.tx_prefix_hash
        ):
            self.state.set_fail()
            raise misc.TrezorTxPrefixHashNotMatchingError("Tx prefix invalid")

        gc.collect()
        self._log_trace(5)

        from monero_glue.messages.MoneroRingCtSig import MoneroRingCtSig
        from monero_glue.messages.MoneroTransactionAllOutSetAck import (
            MoneroTransactionAllOutSetAck
        )

        rv = self.init_rct_sig()
        rv_pb = MoneroRingCtSig(txn_fee=rv.txnFee, message=rv.message, rv_type=rv.type)
        return MoneroTransactionAllOutSetAck(
            extra=extra_b, tx_prefix_hash=self.tx_prefix_hash, rv=rv_pb
        )
Example #7
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)
Example #8
0
    async def gen_input(self,
                        value=1,
                        sub_major=None,
                        sub_minor=0,
                        additionals=False):
        creds = self.src_keys
        r = self.random_scalar()
        R = crypto.scalarmult_base(r)
        additional_keys = []
        Additional = None
        sub_major = sub_major if sub_major is not None else self.account_idx
        is_sub = sub_major != 0 or sub_minor != 0

        if sub_major != self.account_idx:
            logger.warning(
                "Generating input with different major subindex, cannot be spent in the resulting "
                "transaction")

        kssec = monero.get_subaddress_secret_key(creds.view_key_private,
                                                 major=sub_major,
                                                 minor=sub_minor)
        kssub = crypto.sc_add(
            kssec,
            creds.spend_key_private) if is_sub else creds.spend_key_private
        kvsub = crypto.sc_mul(creds.view_key_private,
                              kssub) if is_sub else creds.view_key_private
        KSSUB, KVSUB = monero.generate_sub_address_keys(
            creds.view_key_private, creds.spend_key_public, sub_major,
            sub_minor)

        if not crypto.point_eq(KSSUB, crypto.scalarmult_base(kssub)):
            raise ValueError("Invariant error")

        oidx = self.prng.randint(0, 12)
        additionals_cnt = self.prng.randint(oidx + 1, 2 * oidx + 1)
        deriv = crypto.generate_key_derivation(KVSUB, r)
        kout = crypto.derive_secret_key(deriv, oidx, kssub)
        KOUT = crypto.derive_public_key(deriv, oidx, KSSUB)
        if not crypto.point_eq(KOUT, crypto.scalarmult_base(kout)):
            raise ValueError("Invariant error")

        if additionals:
            if is_sub:
                Additional = crypto.scalarmult(KSSUB, r)
            else:
                Additional = crypto.scalarmult_base(r)
        else:
            if is_sub:
                R = crypto.scalarmult(KSSUB, r)

        amnt = crypto.sc_init(value)
        msk = self.random_scalar()  # commitment mask
        C = crypto.add_keys2(msk, amnt, crypto.xmr_H())

        ring = []
        for i in range(self.ring_size - 1):
            tk = CtKey(
                dest=crypto.encodepoint(self.random_pub()),
                mask=crypto.encodepoint(self.random_pub()),
            )
            ring.append(tk)

        index = self.prng.randint(0, len(ring))
        ring.insert(
            index,
            CtKey(dest=crypto.encodepoint(KOUT), mask=crypto.encodepoint(C)))
        if additionals:
            additional_keys = [
                self.random_pub() for _ in range(additionals_cnt)
            ]
            additional_keys[oidx] = Additional

        src = TxSourceEntry()
        src.outputs = [(self.random_glob_idx(), x) for x in ring]
        src.real_output = index
        src.real_out_tx_key = crypto.encodepoint(R)
        src.real_out_additional_tx_keys = [
            crypto.encodepoint(x) for x in additional_keys
        ]
        src.real_output_in_tx_index = oidx
        src.amount = value
        src.rct = True
        src.mask = crypto.encodeint(msk)
        src.multisig_kLRki = MultisigKLRki(K=crypto.ZERO,
                                           L=crypto.ZERO,
                                           R=crypto.ZERO,
                                           ki=crypto.ZERO)

        td = TransferDetails()
        td.m_internal_output_index = oidx
        td.m_global_output_index = src.outputs[index][0]
        td.m_mask = src.mask
        td.m_amount = value
        td.m_subaddr_index = SubaddressIndex(major=sub_major, minor=sub_minor)
        td.m_rct = True
        td.m_txid = self.random_bytes(32)
        td.m_block_height = self.prng.randint(0, 0xFFFF)
        td.m_spent = False
        td.m_spent_height = 0
        td.m_key_image_known = True
        td.m_key_image_requested = False
        td.m_key_image_partial = False
        td.m_multisig_k = []
        td.m_multisig_info = []
        td.m_uses = []
        td.m_pk_index = 0
        td.m_tx = self.gen_tx_prefix(self.prng.randint(1, 10), additionals_cnt)
        td.m_tx.vout[oidx].target.key = crypto.encodepoint(KOUT)

        extras = []
        extras.append(TxExtraNonce(nonce=self.random_bytes(8)))
        extras.append(TxExtraPubKey(pub_key=src.real_out_tx_key))
        if src.real_out_additional_tx_keys:
            extras.append(
                TxExtraAdditionalPubKeys(data=src.real_out_additional_tx_keys))
        td.m_tx.extra = await self.dump_extra_fields(extras)

        tmpsubs = {}
        monero.compute_subaddresses(creds, sub_major, [sub_minor], tmpsubs)
        xi, ki, rderiv = self.check_input(src, creds, tmpsubs)
        if not crypto.sc_eq(xi, kout):
            raise ValueError("Invariant error")

        td.m_key_image = crypto.encodepoint(ki)

        self.sources.append(src)
        self.selected_transfers.append(len(self.transfers))
        self.transfers.append(td)
        self.sources_creds.append(creds)

        if not crypto.point_eq(
                crypto.decodepoint(src.outputs[src.real_output][1].dest),
                crypto.scalarmult_base(kout)):
            raise ValueError("Invariant error")

        return self