diff --git a/src/apps/monero/signing/step_09_sign_input.py b/src/apps/monero/signing/step_09_sign_input.py index a176bdf167..101722fda7 100644 --- a/src/apps/monero/signing/step_09_sign_input.py +++ b/src/apps/monero/signing/step_09_sign_input.py @@ -126,7 +126,7 @@ async def sign_input( ) state.mem_trace(4, True) - mg_buff = bytearray(_mg_size(len(src_entr.outputs))) + mg_buffer = bytearray(_mg_size(len(src_entr.outputs))) from apps.monero.xmr import mlsag @@ -134,7 +134,7 @@ async def sign_input( ring_pubkeys = [x.key for x in src_entr.outputs] src_entr = None - mg = mlsag.generate_mlsag_simple( + mlsag.generate_mlsag_simple( state.full_message, ring_pubkeys, input_secret_key, @@ -142,6 +142,7 @@ async def sign_input( pseudo_out_c, kLRki, index, + mg_buffer, ) del (ring_pubkeys, input_secret_key, pseudo_out_alpha, pseudo_out_c) @@ -152,7 +153,7 @@ async def sign_input( ring_pubkeys = [[x.key] for x in src_entr.outputs] src_entr = None - mg = mlsag.generate_mlsag_full( + mlsag.generate_mlsag_full( state.full_message, ring_pubkeys, [input_secret_key], @@ -161,6 +162,7 @@ async def sign_input( kLRki, index, txn_fee_key, + mg_buffer, ) del (ring_pubkeys, input_secret_key, txn_fee_key) @@ -168,10 +170,6 @@ async def sign_input( del (mlsag, src_entr) state.mem_trace(5, True) - # Encode - mg_buffer = _mg_serialize(mg, mg_buff) - state.mem_trace(6, True) - from trezor.messages.MoneroTransactionSignInputAck import ( MoneroTransactionSignInputAck, ) @@ -192,41 +190,3 @@ def _mg_size(num_outs): rows_b_size = 1 size += cols_b_size + mg_cols * (rows_b_size + mg_rows * 32) return size - - -def _mg_serialize(mg, buff): - """ - Serializes MgSig structure: (("ss", KeyM), ("cc", ECKey)) - :param mg: - :return: - """ - size = len(buff) - mg_cols = len(mg.ss) - mg_rows = len(mg.ss[0]) - cols_b_size = int_serialize.uvarint_size(mg_cols) - rows_b_size = int_serialize.uvarint_size(mg_rows) - offset = 0 - - int_serialize.dump_uvarint_b_into(mg_cols, buff, offset) - offset += cols_b_size - - for i in range(mg_cols): - utils.ensure(len(mg.ss[i]) == mg_rows, "Irregular matrix shape") - - int_serialize.dump_uvarint_b_into(mg_rows, buff, offset) - offset += rows_b_size - - for j in range(mg_rows): - crypto.encodeint_into(buff, mg.ss[i][j], offset) - offset += 32 - - mg.ss[i] = None - gc.collect() - - mg.ss = None - - crypto.encodeint_into(buff, mg.cc, offset) - offset += 32 - - utils.ensure(offset == size, "Invalid mg size computation") - return buff diff --git a/src/apps/monero/xmr/mlsag.py b/src/apps/monero/xmr/mlsag.py index 095c3bcc93..ac12f0255e 100644 --- a/src/apps/monero/xmr/mlsag.py +++ b/src/apps/monero/xmr/mlsag.py @@ -45,11 +45,22 @@ Author: Dusan Klinec, ph4r05, 2018 import gc +from trezor import utils + from apps.monero.xmr import crypto +from apps.monero.xmr.serialize import int_serialize def generate_mlsag_full( - message, pubs, in_sk, out_sk_mask, out_pk_commitments, kLRki, index, txn_fee_key + message, + pubs, + in_sk, + out_sk_mask, + out_pk_commitments, + kLRki, + index, + txn_fee_key, + mg_buff, ): cols = len(pubs) if cols == 0: @@ -110,10 +121,10 @@ def generate_mlsag_full( del (pubs, tmp_mi_rows, tmp_pt) gc.collect() - return generate_mlsag(message, M, sk, kLRki, index, rows) + return generate_mlsag(message, M, sk, kLRki, index, rows, mg_buff) -def generate_mlsag_simple(message, pubs, in_sk, a, cout, kLRki, index): +def generate_mlsag_simple(message, pubs, in_sk, a, cout, kLRki, index, mg_buff): """ MLSAG for RctType.Simple :param message: the full message to be signed (actually its hash) @@ -123,7 +134,7 @@ def generate_mlsag_simple(message, pubs, in_sk, a, cout, kLRki, index): :param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c :param kLRki: used only in multisig, currently not implemented :param index: specifies corresponding public key to the `in_sk` in the pubs array - :return: MgSig + :param mg_buff: buffer to store the signature to """ # Monero signs inputs separately, so `rows` always equals 2 (pubkey, commitment) # and `dsRows` is always 1 (denotes where the pubkeys "end") @@ -152,7 +163,7 @@ def generate_mlsag_simple(message, pubs, in_sk, a, cout, kLRki, index): del (pubs) gc.collect() - return generate_mlsag(message, M, sk, kLRki, index, dsRows) + return generate_mlsag(message, M, sk, kLRki, index, dsRows, mg_buff) def gen_mlsag_assert(pk, xx, kLRki, index, dsRows): @@ -183,13 +194,10 @@ def gen_mlsag_assert(pk, xx, kLRki, index, dsRows): return rows, cols -def generate_first_c_and_key_images( - message, rv, pk, xx, kLRki, index, dsRows, rows, cols -): +def generate_first_c_and_key_images(message, pk, xx, kLRki, index, dsRows, rows, cols): """ MLSAG computation - the part with secret keys :param message: the full message to be signed (actually its hash) - :param rv: MgSig :param pk: matrix of public keys and commitments :param xx: input secret array composed of a private key and commitment mask :param kLRki: used only in multisig, currently not implemented @@ -198,7 +206,7 @@ def generate_first_c_and_key_images( :param rows: total number of rows :param cols: size of ring """ - rv.II = _key_vector(dsRows) + II = _key_vector(dsRows) alpha = _key_vector(rows) tmp_buff = bytearray(32) @@ -226,7 +234,7 @@ def generate_first_c_and_key_images( # Ri = alpha_i * H(P_i) crypto.scalarmult_into(aHPi, Hi, alpha[i]) # key image - rv.II[i] = crypto.scalarmult(Hi, xx[i]) + II[i] = crypto.scalarmult(Hi, xx[i]) _hash_point(hasher, aGi, tmp_buff) _hash_point(hasher, aHPi, tmp_buff) @@ -243,10 +251,10 @@ def generate_first_c_and_key_images( # the first c c_old = hasher.digest() c_old = crypto.decodeint(c_old) - return c_old, alpha + return c_old, II, alpha -def generate_mlsag(message, pk, xx, kLRki, index, dsRows): +def generate_mlsag(message, pk, xx, kLRki, index, dsRows, mg_buff): """ Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) @@ -256,43 +264,57 @@ def generate_mlsag(message, pk, xx, kLRki, index, dsRows): :param kLRki: used only in multisig, currently not implemented :param index: specifies corresponding public key to the `xx`'s private key in the `pk` array :param dsRows: separates pubkeys from commitment - :return MgSig + :param mg_buff: mg signature buffer """ - from apps.monero.xmr.serialize_messages.tx_full import MgSig - rows, cols = gen_mlsag_assert(pk, xx, kLRki, index, dsRows) + rows_b_size = int_serialize.uvarint_size(rows) + cols_b_size = int_serialize.uvarint_size(cols) + int_serialize.dump_uvarint_b_into(cols, mg_buff) - rv = MgSig() - rv.cc = crypto.new_scalar() + # Computes offset to the mg_buffer + # mg_buffer format: (("ss", KeyM), ("cc", ECKey)) + # ss[i][j], i over cols, j over rows + def buff_offset(col): + return cols_b_size + col * (rows_b_size + rows * 32) + + cc = crypto.new_scalar() # rv.cc c = crypto.new_scalar() L = crypto.new_point() R = crypto.new_point() Hi = crypto.new_point() # calculates the "first" c, key images and random scalars alpha - c_old, alpha = generate_first_c_and_key_images( - message, rv, pk, xx, kLRki, index, dsRows, rows, cols + c_old, II, alpha = generate_first_c_and_key_images( + message, pk, xx, kLRki, index, dsRows, rows, cols ) i = (index + 1) % cols if i == 0: - crypto.sc_copy(rv.cc, c_old) + crypto.sc_copy(cc, c_old) - rv.ss = [None] * cols + ss = [crypto.new_scalar() for _ in range(rows)] tmp_buff = bytearray(32) + while i != index: - rv.ss[i] = _generate_random_vector(rows) hasher = _hasher_message(message) + # Serialize size of the row + offset = buff_offset(i) + int_serialize.dump_uvarint_b_into(rows, mg_buff, offset) + offset += rows_b_size + + for x in ss: + crypto.random_scalar(x) + for j in range(dsRows): # L = rv.ss[i][j] * G + c_old * pk[i][j] crypto.add_keys2_into( - L, rv.ss[i][j], c_old, crypto.decodepoint_into(Hi, pk[i][j]) + L, ss[j], c_old, crypto.decodepoint_into(Hi, pk[i][j]) ) crypto.hash_to_point_into(Hi, pk[i][j]) # R = rv.ss[i][j] * H(pk[i][j]) + c_old * Ip[j] - crypto.add_keys3_into(R, rv.ss[i][j], Hi, c_old, rv.II[j]) + crypto.add_keys3_into(R, ss[j], Hi, c_old, II[j]) hasher.update(pk[i][j]) _hash_point(hasher, L, tmp_buff) @@ -301,27 +323,39 @@ def generate_mlsag(message, pk, xx, kLRki, index, dsRows): for j in range(dsRows, rows): # again, omitting R here as discussed above crypto.add_keys2_into( - L, rv.ss[i][j], c_old, crypto.decodepoint_into(Hi, pk[i][j]) + L, ss[j], c_old, crypto.decodepoint_into(Hi, pk[i][j]) ) hasher.update(pk[i][j]) _hash_point(hasher, L, tmp_buff) + for si in range(rows): + crypto.encodeint_into(tmp_buff, ss[si]) + utils.memcpy(mg_buff, offset + 32 * si, tmp_buff, 0, 32) + crypto.decodeint_into(c, hasher.digest()) crypto.sc_copy(c_old, c) pk[i] = None i = (i + 1) % cols if i == 0: - crypto.sc_copy(rv.cc, c_old) + crypto.sc_copy(cc, c_old) gc.collect() - del rv.II + del II + + # Finalizing rv.ss by processing rv.ss[index] + offset = buff_offset(index) + int_serialize.dump_uvarint_b_into(rows, mg_buff, offset) + offset += rows_b_size - rv.ss[index] = [None] * rows for j in range(rows): - rv.ss[index][j] = crypto.sc_mulsub(c, xx[j], alpha[j]) + crypto.sc_mulsub_into(ss[j], c, xx[j], alpha[j]) + crypto.encodeint_into(tmp_buff, ss[j]) + utils.memcpy(mg_buff, offset + 32 * j, tmp_buff, 0, 32) - return rv + # rv.cc + utils.memcpy(mg_buff, len(mg_buff) - 32, crypto.encodeint_into(tmp_buff, cc), 0, 32) + utils.ensure(buff_offset(cols) + 32 == len(mg_buff), "Invalid mg_buff size") def _key_vector(rows): diff --git a/src/apps/monero/xmr/serialize_messages/tx_full.py b/src/apps/monero/xmr/serialize_messages/tx_full.py deleted file mode 100644 index 1c98619b65..0000000000 --- a/src/apps/monero/xmr/serialize_messages/tx_full.py +++ /dev/null @@ -1,11 +0,0 @@ -from apps.monero.xmr.serialize.message_types import MessageType -from apps.monero.xmr.serialize_messages.base import ECKey -from apps.monero.xmr.serialize_messages.ct_keys import KeyM - - -class MgSig(MessageType): - __slots__ = ("ss", "cc", "II") - - @classmethod - def f_specs(cls): - return (("ss", KeyM), ("cc", ECKey))