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)
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))
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))
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")
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 )
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)
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