1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-15 19:08:07 +00:00

xmr: mlsag - generating serialized signature directly

This commit is contained in:
Dusan Klinec 2018-11-02 07:03:44 +01:00
parent a462ea35ce
commit ddcb836ff7
No known key found for this signature in database
GPG Key ID: 6337E118CCBCE103
3 changed files with 70 additions and 87 deletions

View File

@ -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

View File

@ -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):

View File

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