refactor(core/monero): Monero code cleanup

* remove support for HF12 and below
* remove MLSAG support
* clean up monero cryptography naming
* get rid of "optional first argument" pattern, in favor of mandatory argument that is allowed to be None
  (and fix several bugs related to this feature)

Co-authored-by: grdddj <jiri.musil06@seznam.cz>
Co-authored-by: Martin Milata <martin@martinmilata.cz>
Co-authored-by: matejcik <ja@matejcik.cz>
pull/2227/head
Dusan Klinec 2 years ago committed by matejcik
parent 99c817bad4
commit 33c174491f

@ -5,6 +5,13 @@ package hw.trezor.messages.monero;
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageMonero";
enum MoneroNetworkType {
MAINNET = 0;
TESTNET = 1;
STAGENET = 2;
FAKECHAIN = 3;
}
/**
* Structure representing Monero transaction source entry, UTXO
* @embed
@ -24,8 +31,8 @@ message MoneroTransactionSourceEntry {
optional uint64 idx = 1;
optional MoneroRctKeyPublic key = 2;
message MoneroRctKeyPublic {
optional bytes dest = 1;
optional bytes commitment = 2;
required bytes dest = 1;
required bytes commitment = 2;
}
}
message MoneroMultisigKLRki {
@ -79,7 +86,7 @@ message MoneroTransactionRsigData {
message MoneroGetAddress {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bool show_display = 2; // Optionally show on display before sending the result
optional uint32 network_type = 3; // Main-net / testnet / stagenet
optional MoneroNetworkType network_type = 3 [default=MAINNET]; // Network type
optional uint32 account = 4; // Major subaddr index
optional uint32 minor = 5; // Minor subaddr index
optional bytes payment_id = 6; // Payment ID for integrated address
@ -101,7 +108,7 @@ message MoneroAddress {
*/
message MoneroGetWatchKey {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional uint32 network_type = 2; // Main-net / testnet / stagenet
optional MoneroNetworkType network_type = 2 [default=MAINNET]; // Network type
}
/**
@ -121,7 +128,7 @@ message MoneroWatchKey {
message MoneroTransactionInitRequest {
optional uint32 version = 1;
repeated uint32 address_n = 2;
optional uint32 network_type = 3; // Main-net / testnet / stagenet
optional MoneroNetworkType network_type = 3 [default=MAINNET]; // Network type
optional MoneroTransactionData tsx_data = 4;
/**
* Structure representing Monero initial transaction information
@ -329,16 +336,16 @@ message MoneroTransactionFinalAck {
* @next MoneroKeyImageExportInitAck
*/
message MoneroKeyImageExportInitRequest {
optional uint64 num = 1;
optional bytes hash = 2;
required uint64 num = 1;
required bytes hash = 2;
repeated uint32 address_n = 3; // BIP-32 path to derive the key from master node
optional uint32 network_type = 4; // Main-net / testnet / stagenet
optional MoneroNetworkType network_type = 4 [default=MAINNET]; // network type
repeated MoneroSubAddressIndicesList subs = 5;
/**
* Structure representing Monero list of sub-addresses
*/
message MoneroSubAddressIndicesList {
optional uint32 account = 1;
required uint32 account = 1;
repeated uint32 minor_indices = 2;
}
}
@ -360,10 +367,10 @@ message MoneroKeyImageSyncStepRequest {
* Structure representing Monero UTXO for key image sync
*/
message MoneroTransferDetails {
optional bytes out_key = 1;
optional bytes tx_pub_key = 2;
required bytes out_key = 1;
required bytes tx_pub_key = 2;
repeated bytes additional_tx_pub_keys = 3;
optional uint64 internal_output_index = 4;
required uint64 internal_output_index = 4;
optional uint32 sub_addr_major = 5;
optional uint32 sub_addr_minor = 6;
}
@ -406,12 +413,12 @@ message MoneroKeyImageSyncFinalAck {
*/
message MoneroGetTxKeyRequest {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional uint32 network_type = 2; // Main-net / testnet / stagenet
optional MoneroNetworkType network_type = 2 [default=MAINNET]; // network type
optional bytes salt1 = 3;
optional bytes salt2 = 4;
optional bytes tx_enc_keys = 5;
optional bytes tx_prefix_hash = 6;
required bytes salt1 = 3;
required bytes salt2 = 4;
required bytes tx_enc_keys = 5;
required bytes tx_prefix_hash = 6;
optional uint32 reason = 7; // reason to display for user. e.g., tx_proof
optional bytes view_public_key = 8; // addr for derivation
}
@ -432,7 +439,7 @@ message MoneroGetTxKeyAck {
*/
message MoneroLiveRefreshStartRequest {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional uint32 network_type = 2; // Main-net / testnet / stagenet
optional MoneroNetworkType network_type = 2 [default=MAINNET]; // network type
}
/**
@ -449,11 +456,11 @@ message MoneroLiveRefreshStartAck {
* @next MoneroLiveRefreshStepAck
*/
message MoneroLiveRefreshStepRequest {
optional bytes out_key = 1;
optional bytes recv_deriv = 2;
optional uint64 real_out_idx = 3;
optional uint32 sub_addr_major = 4;
optional uint32 sub_addr_minor = 5;
required bytes out_key = 1;
required bytes recv_deriv = 2;
required uint64 real_out_idx = 3;
required uint32 sub_addr_major = 4;
required uint32 sub_addr_minor = 5;
}
/**

@ -0,0 +1 @@
Refactor and cleanup of Monero code.

@ -0,0 +1 @@
Removed support for obsolete Monero hardfork 12 and below

File diff suppressed because it is too large Load Diff

@ -2,22 +2,22 @@ from typing import *
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
class Ge25519:
class Point:
"""
EC point on ED25519
"""
def __init__(self, x: Ge25519 | bytes | None = None):
def __init__(self, x: Point | bytes | None = None):
"""
Constructor
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
class Sc25519:
class Scalar:
"""
EC scalar on SC25519
"""
def __init__(self, x: Sc25519 | bytes | int | None = None):
def __init__(self, x: Scalar | bytes | int | None = None):
"""
Constructor
"""
@ -47,92 +47,84 @@ class Hasher:
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def init256_modm(
dst: Sc25519 | None, val: int | bytes | Sc25519
) -> Sc25519:
def sc_copy(
dst: Scalar | None, val: int | bytes | Scalar
) -> Scalar:
"""
Initializes Sc25519 scalar
Initializes a scalar
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def check256_modm(val: Sc25519) -> None:
def sc_check(val: Scalar) -> None:
"""
Throws exception if scalar is invalid
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def iszero256_modm(val: Sc25519) -> bool:
def sc_iszero(val: Scalar) -> bool:
"""
Returns False if the scalar is zero
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def eq256_modm(a: Sc25519, b: Sc25519) -> int:
def sc_eq(a: Scalar, b: Scalar) -> int:
"""
Compares scalars, returns 1 on the same value
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def get256_modm(a: Sc25519) -> int:
"""
Extracts 64bit integer from the scalar. Raises exception if scalar is
bigger than 2^64
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def add256_modm(r: Sc25519 | None, a: Sc25519, b: Sc25519) -> Sc25519:
def sc_add_into(r: Scalar | None, a: Scalar, b: Scalar) -> Scalar:
"""
Scalar addition
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def sub256_modm(r: Sc25519 | None, a: Sc25519, b: Sc25519) -> Sc25519:
def sc_sub_into(r: Scalar | None, a: Scalar, b: Scalar) -> Scalar:
"""
Scalar subtraction
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def mul256_modm(r: Sc25519 | None, a: Sc25519, b: Sc25519) -> Sc25519:
def sc_mul_into(r: Scalar | None, a: Scalar, b: Scalar) -> Scalar:
"""
Scalar multiplication
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def mulsub256_modm(
r: Sc25519 | None, a: Sc25519, b: Sc25519, c: Sc25519
) -> Sc25519:
def sc_mulsub_into(
r: Scalar | None, a: Scalar, b: Scalar, c: Scalar
) -> Scalar:
"""
c - a*b
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def muladd256_modm(
r: Sc25519 | None, a: Sc25519, b: Sc25519, c: Sc25519
) -> Sc25519:
def sc_muladd_into(
r: Scalar | None, a: Scalar, b: Scalar, c: Scalar
) -> Scalar:
"""
c + a*b
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def inv256_modm(r: Sc25519 | None, a: Sc25519) -> Sc25519:
def sc_inv_into(r: Scalar | None, a: Scalar) -> Scalar:
"""
Scalar modular inversion
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def pack256_modm(
r: bytes | None, a: Sc25519, offset: int | None = 0
def encodeint_into(
r: bytes | None, a: Scalar, offset: int | None = 0
) -> bytes:
"""
Scalar compression
@ -140,144 +132,124 @@ def pack256_modm(
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def unpack256_modm(
r: Sc25519 | None, a: bytes, offset: int = 0
) -> Sc25519:
def decodeint_into(
r: Scalar | None, a: bytes, offset: int = 0
) -> Scalar:
"""
Scalar decompression
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def unpack256_modm_noreduce(
r: Sc25519 | None, a: bytes, offset: int = 0
) -> Sc25519:
def decodeint_into_noreduce(
r: Scalar | None, a: bytes, offset: int = 0
) -> Scalar:
"""
Scalar decompression, raw, without modular reduction
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_set_neutral(r: Ge25519 | None) -> Ge25519:
def identity_into(r: Point | None = None) -> Point:
"""
Sets neutral point
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_set_xmr_h(r: Ge25519 | None) -> Ge25519:
def xmr_H(r: Point | None = None) -> Point:
"""
Sets H point
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_check(r: Ge25519) -> None:
def ge25519_check(r: Point) -> None:
"""
Checks point, throws if not on curve
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_eq(a: Ge25519, b: Ge25519) -> bool:
def point_eq(a: Point, b: Point) -> bool:
"""
Compares EC points
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_add(r: Ge25519 | None, a: Ge25519, b: Ge25519) -> Ge25519:
def point_add_into(r: Point | None, a: Point, b: Point) -> Point:
"""
Adds EC points
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_sub(r: Ge25519 | None, a: Ge25519, b: Ge25519) -> Ge25519:
def point_sub_into(r: Point | None, a: Point, b: Point) -> Point:
"""
Subtracts EC points
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_double(r: Ge25519 | None, p: Ge25519) -> Ge25519:
"""
EC point doubling
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_mul8(r: Ge25519 | None, p: Ge25519) -> Ge25519:
def ge25519_mul8(r: Point | None, p: Point) -> Point:
"""
EC point * 8
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_double_scalarmult_vartime(
r: Ge25519 | None, p1: Ge25519, s1: Sc25519, s2: Sc25519
) -> Ge25519:
def ge25519_double_scalarmult_vartime_into(
r: Point | None, p1: Point, s1: Scalar, s2: Scalar
) -> Point:
"""
s1 * G + s2 * p1
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_double_scalarmult_vartime2(
r: Ge25519 | None,
p1: Ge25519,
s1: Sc25519,
p2: Ge25519,
s2: Sc25519,
) -> Ge25519:
"""
s1 * p1 + s2 * p2
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_scalarmult_base(
r: Ge25519 | None, s: Sc25519 | int
) -> Ge25519:
def scalarmult_base_into(
r: Point | None, s: Scalar | int
) -> Point:
"""
s * G
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_scalarmult(
r: Ge25519 | None, p: Ge25519, s: Sc25519 | int
) -> Ge25519:
def scalarmult_into(
r: Point | None, p: Point, s: Scalar | int
) -> Point:
"""
s * p
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_pack(r: bytes, p: Ge25519, offset: int = 0) -> bytes:
def encodepoint_into(r: bytes | None, p: Point, offset: int = 0) -> bytes:
"""
Point compression
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def ge25519_unpack_vartime(
r: Ge25519 | None, buff: bytes, offset: int = 0
) -> Ge25519:
def decodepoint_into(
r: Point | None, buff: bytes, offset: int = 0
) -> Point:
"""
Point decompression
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def base58_addr_encode_check(tag: int, buff: bytes) -> bytes:
def xmr_base58_addr_encode_check(tag: int, buff: bytes) -> str:
"""
Monero block base 58 encoding
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def base58_addr_decode_check(buff: bytes) -> tuple[bytes, int]:
def xmr_base58_addr_decode_check(buff: bytes) -> tuple[bytes, int]:
"""
Monero block base 58 decoding, returning (decoded, tag) or raising on
error.
@ -285,30 +257,43 @@ def base58_addr_decode_check(buff: bytes) -> tuple[bytes, int]:
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_random_scalar(r: Sc25519 | None = None) -> Sc25519:
def random_scalar(r: Scalar | None = None) -> Scalar:
"""
Generates a random scalar
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_fast_hash(r: bytes | None, buff: bytes, length: int, offset: int) -> bytes:
def fast_hash_into(
r: bytes | None,
buff: bytes,
length: int | None = None,
offset: int = 0,
) -> bytes:
"""
XMR fast hash
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_hash_to_ec(r: Ge25519 | None, buff: bytes, length: int, offset:
int) -> Ge25519:
def hash_to_point_into(
r: Point | None,
buff: bytes,
length: int | None = None,
offset: int = 0,
) -> Point:
"""
XMR hashing to EC point
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_hash_to_scalar(r: Sc25519 | None, buff: bytes, length: int,
offset: int) -> Sc25519:
def hash_to_scalar_into(
r: Scalar | None,
buff: bytes,
length: int | None = None,
offset: int = 0,
) -> Scalar:
"""
XMR hashing to EC scalar
"""
@ -316,8 +301,8 @@ offset: int) -> Sc25519:
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_derivation_to_scalar(
r: Sc25519 | None, p: Ge25519, output_index: int
) -> Sc25519:
r: Scalar | None, p: Point, output_index: int
) -> Scalar:
"""
H_s(derivation || varint(output_index))
"""
@ -325,8 +310,8 @@ def xmr_derivation_to_scalar(
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_generate_key_derivation(
r: Ge25519 | None, A: Ge25519, b: Sc25519
) -> Ge25519:
r: Point | None, A: Point, b: Scalar
) -> Point:
"""
8*(key2*key1)
"""
@ -334,8 +319,8 @@ def xmr_generate_key_derivation(
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_derive_private_key(
r: Sc25519 | None, deriv: Ge25519, idx: int, base: Sc25519
) -> Sc25519:
r: Scalar | None, deriv: Point, idx: int, base: Scalar
) -> Scalar:
"""
base + H_s(derivation || varint(output_index))
"""
@ -343,44 +328,26 @@ def xmr_derive_private_key(
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_derive_public_key(
r: Ge25519 | None, deriv: Ge25519, idx: int, base: Ge25519
) -> Ge25519:
r: Point | None, deriv: Point, idx: int, base: Point
) -> Point:
"""
H_s(derivation || varint(output_index))G + base
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_add_keys2(
r: Ge25519 | None, a: Sc25519, b: Sc25519, B: Ge25519
) -> Ge25519:
"""
aG + bB, G is basepoint
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_add_keys2_vartime(
r: Ge25519 | None, a: Sc25519, b: Sc25519, B: Ge25519
) -> Ge25519:
def add_keys2_into(
r: Point | None, a: Scalar, b: Scalar, B: Point
) -> Point:
"""
aG + bB, G is basepoint
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_add_keys3(
r: Ge25519 | None, a: Sc25519, A: Ge25519, b: Sc25519, B: Ge25519
) -> Ge25519:
"""
aA + bB
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_add_keys3_vartime(
r: Ge25519 | None, a: Sc25519, A: Ge25519, b: Sc25519, B: Ge25519
) -> Ge25519:
def add_keys3_into(
r: Point | None, a: Scalar, A: Point, b: Scalar, B: Point
) -> Point:
"""
aA + bB
"""
@ -388,15 +355,15 @@ def xmr_add_keys3_vartime(
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_get_subaddress_secret_key(
r: Sc25519 | None, major: int, minor: int, m: Sc25519
) -> Sc25519:
r: Scalar | None, major: int, minor: int, m: Scalar
) -> Scalar:
"""
Hs(SubAddr || a || index_major || index_minor)
"""
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
def xmr_gen_c(r: Ge25519 | None, a: Sc25519, amount: int) -> Ge25519:
def gen_commitment_into(r: Point | None, a: Scalar, amount: int) -> Point:
"""
aG + amount * H
"""
@ -407,3 +374,6 @@ def ct_equals(a: bytes, b: bytes) -> bool:
"""
Constant time buffer comparison
"""
BP_GI_PRE: bytes
BP_HI_PRE: bytes
BP_TWO_N: bytes

@ -3,7 +3,8 @@ from typing import TypeVar
C = TypeVar("C", bound=int)
def const(c: C) -> C: ...
def mem_info(verbose: bool | None = None) -> None: ...
def mem_info(verbose: bool | int | None = None) -> None: ...
def mem_current() -> int: ...
def mem_total() -> int: ...
def mem_peak() -> int: ...
def stack_use() -> int: ...

@ -434,6 +434,8 @@ if not utils.BITCOIN_ONLY:
import trezor.enums.CardanoTxWitnessType
trezor.enums.EthereumDataType
import trezor.enums.EthereumDataType
trezor.enums.MoneroNetworkType
import trezor.enums.MoneroNetworkType
trezor.enums.NEMImportanceTransferMode
import trezor.enums.NEMImportanceTransferMode
trezor.enums.NEMModificationType
@ -592,8 +594,6 @@ if not utils.BITCOIN_ONLY:
import apps.monero.signing.step_01_init_transaction
apps.monero.signing.step_02_set_input
import apps.monero.signing.step_02_set_input
apps.monero.signing.step_03_inputs_permutation
import apps.monero.signing.step_03_inputs_permutation
apps.monero.signing.step_04_input_vini
import apps.monero.signing.step_04_input_vini
apps.monero.signing.step_05_all_inputs_set
@ -606,22 +606,24 @@ if not utils.BITCOIN_ONLY:
import apps.monero.signing.step_09_sign_input
apps.monero.signing.step_10_sign_final
import apps.monero.signing.step_10_sign_final
apps.monero.xmr
import apps.monero.xmr
apps.monero.xmr.addresses
import apps.monero.xmr.addresses
apps.monero.xmr.bulletproof
import apps.monero.xmr.bulletproof
apps.monero.xmr.chacha_poly
import apps.monero.xmr.chacha_poly
apps.monero.xmr.clsag
import apps.monero.xmr.clsag
apps.monero.xmr.credentials
import apps.monero.xmr.credentials
apps.monero.xmr.crypto
import apps.monero.xmr.crypto
apps.monero.xmr.crypto.chacha_poly
import apps.monero.xmr.crypto.chacha_poly
apps.monero.xmr.crypto_helpers
import apps.monero.xmr.crypto_helpers
apps.monero.xmr.keccak_hasher
import apps.monero.xmr.keccak_hasher
apps.monero.xmr.key_image
import apps.monero.xmr.key_image
apps.monero.xmr.mlsag
import apps.monero.xmr.mlsag
apps.monero.xmr.mlsag_hasher
import apps.monero.xmr.mlsag_hasher
apps.monero.xmr.monero
@ -650,8 +652,6 @@ if not utils.BITCOIN_ONLY:
import apps.monero.xmr.serialize_messages.tx_prefix
apps.monero.xmr.serialize_messages.tx_rsig_bulletproof
import apps.monero.xmr.serialize_messages.tx_rsig_bulletproof
apps.monero.xmr.types
import apps.monero.xmr.types
apps.nem
import apps.nem
apps.nem.get_address

@ -1,3 +1,9 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import Failure
if __debug__:
import gc
import micropython
@ -8,7 +14,7 @@ if __debug__:
PREV_MEM = gc.mem_free()
CUR_MES = 0
def log_trace(x=None):
def log_trace(x=None) -> None:
log.debug(
__name__,
"Log trace %s, ... F: %s A: %s, S: %s",
@ -18,7 +24,7 @@ if __debug__:
micropython.stack_use(),
)
def check_mem(x=""):
def check_mem(x: str | int = "") -> None:
global PREV_MEM, CUR_MES
gc.collect()
@ -33,12 +39,12 @@ if __debug__:
CUR_MES += 1
PREV_MEM = free
def retit(**kwargs):
def retit(**kwargs) -> Failure:
from trezor.messages import Failure
return Failure(**kwargs)
async def diag(ctx, msg, **kwargs):
async def diag(ctx, msg, **kwargs) -> Failure:
log.debug(__name__, "----diagnostics")
gc.collect()
@ -85,7 +91,7 @@ if __debug__:
bpi.gc_fnc = gc.collect
bpi.gc_trace = log_trace
vals = [crypto.sc_init((1 << 30) - 1 + 16), crypto.sc_init(22222)]
vals = [crypto.Scalar((1 << 30) - 1 + 16), crypto.Scalar(22222)]
masks = [crypto.random_scalar(), crypto.random_scalar()]
check_mem("BP pre input")

@ -1,33 +1,53 @@
from typing import TYPE_CHECKING
from trezor import wire
from trezor.messages import MoneroAddress
from trezor.ui.layouts import show_address
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import misc
from apps.monero.xmr import addresses, crypto, monero
from apps.monero.xmr import addresses, crypto_helpers, monero
from apps.monero.xmr.networks import net_version
if TYPE_CHECKING:
from trezor.messages import MoneroGetAddress
from apps.common.keychain import Keychain
@auto_keychain(__name__)
async def get_address(ctx, msg, keychain):
async def get_address(
ctx: wire.Context, msg: MoneroGetAddress, keychain: Keychain
) -> MoneroAddress:
await paths.validate_path(ctx, keychain, msg.address_n)
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
addr = creds.address
if msg.payment_id:
have_subaddress = msg.account is not None and msg.minor is not None
have_payment_id = msg.payment_id is not None
if (msg.account is None) != (msg.minor is None):
raise wire.ProcessError("Invalid subaddress indexes")
if have_payment_id and have_subaddress:
raise wire.DataError("Subaddress cannot be integrated")
if have_payment_id:
assert msg.payment_id is not None
if len(msg.payment_id) != 8:
raise ValueError("Invalid payment ID length")
addr = addresses.encode_addr(
net_version(msg.network_type, False, True),
crypto.encodepoint(creds.spend_key_public),
crypto.encodepoint(creds.view_key_public),
crypto_helpers.encodepoint(creds.spend_key_public),
crypto_helpers.encodepoint(creds.view_key_public),
msg.payment_id,
)
if msg.account or msg.minor:
if msg.payment_id:
raise ValueError("Subaddress cannot be integrated")
if have_subaddress:
assert msg.account is not None
assert msg.minor is not None
pub_spend, pub_view = monero.generate_sub_address_keys(
creds.view_key_private, creds.spend_key_public, msg.account, msg.minor
@ -35,17 +55,17 @@ async def get_address(ctx, msg, keychain):
addr = addresses.encode_addr(
net_version(msg.network_type, True, False),
crypto.encodepoint(pub_spend),
crypto.encodepoint(pub_view),
crypto_helpers.encodepoint(pub_spend),
crypto_helpers.encodepoint(pub_view),
)
if msg.show_display:
title = paths.address_n_to_str(msg.address_n)
await show_address(
ctx,
address=addr.decode(),
address_qr="monero:" + addr.decode(),
address=addr,
address_qr="monero:" + addr,
title=title,
)
return MoneroAddress(address=addr)
return MoneroAddress(address=addr.encode())

@ -14,22 +14,26 @@ encrypted using the private spend key. Here the host sends it back
in `MoneroGetTxKeyRequest.tx_enc_keys` to be decrypted and yet again encrypted
using the view key, which the host possess.
"""
from typing import TYPE_CHECKING
from trezor import utils
from trezor import utils, wire
from trezor.messages import MoneroGetTxKeyAck, MoneroGetTxKeyRequest
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import layout, misc
from apps.monero.xmr import crypto
from apps.monero.xmr.crypto import chacha_poly
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers
_GET_TX_KEY_REASON_TX_KEY = 0
_GET_TX_KEY_REASON_TX_DERIVATION = 1
if TYPE_CHECKING:
from apps.common.keychain import Keychain
@auto_keychain(__name__)
async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
async def get_tx_keys(
ctx: wire.Context, msg: MoneroGetTxKeyRequest, keychain: Keychain
) -> MoneroGetTxKeyAck:
await paths.validate_path(ctx, keychain, msg.address_n)
do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION
@ -41,7 +45,7 @@ async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
creds.spend_key_private,
msg.tx_prefix_hash,
msg.salt1,
crypto.decodeint(msg.salt2),
crypto_helpers.decodeint(msg.salt2),
)
# the plain_buff first stores the tx_priv_keys as decrypted here
@ -52,10 +56,13 @@ async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
# If return only derivations do tx_priv * view_pub
if do_deriv:
if msg.view_public_key is None:
raise wire.DataError("Missing view public key")
plain_buff = bytearray(plain_buff)
view_pub = crypto.decodepoint(msg.view_public_key)
tx_priv = crypto.new_scalar()
derivation = crypto.new_point()
view_pub = crypto_helpers.decodepoint(msg.view_public_key)
tx_priv = crypto.Scalar()
derivation = crypto.Point()
n_keys = len(plain_buff) // 32
for c in range(n_keys):
crypto.decodeint_into(tx_priv, plain_buff, 32 * c)

@ -1,19 +1,29 @@
from trezor.messages import MoneroGetWatchKey, MoneroWatchKey
from typing import TYPE_CHECKING
from trezor.messages import MoneroWatchKey
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import layout, misc
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto_helpers
if TYPE_CHECKING:
from trezor.wire import Context
from trezor.messages import MoneroGetWatchKey
from apps.common.keychain import Keychain
@auto_keychain(__name__)
async def get_watch_only(ctx, msg: MoneroGetWatchKey, keychain):
async def get_watch_only(
ctx: Context, msg: MoneroGetWatchKey, keychain: Keychain
) -> MoneroWatchKey:
await paths.validate_path(ctx, keychain, msg.address_n)
await layout.require_confirm_watchkey(ctx)
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
address = creds.address
watch_key = crypto.encodeint(creds.view_key_private)
watch_key = crypto_helpers.encodeint(creds.view_key_private)
return MoneroWatchKey(watch_key=watch_key, address=address)
return MoneroWatchKey(watch_key=watch_key, address=address.encode())

@ -1,6 +1,8 @@
import gc
from typing import TYPE_CHECKING
from trezor import log, wire
from trezor.crypto import random
from trezor.messages import (
MoneroExportedKeyImage,
MoneroKeyImageExportInitAck,
@ -13,20 +15,28 @@ from trezor.messages import (
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import layout, misc
from apps.monero.xmr import crypto, key_image, monero
from apps.monero.xmr.crypto import chacha_poly
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers, key_image, monero
if TYPE_CHECKING:
from trezor.messages import MoneroKeyImageExportInitRequest
from apps.common.keychain import Keychain
from .xmr.credentials import AccountCreds
@auto_keychain(__name__)
async def key_image_sync(ctx, msg, keychain):
async def key_image_sync(
ctx: wire.Context, msg: MoneroKeyImageExportInitRequest, keychain: Keychain
) -> MoneroKeyImageSyncFinalAck:
state = KeyImageSync()
res = await _init_step(state, ctx, msg, keychain)
while state.current_output + 1 < state.num_outputs:
msg = await ctx.call(res, MoneroKeyImageSyncStepRequest)
res = await _sync_step(state, ctx, msg)
step = await ctx.call(res, MoneroKeyImageSyncStepRequest)
res = await _sync_step(state, ctx, step)
gc.collect()
msg = await ctx.call(res, MoneroKeyImageSyncFinalRequest)
await ctx.call(res, MoneroKeyImageSyncFinalRequest)
res = await _final_step(state, ctx)
return res
@ -36,14 +46,19 @@ class KeyImageSync:
def __init__(self):
self.current_output = -1
self.num_outputs = 0
self.expected_hash = None
self.enc_key = None
self.creds = None
self.expected_hash = b""
self.enc_key = b""
self.creds: AccountCreds | None = None
self.subaddresses = {}
self.hasher = crypto.get_keccak()
self.hasher = crypto_helpers.get_keccak()
async def _init_step(s, ctx, msg, keychain):
async def _init_step(
s: KeyImageSync,
ctx: wire.Context,
msg: MoneroKeyImageExportInitRequest,
keychain: Keychain,
) -> MoneroKeyImageExportInitAck:
await paths.validate_path(ctx, keychain, msg.address_n)
s.creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
@ -52,7 +67,7 @@ async def _init_step(s, ctx, msg, keychain):
s.num_outputs = msg.num
s.expected_hash = msg.hash
s.enc_key = crypto.random_bytes(32)
s.enc_key = random.bytes(32)
for sub in msg.subs:
monero.compute_subaddresses(
@ -62,7 +77,11 @@ async def _init_step(s, ctx, msg, keychain):
return MoneroKeyImageExportInitAck()
async def _sync_step(s, ctx, tds):
async def _sync_step(
s: KeyImageSync, ctx: wire.Context, tds: MoneroKeyImageSyncStepRequest
) -> MoneroKeyImageSyncStepAck:
assert s.creds is not None
if not tds.tdis:
raise wire.DataError("Empty")
@ -99,7 +118,7 @@ async def _sync_step(s, ctx, tds):
return MoneroKeyImageSyncStepAck(kis=kis)
async def _final_step(s, ctx):
async def _final_step(s, ctx: wire.Context) -> MoneroKeyImageSyncFinalAck:
if s.current_output + 1 != s.num_outputs:
raise wire.DataError("Invalid number of outputs")

@ -14,18 +14,21 @@ DUMMY_PAYMENT_ID = b"\x00\x00\x00\x00\x00\x00\x00\x00"
if TYPE_CHECKING:
from apps.monero.signing.state import State
from trezor.enums import MoneroNetworkType
from trezor.messages import (
MoneroTransactionData,
MoneroTransactionDestinationEntry,
)
from trezor.wire import Context
from .signing.state import State
def _format_amount(value):
def _format_amount(value: int) -> str:
return f"{strings.format_amount(value, 12)} XMR"
async def require_confirm_watchkey(ctx):
async def require_confirm_watchkey(ctx: Context) -> None:
await confirm_action(
ctx,
"get_watchkey",
@ -37,7 +40,7 @@ async def require_confirm_watchkey(ctx):
)
async def require_confirm_keyimage_sync(ctx):
async def require_confirm_keyimage_sync(ctx: Context) -> None:
await confirm_action(
ctx,
"key_image_sync",
@ -49,7 +52,7 @@ async def require_confirm_keyimage_sync(ctx):
)
async def require_confirm_live_refresh(ctx):
async def require_confirm_live_refresh(ctx: Context) -> None:
await confirm_action(
ctx,
"live_refresh",
@ -61,7 +64,7 @@ async def require_confirm_live_refresh(ctx):
)
async def require_confirm_tx_key(ctx, export_key=False):
async def require_confirm_tx_key(ctx: Context, export_key: bool = False) -> None:
if export_key:
description = "Do you really want to export tx_key?"
else:
@ -78,8 +81,11 @@ async def require_confirm_tx_key(ctx, export_key=False):
async def require_confirm_transaction(
ctx, state: State, tsx_data: MoneroTransactionData, network_type: int
):
ctx: Context,
state: State,
tsx_data: MoneroTransactionData,
network_type: MoneroNetworkType,
) -> None:
"""
Ask for confirmation from user.
"""
@ -87,8 +93,6 @@ async def require_confirm_transaction(
outputs = tsx_data.outputs
change_idx = get_change_addr_idx(outputs, tsx_data.change_dts)
has_integrated = bool(tsx_data.integrated_indices)
has_payment = bool(tsx_data.payment_id)
if tsx_data.unlock_time != 0:
await _require_confirm_unlock_time(ctx, tsx_data.unlock_time)
@ -100,13 +104,17 @@ async def require_confirm_transaction(
is_dummy = change_idx is None and dst.amount == 0 and len(outputs) == 2
if is_dummy:
continue # Dummy output does not need confirmation
if has_integrated and idx in tsx_data.integrated_indices:
if tsx_data.integrated_indices and idx in tsx_data.integrated_indices:
cur_payment = tsx_data.payment_id
else:
cur_payment = None
await _require_confirm_output(ctx, dst, network_type, cur_payment)
if has_payment and not has_integrated and tsx_data.payment_id != DUMMY_PAYMENT_ID:
if (
tsx_data.payment_id
and not tsx_data.integrated_indices
and tsx_data.payment_id != DUMMY_PAYMENT_ID
):
await _require_confirm_payment_id(ctx, tsx_data.payment_id)
await _require_confirm_fee(ctx, tsx_data.fee)
@ -114,8 +122,11 @@ async def require_confirm_transaction(
async def _require_confirm_output(
ctx, dst: MoneroTransactionDestinationEntry, network_type: int, payment_id: bytes
):
ctx: Context,
dst: MoneroTransactionDestinationEntry,
network_type: MoneroNetworkType,
payment_id: bytes | None,
) -> None:
"""
Single transaction destination confirmation
"""
@ -129,14 +140,14 @@ async def _require_confirm_output(
await confirm_output(
ctx,
address=addr.decode(),
address=addr,
amount=_format_amount(dst.amount),
font_amount=ui.BOLD,
br_code=ButtonRequestType.SignTx,
)
async def _require_confirm_payment_id(ctx, payment_id: bytes):
async def _require_confirm_payment_id(ctx: Context, payment_id: bytes) -> None:
await confirm_blob(
ctx,
"confirm_payment_id",
@ -146,7 +157,7 @@ async def _require_confirm_payment_id(ctx, payment_id: bytes):
)
async def _require_confirm_fee(ctx, fee):
async def _require_confirm_fee(ctx: Context, fee: int) -> None:
await confirm_metadata(
ctx,
"confirm_final",
@ -158,7 +169,7 @@ async def _require_confirm_fee(ctx, fee):
)
async def _require_confirm_unlock_time(ctx, unlock_time):
async def _require_confirm_unlock_time(ctx: Context, unlock_time: int) -> None:
await confirm_metadata(
ctx,
"confirm_locktime",
@ -170,12 +181,12 @@ async def _require_confirm_unlock_time(ctx, unlock_time):
class TransactionStep(ui.Component):
def __init__(self, state, info):
def __init__(self, state: State, info: list[str]) -> None:
super().__init__()
self.state = state
self.info = info
def on_render(self):
def on_render(self) -> None:
state = self.state
info = self.info
ui.header("Signing transaction", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE)
@ -187,12 +198,12 @@ class TransactionStep(ui.Component):
class KeyImageSyncStep(ui.Component):
def __init__(self, current, total_num):
def __init__(self, current: int, total_num: int) -> None:
super().__init__()
self.current = current
self.total_num = total_num
def on_render(self):
def on_render(self) -> None:
current = self.current
total_num = self.total_num
ui.header("Syncing", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE)
@ -201,11 +212,11 @@ class KeyImageSyncStep(ui.Component):
class LiveRefreshStep(ui.Component):
def __init__(self, current):
def __init__(self, current: int) -> None:
super().__init__()
self.current = current
def on_render(self):
def on_render(self) -> None:
current = self.current
ui.header("Refreshing", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE)
p = (1000 * current // 8) % 1000
@ -215,13 +226,11 @@ class LiveRefreshStep(ui.Component):
)
async def transaction_step(state: State, step: int, sub_step: int | None = None):
async def transaction_step(state: State, step: int, sub_step: int = 0) -> None:
if step == 0:
info = ["Signing..."]
elif step == state.STEP_INP:
info = ["Processing inputs", f"{sub_step + 1}/{state.input_count}"]
elif step == state.STEP_PERM:
info = ["Sorting..."]
elif step == state.STEP_VINI:
info = ["Hashing inputs", f"{sub_step + 1}/{state.input_count}"]
elif step == state.STEP_ALL_IN:
@ -239,13 +248,13 @@ async def transaction_step(state: State, step: int, sub_step: int | None = None)
await Popup(TransactionStep(state, info))
async def keyimage_sync_step(ctx, current, total_num):
async def keyimage_sync_step(ctx: Context, current: int | None, total_num: int) -> None:
if current is None:
return
await Popup(KeyImageSyncStep(current, total_num))
async def live_refresh_step(ctx, current):
async def live_refresh_step(ctx: Context, current: int | None) -> None:
if current is None:
return
await Popup(LiveRefreshStep(current))

@ -1,4 +1,5 @@
import gc
from typing import TYPE_CHECKING
import storage.cache
from trezor import log
@ -6,7 +7,6 @@ from trezor.enums import MessageType
from trezor.messages import (
MoneroLiveRefreshFinalAck,
MoneroLiveRefreshStartAck,
MoneroLiveRefreshStartRequest,
MoneroLiveRefreshStepAck,
MoneroLiveRefreshStepRequest,
)
@ -14,40 +14,49 @@ from trezor.messages import (
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import layout, misc
from apps.monero.xmr import crypto, key_image, monero
from apps.monero.xmr.crypto import chacha_poly
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers, key_image, monero
if TYPE_CHECKING:
from trezor.messages import MoneroLiveRefreshStartRequest
from trezor.wire import Context
from apps.common.keychain import Keychain
from .xmr.credentials import AccountCreds
@auto_keychain(__name__)
async def live_refresh(ctx, msg: MoneroLiveRefreshStartRequest, keychain):
async def live_refresh(
ctx: Context, msg: MoneroLiveRefreshStartRequest, keychain: Keychain
) -> MoneroLiveRefreshFinalAck:
state = LiveRefreshState()
res = await _init_step(state, ctx, msg, keychain)
while True:
msg = await ctx.call_any(
step = await ctx.call_any(
res,
MessageType.MoneroLiveRefreshStepRequest,
MessageType.MoneroLiveRefreshFinalRequest,
)
del res
if msg.MESSAGE_WIRE_TYPE == MessageType.MoneroLiveRefreshStepRequest:
res = await _refresh_step(state, ctx, msg)
if MoneroLiveRefreshStepRequest.is_type_of(step):
res = await _refresh_step(state, ctx, step)
else:
return MoneroLiveRefreshFinalAck()
gc.collect()
return res
class LiveRefreshState:
def __init__(self):
def __init__(self) -> None:
self.current_output = 0
self.creds = None
self.creds: AccountCreds | None = None
async def _init_step(
s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStartRequest, keychain
):
s: LiveRefreshState,
ctx: Context,
msg: MoneroLiveRefreshStartRequest,
keychain: Keychain,
) -> MoneroLiveRefreshStartAck:
await paths.validate_path(ctx, keychain, msg.address_n)
if not storage.cache.get(storage.cache.APP_MONERO_LIVE_REFRESH):
@ -59,7 +68,11 @@ async def _init_step(
return MoneroLiveRefreshStartAck()
async def _refresh_step(s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStepRequest):
async def _refresh_step(
s: LiveRefreshState, ctx: Context, msg: MoneroLiveRefreshStepRequest
) -> MoneroLiveRefreshStepAck:
assert s.creds is not None
buff = bytearray(32 * 3)
buff_mv = memoryview(buff)
@ -74,14 +87,14 @@ async def _refresh_step(s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStepRequ
# If subaddr:
# spend_priv += Hs("SubAddr" || view_key_private || major || minor)
# out_key = spend_priv * G, KI: spend_priv * Hp(out_key)
out_key = crypto.decodepoint(msg.out_key)
recv_deriv = crypto.decodepoint(msg.recv_deriv)
out_key = crypto_helpers.decodepoint(msg.out_key)
recv_deriv = crypto_helpers.decodepoint(msg.recv_deriv)
received_index = msg.sub_addr_major, msg.sub_addr_minor
spend_priv, ki = monero.generate_tx_spend_and_key_image(
s.creds, out_key, recv_deriv, msg.real_out_idx, received_index
)
ki_enc = crypto.encodepoint(ki)
ki_enc = crypto_helpers.encodepoint(ki)
sig = key_image.generate_ring_signature(ki_enc, ki, [out_key], spend_priv, 0, False)
del spend_priv # spend_priv never leaves the device

@ -1,10 +1,18 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519
from apps.common.keychain import Keychain
from apps.common.paths import Bip32Path
from trezor.enums import MoneroNetworkType
def get_creds(keychain, address_n=None, network_type=None):
from .xmr.crypto import Scalar
from .xmr.credentials import AccountCreds
def get_creds(
keychain: Keychain, address_n: Bip32Path, network_type: MoneroNetworkType
) -> AccountCreds:
from apps.monero.xmr import monero
from apps.monero.xmr.credentials import AccountCreds
@ -18,25 +26,30 @@ def get_creds(keychain, address_n=None, network_type=None):
def compute_tx_key(
spend_key_private: Sc25519,
spend_key_private: Scalar,
tx_prefix_hash: bytes,
salt: bytes,
rand_mult_num: Sc25519,
rand_mult_num: Scalar,
) -> bytes:
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto, crypto_helpers
rand_inp = crypto.sc_add(spend_key_private, rand_mult_num)
passwd = crypto.keccak_2hash(crypto.encodeint(rand_inp) + tx_prefix_hash)
tx_key = crypto.compute_hmac(salt, passwd)
rand_inp = crypto.sc_add_into(None, spend_key_private, rand_mult_num)
passwd = crypto_helpers.keccak_2hash(
crypto_helpers.encodeint(rand_inp) + tx_prefix_hash
)
tx_key = crypto_helpers.compute_hmac(salt, passwd)
return tx_key
def compute_enc_key_host(
view_key_private: Sc25519, tx_prefix_hash: bytes
view_key_private: Scalar, tx_prefix_hash: bytes
) -> tuple[bytes, bytes]:
from apps.monero.xmr import crypto
salt = crypto.random_bytes(32)
passwd = crypto.keccak_2hash(crypto.encodeint(view_key_private) + tx_prefix_hash)
tx_key = crypto.compute_hmac(salt, passwd)
from trezor.crypto import random
from apps.monero.xmr import crypto_helpers
salt = random.bytes(32)
passwd = crypto_helpers.keccak_2hash(
crypto_helpers.encodeint(view_key_private) + tx_prefix_hash
)
tx_key = crypto_helpers.compute_hmac(salt, passwd)
return tx_key, salt

@ -1,4 +1,5 @@
import gc
from typing import TYPE_CHECKING
from trezor import log, utils, wire
from trezor.enums import MessageType
@ -6,9 +7,15 @@ from trezor.enums import MessageType
from apps.common.keychain import auto_keychain
from apps.monero.signing.state import State
if TYPE_CHECKING:
from trezor.messages import MoneroTransactionFinalAck
from apps.common.keychain import Keychain
@auto_keychain(__name__)
async def sign_tx(ctx, received_msg, keychain):
async def sign_tx(
ctx: wire.Context, received_msg, keychain: Keychain
) -> MoneroTransactionFinalAck:
state = State(ctx)
mods = utils.unimport_begin()
@ -34,7 +41,7 @@ async def sign_tx(ctx, received_msg, keychain):
return result_msg
async def sign_tx_dispatch(state, msg, keychain):
async def sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple:
if msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInitRequest:
from apps.monero.signing import step_01_init_transaction
@ -57,14 +64,6 @@ async def sign_tx_dispatch(state, msg, keychain):
),
)
elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInputsPermutationRequest:
from apps.monero.signing import step_03_inputs_permutation
return (
await step_03_inputs_permutation.tsx_inputs_permutation(state, msg.perm),
(MessageType.MoneroTransactionInputViniRequest,),
)
elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInputViniRequest:
from apps.monero.signing import step_04_input_vini

@ -16,8 +16,7 @@ class NotEnoughOutputsError(wire.DataError):
class RctType:
"""
There are several types of monero Ring Confidential Transactions
like RCTTypeFull and RCTTypeSimple but currently we use only Bulletproof2
like RCTTypeFull and RCTTypeSimple but currently we use only CLSAG
"""
Bulletproof2 = 4
CLSAG = 5

@ -3,10 +3,9 @@ from typing import TYPE_CHECKING
from trezor import utils
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto, crypto_helpers
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519
from trezor.messages import (
MoneroTransactionDestinationEntry,
MoneroTransactionSourceEntry,
@ -20,7 +19,10 @@ _BUILD_KEY_BUFFER = bytearray(_SECRET_LENGTH + _DISCRIMINATOR_LENGTH + _INDEX_LE
def _build_key(
secret, discriminator=None, index: int = None, out: bytes = None
secret: bytes,
discriminator: bytes,
index: int | None = None,
out: bytes | None = None,
) -> bytes:
"""
Creates an unique-purpose key
@ -48,81 +50,81 @@ def _build_key(
offset += 1
index = shifted
return crypto.keccak_2hash(key_buff, out)
return crypto_helpers.keccak_2hash(key_buff, out)
def hmac_key_txin(key_hmac, idx: int) -> bytes:
def hmac_key_txin(key_hmac: bytes, idx: int) -> bytes:
"""
(TxSourceEntry[i] || tx.vin[i]) hmac key
"""
return _build_key(key_hmac, b"txin", idx)
def hmac_key_txin_comm(key_hmac, idx: int) -> bytes:
def hmac_key_txin_comm(key_hmac: bytes, idx: int) -> bytes:
"""
pseudo_outputs[i] hmac key. Pedersen commitment for inputs.
"""
return _build_key(key_hmac, b"txin-comm", idx)
def hmac_key_txdst(key_hmac, idx: int) -> bytes:
def hmac_key_txdst(key_hmac: bytes, idx: int) -> bytes:
"""
TxDestinationEntry[i] hmac key
"""
return _build_key(key_hmac, b"txdest", idx)
def hmac_key_txout(key_hmac, idx: int) -> bytes:
def hmac_key_txout(key_hmac: bytes, idx: int) -> bytes:
"""
(TxDestinationEntry[i] || tx.vout[i]) hmac key
"""
return _build_key(key_hmac, b"txout", idx)
def hmac_key_txout_asig(key_hmac, idx: int) -> bytes:
def hmac_key_txout_asig(key_hmac: bytes, idx: int) -> bytes:
"""
rsig[i] hmac key. Range signature HMAC
"""
return _build_key(key_hmac, b"txout-asig", idx)
def enc_key_txin_alpha(key_enc, idx: int) -> bytes:
def enc_key_txin_alpha(key_enc: bytes, idx: int) -> bytes:
"""
Chacha20Poly1305 encryption key for alpha[i] used in Pedersen commitment in pseudo_outs[i]
"""
return _build_key(key_enc, b"txin-alpha", idx)
def enc_key_spend(key_enc, idx: int) -> bytes:
def enc_key_spend(key_enc: bytes, idx: int) -> bytes:
"""
Chacha20Poly1305 encryption key for alpha[i] used in Pedersen commitment in pseudo_outs[i]
"""
return _build_key(key_enc, b"txin-spend", idx)
def enc_key_cout(key_enc, idx: int = None) -> bytes:
def enc_key_cout(key_enc: bytes, idx: int | None = None) -> bytes:
"""
Chacha20Poly1305 encryption key for multisig C values from MLASG.
"""
return _build_key(key_enc, b"cout", idx)
def key_signature(master, idx: int, is_iv=False) -> bytes:
def key_signature(master: bytes, idx: int, is_iv: bool = False) -> bytes:
"""
Generates signature offloading related offloading keys
"""
return _build_key(master, b"sig-iv" if is_iv else b"sig-key", idx)
def det_comm_masks(key_enc, idx: int) -> Sc25519:
def det_comm_masks(key_enc: bytes, idx: int) -> crypto.Scalar:
"""
Deterministic output commitment masks
"""
return crypto.decodeint(_build_key(key_enc, b"out-mask", idx))
return crypto_helpers.decodeint(_build_key(key_enc, b"out-mask", idx))
def gen_hmac_vini(
key, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, idx: int
key: bytes, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, idx: int
) -> bytes:
"""
Computes hmac (TxSourceEntry[i] || tx.vin[i])
@ -151,12 +153,12 @@ def gen_hmac_vini(
kwriter.write(vini_bin)
hmac_key_vini = hmac_key_txin(key, idx)
hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest())
hmac_vini = crypto_helpers.compute_hmac(hmac_key_vini, kwriter.get_digest())
return hmac_vini
def gen_hmac_vouti(
key, dst_entr: MoneroTransactionDestinationEntry, tx_out_bin: bytes, idx: int
key: bytes, dst_entr: MoneroTransactionDestinationEntry, tx_out_bin: bytes, idx: int
) -> bytes:
"""
Generates HMAC for (TxDestinationEntry[i] || tx.vout[i])
@ -169,12 +171,12 @@ def gen_hmac_vouti(
kwriter.write(tx_out_bin)
hmac_key_vouti = hmac_key_txout(key, idx)
hmac_vouti = crypto.compute_hmac(hmac_key_vouti, kwriter.get_digest())
hmac_vouti = crypto_helpers.compute_hmac(hmac_key_vouti, kwriter.get_digest())
return hmac_vouti
def gen_hmac_tsxdest(
key, dst_entr: MoneroTransactionDestinationEntry, idx: int
key: bytes, dst_entr: MoneroTransactionDestinationEntry, idx: int
) -> bytes:
"""
Generates HMAC for TxDestinationEntry[i]
@ -186,7 +188,7 @@ def gen_hmac_tsxdest(
kwriter.write(protobuf.dump_message_buffer(dst_entr))
hmac_key = hmac_key_txdst(key, idx)
hmac_tsxdest = crypto.compute_hmac(hmac_key, kwriter.get_digest())
hmac_tsxdest = crypto_helpers.compute_hmac(hmac_key, kwriter.get_digest())
return hmac_tsxdest

@ -7,8 +7,10 @@ from trezor import log
from apps.monero.xmr import crypto
if TYPE_CHECKING:
from apps.monero.xmr.types import Ge25519, Sc25519
from trezor.wire import Context
from apps.monero.xmr.crypto import Point, Scalar
from apps.monero.xmr.credentials import AccountCreds
from trezor.messages import MoneroTransactionDestinationEntry
Subaddresses = dict[bytes, tuple[int, int]]
@ -17,14 +19,13 @@ class State:
STEP_INIT = const(0)
STEP_INP = const(100)
STEP_PERM = const(200)
STEP_VINI = const(300)
STEP_ALL_IN = const(350)
STEP_OUT = const(400)
STEP_ALL_OUT = const(500)
STEP_SIGN = const(600)
def __init__(self, ctx):
def __init__(self, ctx: Context) -> None:
from apps.monero.xmr.keccak_hasher import KeccakXmrArchive
from apps.monero.xmr.mlsag_hasher import PreMlsagHasher
@ -51,8 +52,8 @@ class State:
- for subaddresses the `r` is commonly denoted as `s`, however it is still just a random number
- the keys are used to derive the one time address and its keys (P = H(A*r)*G + B)
"""
self.tx_priv: Sc25519 = None
self.tx_pub: Ge25519 = None
self.tx_priv: Scalar | None = None
self.tx_pub: Point | None = None
"""
In some cases when subaddresses are used we need more tx_keys
@ -64,58 +65,54 @@ class State:
self.client_version = 0
self.hard_fork = 12
self.input_count = 0
self.input_count: int | None = 0
self.output_count = 0
self.progress_total = 0
self.progress_cur = 0
self.output_change = None
self.fee = 0
self.output_change: "MoneroTransactionDestinationEntry" | None = None
self.fee: int | None = 0
self.tx_type = 0
# wallet sub-address major index
self.account_idx = 0
self.account_idx: int | None = 0
# contains additional tx keys if need_additional_tx_keys is True
self.additional_tx_private_keys: list[Sc25519] = []
self.additional_tx_public_keys: list[bytes] = []
self.additional_tx_private_keys: list[Scalar] = []
self.additional_tx_public_keys: list[bytes] | None = []
# currently processed input/output index
self.current_input_index = -1
self.current_output_index = -1
self.current_output_index: int | None = -1
self.is_processing_offloaded = False
# for pseudo_out recomputation from new mask
self.input_last_amount = 0
self.input_last_amount: int | None = 0
self.summary_inputs_money = 0
self.summary_outs_money = 0
self.summary_inputs_money: int | None = 0
self.summary_outs_money: int | None = 0
# output commitments
self.output_pk_commitments: list[bytes] = []
self.output_pk_commitments: list[bytes] | None = []
self.output_amounts: list[int] = []
self.output_amounts: list[int] | None = []
# output *range proof* masks. HP10+ makes them deterministic.
self.output_masks: list[Sc25519] = []
self.output_masks: list[Scalar] | None = []
# the range proofs are calculated in batches, this denotes the grouping
self.rsig_grouping: list[int] = []
self.rsig_grouping: list[int] | None = []
# is range proof computing offloaded or not
self.rsig_offload = False
self.rsig_offload: bool | None = False
# sum of all inputs' pseudo out masks
self.sumpouts_alphas: Sc25519 = crypto.sc_0()
self.sumpouts_alphas: Scalar = crypto.Scalar(0)
# sum of all output' pseudo out masks
self.sumout: Sc25519 = crypto.sc_0()
self.sumout: Scalar = crypto.Scalar(0)
self.subaddresses: Subaddresses = {}
self.subaddresses: Subaddresses | None = {}
# TX_EXTRA_NONCE extra field for tx.extra, due to sort_tx_extra()
self.extra_nonce = None
# contains an array where each item denotes the input's position
# (inputs are sorted by key images)
self.source_permutation: list[int] = []
self.extra_nonce: bytes | None = None
# Last key image seen. Used for input permutation correctness check
self.last_ki: bytes | None = None
@ -124,14 +121,14 @@ class State:
self.opening_key: bytes | None = None
# Step transition automaton
self.last_step = self.STEP_INIT
self.last_step: int | None = self.STEP_INIT
"""
Tx prefix hasher/hash. We use the hasher to incrementally hash and then
store the final hash in tx_prefix_hash.
See Monero-Trezor documentation section 3.3 for more details.
"""
self.tx_prefix_hasher = KeccakXmrArchive()
self.tx_prefix_hasher: KeccakXmrArchive | None = KeccakXmrArchive()
self.tx_prefix_hash: bytes | None = None
"""
@ -139,10 +136,10 @@ class State:
Contains tx_prefix_hash.
See Monero-Trezor documentation section 3.3 for more details.
"""
self.full_message_hasher = PreMlsagHasher()
self.full_message_hasher: PreMlsagHasher | None = PreMlsagHasher()
self.full_message: bytes | None = None
def mem_trace(self, x=None, collect=False):
def mem_trace(self, x=None, collect: bool = False) -> None:
if __debug__:
log.debug(
__name__,

@ -7,10 +7,9 @@ from typing import TYPE_CHECKING
from apps.monero import layout, misc, signing
from apps.monero.signing.state import State
from apps.monero.xmr import crypto, monero
from apps.monero.xmr import crypto, crypto_helpers, monero
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519, Ge25519
from trezor.messages import (
MoneroAccountPublicAddress,
MoneroTransactionData,
@ -34,16 +33,17 @@ async def init_transaction(
state.creds = misc.get_creds(keychain, address_n, network_type)
state.client_version = tsx_data.client_version or 0
if state.client_version == 0:
if state.client_version < 3:
raise ValueError("Client version not supported")
state.fee = state.fee if state.fee > 0 else 0
state.tx_priv = crypto.random_scalar()
state.tx_pub = crypto.scalarmult_base(state.tx_priv)
state.tx_pub = crypto.scalarmult_base_into(None, state.tx_priv)
state.mem_trace(1)
state.input_count = tsx_data.num_inputs
state.output_count = len(tsx_data.outputs)
assert state.input_count is not None
state.progress_total = 4 + 3 * state.input_count + state.output_count
state.progress_cur = 0
@ -58,16 +58,14 @@ async def init_transaction(
# Basic transaction parameters
state.output_change = tsx_data.change_dts
state.mixin = tsx_data.mixin
state.fee = tsx_data.fee
state.account_idx = tsx_data.account
state.last_step = state.STEP_INIT
state.tx_type = signing.RctType.CLSAG
if tsx_data.hard_fork:
state.hard_fork = tsx_data.hard_fork
state.tx_type = (
signing.RctType.CLSAG if state.hard_fork >= 13 else signing.RctType.Bulletproof2
)
if state.hard_fork < 13:
raise ValueError("Unsupported hard-fork version")
# Ensure change is correct
_check_change(state, tsx_data.outputs)
@ -123,7 +121,9 @@ async def init_transaction(
return MoneroTransactionInitAck(hmacs=hmacs, rsig_data=rsig_data)
def _check_subaddresses(state: State, outputs: list[MoneroTransactionDestinationEntry]):
def _check_subaddresses(
state: State, outputs: list[MoneroTransactionDestinationEntry]
) -> None:
"""
Using subaddresses leads to a few poorly documented exceptions.
@ -156,8 +156,10 @@ def _check_subaddresses(state: State, outputs: list[MoneroTransactionDestination
# we set (override) the tx pubkey to R=r*D and no additional
# tx keys are needed
if num_stdaddresses == 0 and num_subaddresses == 1:
state.tx_pub = crypto.scalarmult(
crypto.decodepoint(single_dest_subaddress.spend_public_key), state.tx_priv
state.tx_pub = crypto.scalarmult_into(
None,
crypto_helpers.decodepoint(single_dest_subaddress.spend_public_key),
state.tx_priv,
)
# if a subaddress is used and either standard address is as well
@ -178,11 +180,12 @@ def _get_primary_change_address(state: State) -> MoneroAccountPublicAddress:
state.creds.view_key_private, state.creds.spend_key_public, state.account_idx, 0
)
return MoneroAccountPublicAddress(
view_public_key=crypto.encodepoint(C), spend_public_key=crypto.encodepoint(D)
view_public_key=crypto_helpers.encodepoint(C),
spend_public_key=crypto_helpers.encodepoint(D),
)
def _check_rsig_data(state: State, rsig_data: MoneroTransactionRsigData):
def _check_rsig_data(state: State, rsig_data: MoneroTransactionRsigData) -> None:
"""
There are two types of monero ring confidential transactions:
1. RCTTypeFull = 1 (used if num_inputs == 1 && Borromean)
@ -211,7 +214,7 @@ def _check_rsig_data(state: State, rsig_data: MoneroTransactionRsigData):
_check_grouping(state)
def _check_grouping(state: State):
def _check_grouping(state: State) -> None:
acc = 0
for x in state.rsig_grouping:
if x is None or x <= 0:
@ -222,7 +225,9 @@ def _check_grouping(state: State):
raise ValueError("Invalid grouping")
def _check_change(state: State, outputs: list[MoneroTransactionDestinationEntry]):
def _check_change(
state: State, outputs: list[MoneroTransactionDestinationEntry]
) -> None:
"""
Check if the change address in state.output_change (from `tsx_data.outputs`) is
a) among tx outputs
@ -269,7 +274,7 @@ def _check_change(state: State, outputs: list[MoneroTransactionDestinationEntry]
raise signing.ChangeAddressError("Change address differs from ours")
def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData):
def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData) -> None:
"""
Generate master key H( H(TsxData || tx_priv) || rand )
"""
@ -278,16 +283,16 @@ def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData):
writer = get_keccak_writer()
writer.write(protobuf.dump_message_buffer(tsx_data))
writer.write(crypto.encodeint(state.tx_priv))
writer.write(crypto_helpers.encodeint(state.tx_priv))
master_key = crypto.keccak_2hash(
writer.get_digest() + crypto.encodeint(crypto.random_scalar())
master_key = crypto_helpers.keccak_2hash(
writer.get_digest() + crypto_helpers.encodeint(crypto.random_scalar())
)
state.key_hmac = crypto.keccak_2hash(b"hmac" + master_key)
state.key_enc = crypto.keccak_2hash(b"enc" + master_key)
state.key_hmac = crypto_helpers.keccak_2hash(b"hmac" + master_key)
state.key_enc = crypto_helpers.keccak_2hash(b"enc" + master_key)
def _precompute_subaddr(state: State, account: int, indices: list[int]):
def _precompute_subaddr(state: State, account: int, indices: list[int]) -> None:
"""
Precomputes subaddresses for account (major) and list of indices (minors)
Subaddresses have to be stored in encoded form - unique representation.
@ -296,7 +301,7 @@ def _precompute_subaddr(state: State, account: int, indices: list[int]):
monero.compute_subaddresses(state.creds, account, indices, state.subaddresses)
def _process_payment_id(state: State, tsx_data: MoneroTransactionData):
def _process_payment_id(state: State, tsx_data: MoneroTransactionData) -> None:
"""
Writes payment id to the `extra` field under the TX_EXTRA_NONCE = 0x02 tag.
@ -316,14 +321,14 @@ def _process_payment_id(state: State, tsx_data: MoneroTransactionData):
if not tsx_data.payment_id or len(tsx_data.payment_id) == 8:
view_key_pub_enc = _get_key_for_payment_id_encryption(
tsx_data, state.change_address(), state.client_version > 0
tsx_data, state.change_address(), True
)
if not tsx_data.payment_id:
return
elif len(tsx_data.payment_id) == 8:
view_key_pub = crypto.decodepoint(view_key_pub_enc)
view_key_pub = crypto_helpers.decodepoint(view_key_pub_enc)
payment_id_encr = _encrypt_payment_id(
tsx_data.payment_id, view_key_pub, state.tx_priv
)
@ -354,7 +359,7 @@ def _process_payment_id(state: State, tsx_data: MoneroTransactionData):
def _get_key_for_payment_id_encryption(
tsx_data: MoneroTransactionData,
change_addr=None,
change_addr: MoneroAccountPublicAddress | None = None,
add_dummy_payment_id: bool = False,
) -> bytes:
"""
@ -366,7 +371,8 @@ def _get_key_for_payment_id_encryption(
from trezor.messages import MoneroAccountPublicAddress
addr = MoneroAccountPublicAddress(
spend_public_key=crypto.NULL_KEY_ENC, view_public_key=crypto.NULL_KEY_ENC
spend_public_key=crypto_helpers.NULL_KEY_ENC,
view_public_key=crypto_helpers.NULL_KEY_ENC,
)
count = 0
for dest in tsx_data.outputs:
@ -390,23 +396,23 @@ def _get_key_for_payment_id_encryption(
if count == 0 and change_addr:
return change_addr.view_public_key
if addr.view_public_key == crypto.NULL_KEY_ENC:
if addr.view_public_key == crypto_helpers.NULL_KEY_ENC:
raise ValueError("Invalid key")
return addr.view_public_key
def _encrypt_payment_id(
payment_id: bytes, public_key: Ge25519, secret_key: Sc25519
payment_id: bytes, public_key: crypto.Point, secret_key: crypto.Scalar
) -> bytes:
"""
Encrypts payment_id hex.
Used in the transaction extra. Only recipient is able to decrypt.
"""
derivation_p = crypto.generate_key_derivation(public_key, secret_key)
derivation_p = crypto_helpers.generate_key_derivation(public_key, secret_key)
derivation = bytearray(33)
derivation = crypto.encodepoint_into(derivation, derivation_p)
derivation[32] = 0x8D # ENCRYPTED_PAYMENT_ID_TAIL
hash = crypto.cn_fast_hash(derivation)
hash = crypto.fast_hash_into(None, derivation)
pm_copy = bytearray(payment_id)
return crypto.xor8(pm_copy, hash)
return crypto_helpers.xor8(pm_copy, hash)

@ -14,12 +14,11 @@ key derived for exactly this purpose.
from typing import TYPE_CHECKING
from apps.monero import layout
from apps.monero.xmr import crypto, monero, serialize
from apps.monero.xmr import crypto, crypto_helpers, monero, serialize
from .state import State
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519, Ge25519
from trezor.messages import MoneroTransactionSourceEntry
from trezor.messages import MoneroTransactionSetInputAck
@ -28,7 +27,7 @@ async def set_input(
state: State, src_entr: MoneroTransactionSourceEntry
) -> MoneroTransactionSetInputAck:
from trezor.messages import MoneroTransactionSetInputAck
from apps.monero.xmr.crypto import chacha_poly
from apps.monero.xmr import chacha_poly
from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey
from apps.monero.signing import offloading_keys
@ -49,9 +48,11 @@ async def set_input(
# Secrets derivation
# the UTXO's one-time address P
out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output].key.dest)
out_key = crypto_helpers.decodepoint(
src_entr.outputs[src_entr.real_output].key.dest
)
# the tx_pub of our UTXO stored inside its transaction
tx_key = crypto.decodepoint(src_entr.real_out_tx_key)
tx_key = crypto_helpers.decodepoint(src_entr.real_out_tx_key)
additional_tx_pub_key = _get_additional_public_key(src_entr)
# Calculates `derivation = Ra`, private spend key `x = H(Ra||i) + b` to be able
@ -70,7 +71,7 @@ async def set_input(
# Construct tx.vin
# If multisig is used then ki in vini should be src_entr.multisig_kLRki.ki
vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki))
vini = TxinToKey(amount=src_entr.amount, k_image=crypto_helpers.encodepoint(ki))
vini.key_offsets = _absolute_output_offsets_to_relative(
[x.idx for x in src_entr.outputs]
)
@ -92,22 +93,22 @@ async def set_input(
# PseudoOuts commitment, alphas stored to state
alpha, pseudo_out = _gen_commitment(state, src_entr.amount)
pseudo_out = crypto.encodepoint(pseudo_out)
pseudo_out = crypto_helpers.encodepoint(pseudo_out)
# The alpha is encrypted and passed back for storage
pseudo_out_hmac = crypto.compute_hmac(
pseudo_out_hmac = crypto_helpers.compute_hmac(
offloading_keys.hmac_key_txin_comm(state.key_hmac, state.current_input_index),
pseudo_out,
)
alpha_enc = chacha_poly.encrypt_pack(
offloading_keys.enc_key_txin_alpha(state.key_enc, state.current_input_index),
crypto.encodeint(alpha),
crypto_helpers.encodeint(alpha),
)
spend_enc = chacha_poly.encrypt_pack(
offloading_keys.enc_key_spend(state.key_enc, state.current_input_index),
crypto.encodeint(xi),
crypto_helpers.encodeint(xi),
)
state.last_step = state.STEP_INP
@ -127,7 +128,7 @@ async def set_input(
)
def _gen_commitment(state: State, in_amount: int) -> tuple[Sc25519, Ge25519]:
def _gen_commitment(state: State, in_amount: int) -> tuple[crypto.Scalar, crypto.Point]:
"""
Computes Pedersen commitment - pseudo outs
Here is slight deviation from the original protocol.
@ -139,8 +140,8 @@ def _gen_commitment(state: State, in_amount: int) -> tuple[Sc25519, Ge25519]:
Returns pseudo_out
"""
alpha = crypto.random_scalar()
state.sumpouts_alphas = crypto.sc_add(state.sumpouts_alphas, alpha)
return alpha, crypto.gen_commitment(alpha, in_amount)
state.sumpouts_alphas = crypto.sc_add_into(None, state.sumpouts_alphas, alpha)
return alpha, crypto.gen_commitment_into(None, alpha, in_amount)
def _absolute_output_offsets_to_relative(off: list[int]) -> list[int]:
@ -161,10 +162,10 @@ def _absolute_output_offsets_to_relative(off: list[int]) -> list[int]:
def _get_additional_public_key(
src_entr: MoneroTransactionSourceEntry,
) -> Ge25519 | None:
) -> crypto.Point | None:
additional_tx_pub_key = None
if len(src_entr.real_out_additional_tx_keys) == 1: # compression
additional_tx_pub_key = crypto.decodepoint(
additional_tx_pub_key = crypto_helpers.decodepoint(
src_entr.real_out_additional_tx_keys[0]
)
elif src_entr.real_out_additional_tx_keys:
@ -172,7 +173,7 @@ def _get_additional_public_key(
src_entr.real_out_additional_tx_keys
):
raise ValueError("Wrong number of additional derivations")
additional_tx_pub_key = crypto.decodepoint(
additional_tx_pub_key = crypto_helpers.decodepoint(
src_entr.real_out_additional_tx_keys[src_entr.real_output_in_tx_index]
)
return additional_tx_pub_key

@ -1,57 +0,0 @@
"""
Inputs in transaction need to be sorted by their key image, otherwise the
transaction is rejected. The sorting is done on host and then sent here in
the MoneroTransactionInputsPermutationRequest message.
The message contains just a simple array where each item stands for the
input's position in the transaction.
We do not do the actual sorting here (we do not store the complete input
data anyway, so we can't) we just save the array to the state and use
it later when needed.
New protocol version (CL3) does not store the permutation. The permutation
correctness is checked by checking the number of elements,
HMAC correctness (host sends original sort idx) and ordering check
on the key images. This step is skipped.
"""
from typing import TYPE_CHECKING
from apps.monero.layout import transaction_step
from .state import State
if TYPE_CHECKING:
from trezor.messages import MoneroTransactionInputsPermutationAck
async def tsx_inputs_permutation(
state: State, permutation: list[int]
) -> MoneroTransactionInputsPermutationAck:
"""
Set permutation on the inputs - sorted by key image on host.
"""
from trezor.messages import MoneroTransactionInputsPermutationAck
await transaction_step(state, state.STEP_PERM)
if state.last_step != state.STEP_INP:
raise ValueError("Invalid state transition")
if len(permutation) != state.input_count:
raise ValueError("Invalid permutation size")
if state.current_input_index != state.input_count - 1:
raise ValueError("Invalid input count")
_check_permutation(permutation)
state.source_permutation = permutation
state.current_input_index = -1
state.last_step = state.STEP_PERM
return MoneroTransactionInputsPermutationAck()
def _check_permutation(permutation: list[int]):
for n in range(len(permutation)):
if n not in permutation:
raise ValueError("Invalid permutation")

@ -28,12 +28,12 @@ async def input_vini(
from trezor.messages import MoneroTransactionInputViniAck
await layout.transaction_step(state, state.STEP_VINI, state.current_input_index + 1)
if state.last_step not in (state.STEP_INP, state.STEP_PERM, state.STEP_VINI):
if state.last_step not in (state.STEP_INP, state.STEP_VINI):
raise ValueError("Invalid state transition")
if state.current_input_index >= state.input_count:
raise ValueError("Too many inputs")
if state.client_version >= 2 and state.last_step < state.STEP_VINI:
if state.last_step < state.STEP_VINI:
state.current_input_index = -1
state.last_ki = None
@ -44,9 +44,7 @@ async def input_vini(
state.key_hmac,
src_entr,
vini_bin,
state.source_permutation[state.current_input_index]
if state.client_version <= 1
else orig_idx,
orig_idx,
)
if not crypto.ct_equals(hmac_vini_comp, vini_hmac):
raise ValueError("HMAC is not correct")

@ -27,7 +27,7 @@ async def all_inputs_set(state: State) -> MoneroTransactionAllInputsSetAck:
raise ValueError("Invalid input count")
# The sum of the masks must match the input masks sum.
state.sumout = crypto.sc_init(0)
state.sumout = crypto.Scalar()
state.last_step = state.STEP_ALL_IN
resp = MoneroTransactionAllInputsSetAck()
return resp

@ -9,12 +9,11 @@ from trezor import utils
from apps.monero import layout, signing
from apps.monero.signing import offloading_keys
from apps.monero.xmr import crypto, serialize
from apps.monero.xmr import crypto, crypto_helpers, serialize
from .state import State
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519, Ge25519
from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple
from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof
from trezor.messages import (
@ -170,7 +169,7 @@ def _validate(
def _compute_tx_keys(
state: State, dst_entr: MoneroTransactionDestinationEntry
) -> tuple[Ge25519, Sc25519]:
) -> tuple[crypto.Point, crypto.Scalar]:
"""Computes tx_out_key, amount_key"""
if state.is_processing_offloaded:
@ -181,24 +180,26 @@ def _compute_tx_keys(
# derivation = a*R or r*A or s*C
derivation = _set_out_derivation(state, dst_entr, additional_txkey_priv)
# amount key = H_s(derivation || i)
amount_key = crypto.derivation_to_scalar(derivation, state.current_output_index)
amount_key = crypto_helpers.derivation_to_scalar(
derivation, state.current_output_index
)
# one-time destination address P = H_s(derivation || i)*G + B
tx_out_key = crypto.derive_public_key(
tx_out_key = crypto_helpers.derive_public_key(
derivation,
state.current_output_index,
crypto.decodepoint(dst_entr.addr.spend_public_key),
crypto_helpers.decodepoint(dst_entr.addr.spend_public_key),
)
del (derivation, additional_txkey_priv)
from apps.monero.xmr import monero
mask = monero.commitment_mask(crypto.encodeint(amount_key))
mask = monero.commitment_mask(crypto_helpers.encodeint(amount_key))
state.output_masks.append(mask)
return tx_out_key, amount_key
def _set_out_tx_out(
state: State, dst_entr: MoneroTransactionDestinationEntry, tx_out_key: Ge25519
state: State, dst_entr: MoneroTransactionDestinationEntry, tx_out_key: crypto.Point
) -> tuple[bytes, bytes]:
"""
Manually serializes TxOut(0, TxoutToKey(key)) and calculates hmac.
@ -223,7 +224,7 @@ def _set_out_tx_out(
def _range_proof(
state: State, rsig_data: MoneroTransactionRsigData
) -> tuple[MoneroTransactionRsigData, Sc25519]:
) -> tuple[MoneroTransactionRsigData, crypto.Scalar]:
"""
Computes rangeproof and handles range proof offloading logic.
@ -257,7 +258,9 @@ def _range_proof(
# If not last, do not proceed to the BP processing.
if not last_in_batch:
rsig_data_new = (
_return_rsig_data(mask=crypto.encodeint(mask)) if offload_mask else None
_return_rsig_data(mask=crypto_helpers.encodeint(mask))
if offload_mask
else None
)
return rsig_data_new, mask
@ -282,7 +285,7 @@ def _range_proof(
# Construct new rsig data to send back to the host.
rsig_data_new = _return_rsig_data(
rsig, crypto.encodeint(mask) if offload_mask else None
rsig, crypto_helpers.encodeint(mask) if offload_mask else None
)
if state.current_output_index + 1 == state.output_count and (
@ -386,7 +389,7 @@ def _dump_rsig_bp(rsig: Bulletproof) -> bytes:
def _return_rsig_data(
rsig: bytes = None, mask: bytes = None
rsig: bytes | None = None, mask: bytes | None = None
) -> MoneroTransactionRsigData:
if rsig is None and mask is None:
return None
@ -405,17 +408,23 @@ def _return_rsig_data(
def _get_ecdh_info_and_out_pk(
state: State, tx_out_key: Ge25519, amount: int, mask: Sc25519, amount_key: Sc25519
state: State,
tx_out_key: crypto.Point,
amount: int,
mask: crypto.Scalar,
amount_key: crypto.Scalar,
) -> tuple[bytes, bytes, bytes]:
"""
Calculates the Pedersen commitment C = aG + bH and returns it as CtKey.
Also encodes the two items - `mask` and `amount` - into ecdh info,
so the recipient is able to reconstruct the commitment.
"""
out_pk_dest = crypto.encodepoint(tx_out_key)
out_pk_commitment = crypto.encodepoint(crypto.gen_commitment(mask, amount))
out_pk_dest = crypto_helpers.encodepoint(tx_out_key)
out_pk_commitment = crypto_helpers.encodepoint(
crypto.gen_commitment_into(None, mask, amount)
)
crypto.sc_add_into(state.sumout, state.sumout, mask)
ecdh_info = _ecdh_encode(amount, crypto.encodeint(amount_key))
ecdh_info = _ecdh_encode(amount, crypto_helpers.encodeint(amount_key))
# Manual ECDH info serialization
ecdh_info_bin = _serialize_ecdh(ecdh_info)
@ -442,7 +451,7 @@ def _ecdh_hash(shared_sec: bytes) -> bytes:
data = bytearray(38)
data[0:6] = b"amount"
data[6:] = shared_sec
return crypto.cn_fast_hash(data)
return crypto.fast_hash_into(None, data)
def _ecdh_encode(amount: int, amount_key: bytes) -> EcdhTuple:
@ -451,16 +460,16 @@ def _ecdh_encode(amount: int, amount_key: bytes) -> EcdhTuple:
"""
from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple
ecdh_info = EcdhTuple(mask=crypto.NULL_KEY_ENC, amount=bytearray(32))
amnt = crypto.sc_init(amount)
ecdh_info = EcdhTuple(mask=crypto_helpers.NULL_KEY_ENC, amount=bytearray(32))
amnt = crypto.Scalar(amount)
crypto.encodeint_into(ecdh_info.amount, amnt)
crypto.xor8(ecdh_info.amount, _ecdh_hash(amount_key))
crypto_helpers.xor8(ecdh_info.amount, _ecdh_hash(amount_key))
return ecdh_info
def _set_out_additional_keys(
state: State, dst_entr: MoneroTransactionDestinationEntry
) -> Sc25519:
) -> crypto.Scalar:
"""
If needed (decided in step 1), additional tx keys are calculated
for this particular output.
@ -472,15 +481,15 @@ def _set_out_additional_keys(
if dst_entr.is_subaddress:
# R=r*D
additional_txkey = crypto.decodepoint(dst_entr.addr.spend_public_key)
additional_txkey = crypto_helpers.decodepoint(dst_entr.addr.spend_public_key)
crypto.scalarmult_into(
additional_txkey, additional_txkey, additional_txkey_priv
)
else:
# R=r*G
additional_txkey = crypto.scalarmult_base(additional_txkey_priv)
additional_txkey = crypto.scalarmult_base_into(None, additional_txkey_priv)
state.additional_tx_public_keys.append(crypto.encodepoint(additional_txkey))
state.additional_tx_public_keys.append(crypto_helpers.encodepoint(additional_txkey))
state.additional_tx_private_keys.append(additional_txkey_priv)
return additional_txkey_priv
@ -488,8 +497,8 @@ def _set_out_additional_keys(
def _set_out_derivation(
state: State,
dst_entr: MoneroTransactionDestinationEntry,
additional_txkey_priv: Sc25519,
) -> Ge25519:
additional_txkey_priv: crypto.Scalar,
) -> crypto.Point:
"""
Calculates derivation which is then used in the one-time address as
`P = H(derivation)*G + B`.
@ -504,7 +513,7 @@ def _set_out_derivation(
change_addr = state.change_address()
if change_addr and addr_eq(dst_entr.addr, change_addr):
# sending change to yourself; derivation = a*R
derivation = crypto.generate_key_derivation(
derivation = crypto_helpers.generate_key_derivation(
state.tx_pub, state.creds.view_key_private
)
@ -514,8 +523,8 @@ def _set_out_derivation(
deriv_priv = additional_txkey_priv
else:
deriv_priv = state.tx_priv
derivation = crypto.generate_key_derivation(
crypto.decodepoint(dst_entr.addr.view_public_key), deriv_priv
derivation = crypto_helpers.generate_key_derivation(
crypto_helpers.decodepoint(dst_entr.addr.view_public_key), deriv_priv
)
return derivation

@ -62,6 +62,7 @@ async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck:
state.current_output_index = None
state.current_input_index = -1
assert state.full_message_hasher is not None
state.full_message = state.full_message_hasher.get_digest()
state.full_message_hasher = None
state.output_pk_commitments = None
@ -79,7 +80,7 @@ async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck:
)
def _validate(state: State):
def _validate(state: State) -> None:
if state.last_step != state.STEP_OUT:
raise ValueError("Invalid state transition")
if state.current_output_index + 1 != state.output_count:
@ -143,7 +144,7 @@ def _set_tx_extra(state: State) -> bytes:
return extra
def _set_tx_prefix(state: State, extra: bytes):
def _set_tx_prefix(state: State, extra: bytes) -> None:
"""
Adds `extra` to the tx_prefix_hash, which is the last needed item,
so the tx_prefix_hash is now complete and can be incorporated
@ -160,7 +161,7 @@ def _set_tx_prefix(state: State, extra: bytes):
state.full_message_hasher.set_message(state.tx_prefix_hash)
def _out_pk(state: State):
def _out_pk(state: State) -> None:
"""
Hashes out_pk into the full message.
"""

@ -1,5 +1,5 @@
"""
Generates a MLSAG signature for one input.
Generates a clsag signature for one input.
Mask Balancing.
Sum of input masks has to be equal to the sum of output masks.
@ -16,7 +16,7 @@ from typing import TYPE_CHECKING
from trezor import utils
from apps.monero import layout
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto, crypto_helpers
from .state import State
@ -61,11 +61,7 @@ async def sign_input(
if pseudo_out_alpha_enc is None:
raise ValueError("SimpleRCT requires pseudo_out's mask but none provided")
input_position = (
state.source_permutation[state.current_input_index]
if state.client_version <= 1
else orig_idx
)
input_position = orig_idx
mods = utils.unimport_begin()
# Check input's HMAC
@ -88,9 +84,9 @@ async def sign_input(
gc.collect()
state.mem_trace(1, True)
from apps.monero.xmr.crypto import chacha_poly
from apps.monero.xmr import chacha_poly
pseudo_out_alpha = crypto.decodeint(
pseudo_out_alpha = crypto_helpers.decodeint(
chacha_poly.decrypt_pack(
offloading_keys.enc_key_txin_alpha(state.key_enc, input_position),
bytes(pseudo_out_alpha_enc),
@ -101,31 +97,33 @@ async def sign_input(
if input_position + 1 == state.input_count:
# Recompute the lash alpha so the sum holds
state.mem_trace("Correcting alpha")
alpha_diff = crypto.sc_sub(state.sumout, state.sumpouts_alphas)
alpha_diff = crypto.sc_sub_into(None, state.sumout, state.sumpouts_alphas)
crypto.sc_add_into(pseudo_out_alpha, pseudo_out_alpha, alpha_diff)
pseudo_out_c = crypto.gen_commitment(pseudo_out_alpha, state.input_last_amount)
pseudo_out_c = crypto.gen_commitment_into(
None, pseudo_out_alpha, state.input_last_amount
)
else:
if input_position + 1 == state.input_count:
utils.ensure(
crypto.sc_eq(state.sumpouts_alphas, state.sumout), "Sum eq error"
crypto.sc_eq(state.sumpouts_alphas, state.sumout) != 0, "Sum eq error"
)
# both pseudo_out and its mask were offloaded so we need to
# validate pseudo_out's HMAC and decrypt the alpha
pseudo_out_hmac_comp = crypto.compute_hmac(
pseudo_out_hmac_comp = crypto_helpers.compute_hmac(
offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position),
pseudo_out,
)
if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac):
raise ValueError("HMAC is not correct")
pseudo_out_c = crypto.decodepoint(pseudo_out)
pseudo_out_c = crypto_helpers.decodepoint(pseudo_out)
state.mem_trace(2, True)
# Spending secret
spend_key = crypto.decodeint(
spend_key = crypto_helpers.decodeint(
chacha_poly.decrypt_pack(
offloading_keys.enc_key_spend(state.key_enc, input_position),
bytes(spend_enc),
@ -147,28 +145,29 @@ async def sign_input(
from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey
index = src_entr.real_output
input_secret_key = CtKey(spend_key, crypto.decodeint(src_entr.mask))
input_secret_key = CtKey(spend_key, crypto_helpers.decodeint(src_entr.mask))
# Private key correctness test
utils.ensure(
crypto.point_eq(
crypto.decodepoint(src_entr.outputs[src_entr.real_output].key.dest),
crypto.scalarmult_base(input_secret_key.dest),
crypto_helpers.decodepoint(src_entr.outputs[src_entr.real_output].key.dest),
crypto.scalarmult_base_into(None, input_secret_key.dest),
),
"Real source entry's destination does not equal spend key's",
)
utils.ensure(
crypto.point_eq(
crypto.decodepoint(src_entr.outputs[src_entr.real_output].key.commitment),
crypto.gen_commitment(input_secret_key.mask, src_entr.amount),
crypto_helpers.decodepoint(
src_entr.outputs[src_entr.real_output].key.commitment
),
crypto.gen_commitment_into(None, input_secret_key.mask, src_entr.amount),
),
"Real source entry's mask does not equal spend key's",
)
state.mem_trace(4, True)
from apps.monero.xmr import mlsag
from apps.monero import signing
from apps.monero.xmr import clsag
mg_buffer = []
ring_pubkeys = [x.key for x in src_entr.outputs if x]
@ -177,43 +176,32 @@ async def sign_input(
state.mem_trace(5, True)
if state.tx_type == signing.RctType.CLSAG:
state.mem_trace("CLSAG")
mlsag.generate_clsag_simple(
state.full_message,
ring_pubkeys,
input_secret_key,
pseudo_out_alpha,
pseudo_out_c,
index,
mg_buffer,
)
else:
mlsag.generate_mlsag_simple(
state.full_message,
ring_pubkeys,
input_secret_key,
pseudo_out_alpha,
pseudo_out_c,
index,
mg_buffer,
)
assert state.full_message is not None
state.mem_trace("CLSAG")
clsag.generate_clsag_simple(
state.full_message,
ring_pubkeys,
input_secret_key,
pseudo_out_alpha,
pseudo_out_c,
index,
mg_buffer,
)
del (CtKey, input_secret_key, pseudo_out_alpha, mlsag, ring_pubkeys)
del (CtKey, input_secret_key, pseudo_out_alpha, clsag, ring_pubkeys)
state.mem_trace(6, True)
from trezor.messages import MoneroTransactionSignInputAck
# Encrypt signature, reveal once protocol finishes OK
if state.client_version >= 3:
utils.unimport_end(mods)
state.mem_trace(7, True)
mg_buffer = _protect_signature(state, mg_buffer)
utils.unimport_end(mods)
state.mem_trace(7, True)
mg_buffer = _protect_signature(state, mg_buffer)
state.mem_trace(8, True)
state.last_step = state.STEP_SIGN
return MoneroTransactionSignInputAck(
signature=mg_buffer, pseudo_out=crypto.encodepoint(pseudo_out_c)
signature=mg_buffer, pseudo_out=crypto_helpers.encodepoint(pseudo_out_c)
)

@ -8,19 +8,14 @@ so we can recover it just from the transaction and the spend key.
The private tx keys are used in other numerous Monero features.
"""
from typing import TYPE_CHECKING
from trezor.crypto import random
from trezor.messages import MoneroTransactionFinalAck
from apps.monero import misc
from apps.monero.xmr import crypto
from apps.monero.xmr.crypto import chacha_poly
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers
from .state import State
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519
def final_msg(state: State) -> MoneroTransactionFinalAck:
if state.last_step != state.STEP_SIGN:
@ -32,8 +27,8 @@ def final_msg(state: State) -> MoneroTransactionFinalAck:
state.creds.spend_key_private, state.tx_prefix_hash
)
key_buff = crypto.encodeint(state.tx_priv) + b"".join(
[crypto.encodeint(x) for x in state.additional_tx_private_keys]
key_buff = crypto_helpers.encodeint(state.tx_priv) + b"".join(
[crypto_helpers.encodeint(x) for x in state.additional_tx_private_keys]
)
tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff)
state.last_step = None
@ -48,12 +43,12 @@ def final_msg(state: State) -> MoneroTransactionFinalAck:
def _compute_tx_key(
spend_key_private: Sc25519, tx_prefix_hash: bytes
spend_key_private: crypto.Scalar, tx_prefix_hash: bytes
) -> tuple[bytes, bytes, bytes]:
salt = crypto.random_bytes(32)
salt = random.bytes(32)
rand_mult_num = crypto.random_scalar()
rand_mult = crypto.encodeint(rand_mult_num)
rand_mult = crypto_helpers.encodeint(rand_mult_num)
tx_key = misc.compute_tx_key(spend_key_private, tx_prefix_hash, salt, rand_mult_num)
return tx_key, salt, rand_mult

@ -0,0 +1 @@
from trezor.crypto import monero as crypto # noqa: F401

@ -1,11 +1,11 @@
from typing import TYPE_CHECKING
from trezor.crypto import monero as tcry
from trezor.enums import MoneroNetworkType
from apps.monero.xmr.networks import NetworkTypes, net_version
from apps.monero.xmr.networks import net_version
if TYPE_CHECKING:
from apps.monero.xmr.types import Ge25519
from trezor.messages import MoneroAccountPublicAddress
from trezor.messages import MoneroTransactionDestinationEntry
@ -18,7 +18,7 @@ def addr_to_hash(addr: MoneroAccountPublicAddress) -> bytes:
def encode_addr(
version, spend_pub: Ge25519, view_pub: Ge25519, payment_id: bytes | None = None
version, spend_pub: bytes, view_pub: bytes, payment_id: bytes | None = None
) -> str:
"""
Builds Monero address from public keys
@ -40,8 +40,10 @@ def decode_addr(addr: bytes) -> tuple[int, bytes, bytes]:
def public_addr_encode(
pub_addr: MoneroAccountPublicAddress, is_sub=False, net=NetworkTypes.MAINNET
):
pub_addr: MoneroAccountPublicAddress,
is_sub: bool = False,
net: MoneroNetworkType = MoneroNetworkType.MAINNET,
) -> str:
"""
Encodes public address to Monero address
"""
@ -52,13 +54,13 @@ def public_addr_encode(
def classify_subaddresses(
tx_dests: list[MoneroTransactionDestinationEntry],
change_addr: MoneroAccountPublicAddress,
) -> tuple[int, int, int]:
) -> tuple[int, int, MoneroAccountPublicAddress | None]:
"""
Classify destination subaddresses
"""
num_stdaddresses = 0
num_subaddresses = 0
single_dest_subaddress = None
single_dest_subaddress: MoneroAccountPublicAddress | None = None
addr_set = set()
for tx in tx_dests:
if change_addr and addr_eq(change_addr, tx.addr):
@ -75,7 +77,7 @@ def classify_subaddresses(
return num_stdaddresses, num_subaddresses, single_dest_subaddress
def addr_eq(a: MoneroAccountPublicAddress, b: MoneroAccountPublicAddress):
def addr_eq(a: MoneroAccountPublicAddress, b: MoneroAccountPublicAddress) -> bool:
return (
a.spend_public_key == b.spend_public_key
and a.view_public_key == b.view_public_key
@ -84,8 +86,8 @@ def addr_eq(a: MoneroAccountPublicAddress, b: MoneroAccountPublicAddress):
def get_change_addr_idx(
outputs: list[MoneroTransactionDestinationEntry],
change_dts: MoneroTransactionDestinationEntry,
) -> int:
change_dts: MoneroTransactionDestinationEntry | None,
) -> int | None:
"""
Returns ID of the change output from the change_dts and outputs
"""

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
from trezor.crypto import chacha20poly1305 as ChaCha20Poly1305, monero, random
def encrypt(key, plaintext, associated_data=None):
def encrypt(key: bytes, plaintext: bytes, associated_data: bytes | None = None):
"""
Uses ChaCha20Poly1305 for encryption
"""
@ -14,7 +14,13 @@ def encrypt(key, plaintext, associated_data=None):
return nonce, ciphertext + tag, b""
def decrypt(key, iv, ciphertext, tag=None, associated_data=None):
def decrypt(
key: bytes,
iv: bytes,
ciphertext: bytes,
tag: bytes | None = None,
associated_data: bytes | None = None,
):
"""
ChaCha20Poly1305 decryption
"""
@ -30,11 +36,11 @@ def decrypt(key, iv, ciphertext, tag=None, associated_data=None):
return plaintext
def encrypt_pack(key, plaintext, associated_data=None):
def encrypt_pack(key: bytes, plaintext: bytes, associated_data: bytes | None = None):
b = encrypt(key, plaintext, associated_data)
return b[0] + b[1]
def decrypt_pack(key, ciphertext):
def decrypt_pack(key: bytes, ciphertext: bytes):
cp = memoryview(ciphertext)
return decrypt(key, cp[:12], cp[12:], None)

@ -0,0 +1,243 @@
"""
Multilayer Linkable Spontaneous Anonymous Group (MLSAG)
Optimized versions with incremental hashing.
See https://eprint.iacr.org/2015/1098.pdf for details.
Also explained in From Zero to Monero section 3.3 and 5.
----------
Please note, that the MLSAG code is written in a generic manner,
where it is designed for multiple public keys (aka inputs). In another
words, MLSAG should be used to sign multiple inputs, but that is currently
not the case of Monero, where the inputs are signed one by one.
So the public keys matrix has always two rows (one for public keys,
one for commitments), although the algorithm is designed for `n` rows.
This has one unfortunate effect where `rows` is always equal to 2 and
dsRows always to 1, but the algorithm is still written as the numbers
are arbitrary. That's why there are loops such as `for i in range(dsRows)`
where it is run only once currently.
----------
Also note, that the matrix of public keys is indexed by columns first.
This is because the code was ported from the official Monero client,
which is written in C++ and where it does have some memory advantages.
For ring size = 3 and one input the matrix M will look like this:
|------------------------|------------------------|------------------------|
| public key 0 | public key 1 | public key 2 |
| cmt 0 - pseudo_out cmt | cmt 1 - pseudo_out cmt | cmt 2 - pseudo_out cmt |
and `sk` is equal to:
|--------------|-----------------------------------------------------|
| private key* | input secret key's mask - pseudo_out's mask (alpha) |
* corresponding to one of the public keys (`index` denotes which one)
----------
Mostly ported from official Monero client, but also inspired by Mininero.
Author: Dusan Klinec, ph4r05, 2018
"""
import gc
from typing import TYPE_CHECKING
from apps.monero.xmr import crypto, crypto_helpers
from apps.monero.xmr.serialize import int_serialize
if TYPE_CHECKING:
from typing import Any, TypeGuard, TypeVar
from .serialize_messages.tx_ct_key import CtKey
from trezor.messages import MoneroRctKeyPublic
KeyM = list[list[bytes]]
T = TypeVar("T")
def list_of_type(lst: list[Any], typ: type[T]) -> TypeGuard[list[T]]:
...
_HASH_KEY_CLSAG_ROUND = b"CLSAG_round\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
_HASH_KEY_CLSAG_AGG_0 = b"CLSAG_agg_0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
_HASH_KEY_CLSAG_AGG_1 = b"CLSAG_agg_1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
def generate_clsag_simple(
message: bytes,
pubs: list[MoneroRctKeyPublic],
in_sk: CtKey,
a: crypto.Scalar,
cout: crypto.Point,
index: int,
mg_buff: list[bytearray],
) -> list[bytes]:
"""
CLSAG for RctType.Simple
https://eprint.iacr.org/2019/654.pdf
Corresponds to proveRctCLSAGSimple in rctSigs.cpp
:param message: the full message to be signed (actually its hash)
:param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
:param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
:param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
:param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
:param index: specifies corresponding public key to the `in_sk` in the pubs array
:param mg_buff: buffer to store the signature to
"""
cols = len(pubs)
if cols == 0:
raise ValueError("Empty pubs")
P = _key_vector(cols)
C_nonzero = _key_vector(cols)
p = in_sk.dest
z = crypto.sc_sub_into(None, in_sk.mask, a)
for i in range(cols):
P[i] = pubs[i].dest
C_nonzero[i] = pubs[i].commitment
pubs[i] = None # type: ignore
del pubs
gc.collect()
return _generate_clsag(message, P, p, C_nonzero, z, cout, index, mg_buff)
def _generate_clsag(
message: bytes,
P: list[bytes],
p: crypto.Scalar,
C_nonzero: list[bytes],
z: crypto.Scalar,
Cout: crypto.Point,
index: int,
mg_buff: list[bytearray],
) -> list[bytes]:
sI = crypto.Point() # sig.I
sD = crypto.Point() # sig.D
sc1 = crypto.Scalar() # sig.c1
a = crypto.random_scalar()
H = crypto.Point()
D = crypto.Point()
Cout_bf = crypto_helpers.encodepoint(Cout)
tmp_sc = crypto.Scalar()
tmp = crypto.Point()
tmp_bf = bytearray(32)
crypto.hash_to_point_into(H, P[index])
crypto.scalarmult_into(sI, H, p) # I = p*H
crypto.scalarmult_into(D, H, z) # D = z*H
crypto.sc_mul_into(tmp_sc, z, crypto_helpers.INV_EIGHT_SC) # 1/8*z
crypto.scalarmult_into(sD, H, tmp_sc) # sig.D = 1/8*z*H
sD = crypto_helpers.encodepoint(sD)
hsh_P = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
hsh_C = crypto_helpers.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):
nonlocal hsh_P, hsh_C
hsh_P.update(x)
hsh_C.update(x)
for x in P:
hsh_PC(x)
for x in C_nonzero:
hsh_PC(x)
hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
hsh_PC(sD)
hsh_PC(Cout_bf)
mu_P = crypto_helpers.decodeint(hsh_P.digest())
mu_C = crypto_helpers.decodeint(hsh_C.digest())
del (hsh_PC, hsh_P, hsh_C)
c_to_hash = crypto_helpers.get_keccak() # domain, P, C, C_offset, message, aG, aH
c_to_hash.update(_HASH_KEY_CLSAG_ROUND)
for i in range(len(P)):
c_to_hash.update(P[i])
for i in range(len(P)):
c_to_hash.update(C_nonzero[i])
c_to_hash.update(Cout_bf)
c_to_hash.update(message)
chasher = c_to_hash.copy()
crypto.scalarmult_base_into(tmp, a)
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aG
crypto.scalarmult_into(tmp, H, a)
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aH
c = crypto_helpers.decodeint(chasher.digest())
del (chasher, H)
L = crypto.Point()
R = crypto.Point()
c_p = crypto.Scalar()
c_c = crypto.Scalar()
i = (index + 1) % len(P)
if i == 0:
crypto.sc_copy(sc1, c)
mg_buff.append(int_serialize.dump_uvarint_b(len(P)))
for _ in range(len(P)):
mg_buff.append(bytearray(32))
while i != index:
crypto.random_scalar(tmp_sc)
crypto.encodeint_into(mg_buff[i + 1], tmp_sc)
crypto.sc_mul_into(c_p, mu_P, c)
crypto.sc_mul_into(c_c, mu_C, c)
# L = tmp_sc * G + c_P * P[i] + c_c * C[i]
crypto.add_keys2_into(L, tmp_sc, c_p, crypto.decodepoint_into(tmp, P[i]))
crypto.decodepoint_into(tmp, C_nonzero[i]) # C = C_nonzero - Cout
crypto.point_sub_into(tmp, tmp, Cout)
crypto.scalarmult_into(tmp, tmp, c_c)
crypto.point_add_into(L, L, tmp)
# R = tmp_sc * HP + c_p * I + c_c * D
crypto.hash_to_point_into(tmp, P[i])
crypto.add_keys3_into(R, tmp_sc, tmp, c_p, sI)
crypto.point_add_into(R, R, crypto.scalarmult_into(tmp, D, 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())
P[i] = None # type: ignore
C_nonzero[i] = None # type: ignore
i = (i + 1) % len(P)
if i == 0:
crypto.sc_copy(sc1, c)
if i & 3 == 0:
gc.collect()
# Final scalar = a - c * (mu_P * p + mu_c * Z)
crypto.sc_mul_into(tmp_sc, mu_P, p)
crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
crypto.encodeint_into(mg_buff[index + 1], tmp_sc)
if TYPE_CHECKING:
assert list_of_type(mg_buff, bytes)
mg_buff.append(crypto_helpers.encodeint(sc1))
mg_buff.append(sD)
return mg_buff
def _key_vector(rows: int) -> list[Any]:
return [None] * rows

@ -1,11 +1,8 @@
from typing import TYPE_CHECKING
from trezor.enums import MoneroNetworkType
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto, crypto_helpers
from apps.monero.xmr.addresses import encode_addr
from apps.monero.xmr.networks import NetworkTypes, net_version
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519, Ge25519
from apps.monero.xmr.networks import net_version
class AccountCreds:
@ -15,33 +12,33 @@ class AccountCreds:
def __init__(
self,
view_key_private: Sc25519 | None = None,
spend_key_private: Sc25519 | None = None,
view_key_public: Ge25519 | None = None,
spend_key_public: Ge25519 | None = None,
address: str | None = None,
network_type=NetworkTypes.MAINNET,
):
view_key_private: crypto.Scalar,
spend_key_private: crypto.Scalar,
view_key_public: crypto.Point,
spend_key_public: crypto.Point,
address: str,
network_type: MoneroNetworkType,
) -> None:
self.view_key_private = view_key_private
self.view_key_public = view_key_public
self.spend_key_private = spend_key_private
self.spend_key_public = spend_key_public
self.address = address
self.network_type = network_type
self.address: str | None = address
self.network_type: MoneroNetworkType | None = network_type
@classmethod
def new_wallet(
cls,
priv_view_key: Sc25519,
priv_spend_key: Sc25519,
network_type=NetworkTypes.MAINNET,
):
pub_view_key = crypto.scalarmult_base(priv_view_key)
pub_spend_key = crypto.scalarmult_base(priv_spend_key)
priv_view_key: crypto.Scalar,
priv_spend_key: crypto.Scalar,
network_type: MoneroNetworkType = MoneroNetworkType.MAINNET,
) -> "AccountCreds":
pub_view_key = crypto.scalarmult_base_into(None, priv_view_key)
pub_spend_key = crypto.scalarmult_base_into(None, priv_spend_key)
addr = encode_addr(
net_version(network_type),
crypto.encodepoint(pub_spend_key),
crypto.encodepoint(pub_view_key),
crypto_helpers.encodepoint(pub_spend_key),
crypto_helpers.encodepoint(pub_view_key),
)
return cls(
view_key_private=priv_view_key,

@ -1,325 +0,0 @@
# Author: Dusan Klinec, ph4r05, 2018
#
# Resources:
# https://cr.yp.to
# https://github.com/monero-project/mininero
# https://godoc.org/github.com/agl/ed25519/edwards25519
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-00#section-4
# https://github.com/monero-project/research-lab
from typing import TYPE_CHECKING
from trezor.crypto import monero as tcry, random
from trezor.crypto.hashlib import sha3_256
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519, Ge25519
NULL_KEY_ENC = b"\x00" * 32
random_bytes = random.bytes
ct_equals = tcry.ct_equals
def keccak_factory(data=None):
return sha3_256(data=data, keccak=True)
get_keccak = keccak_factory
keccak_hash = tcry.xmr_fast_hash
keccak_hash_into = tcry.xmr_fast_hash
def keccak_2hash(inp, buff=None):
buff = buff if buff else bytearray(32)
keccak_hash_into(buff, inp)
keccak_hash_into(buff, buff)
return buff
def compute_hmac(key, msg):
digestmod = keccak_factory
inner = digestmod()
block_size = inner.block_size
if len(key) > block_size:
key = digestmod(key).digest()
key_block = bytearray(block_size)
for i in range(block_size):
key_block[i] = 0x36
for i in range(len(key)):
key_block[i] ^= key[i]
inner.update(key_block)
inner.update(msg)
outer = digestmod()
for i in range(block_size):
key_block[i] = 0x5C
for i in range(len(key)):
key_block[i] ^= key[i]
outer.update(key_block)
outer.update(inner.digest())
return outer.digest()
#
# EC
#
new_point = tcry.ge25519_set_neutral
def new_scalar() -> Sc25519:
return tcry.init256_modm(0)
decodepoint = tcry.ge25519_unpack_vartime
decodepoint_into = tcry.ge25519_unpack_vartime
encodepoint = tcry.ge25519_pack
encodepoint_into = tcry.ge25519_pack
decodeint = tcry.unpack256_modm
decodeint_into_noreduce = tcry.unpack256_modm_noreduce
decodeint_into = tcry.unpack256_modm
encodeint = tcry.pack256_modm
encodeint_into = tcry.pack256_modm
check_ed25519point = tcry.ge25519_check
scalarmult_base = tcry.ge25519_scalarmult_base
scalarmult_base_into = tcry.ge25519_scalarmult_base
scalarmult = tcry.ge25519_scalarmult
scalarmult_into = tcry.ge25519_scalarmult
point_add = tcry.ge25519_add
point_add_into = tcry.ge25519_add
point_sub = tcry.ge25519_sub
point_sub_into = tcry.ge25519_sub
point_eq = tcry.ge25519_eq
point_double = tcry.ge25519_double
point_double_into = tcry.ge25519_double
point_mul8 = tcry.ge25519_mul8
point_mul8_into = tcry.ge25519_mul8
INV_EIGHT = b"\x79\x2f\xdc\xe2\x29\xe5\x06\x61\xd0\xda\x1c\x7d\xb3\x9d\xd3\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"
INV_EIGHT_SC = decodeint(INV_EIGHT)
def sc_inv_eight() -> Sc25519:
return INV_EIGHT_SC
#
# Zmod(order), scalar values field
#
def sc_0() -> Sc25519:
return tcry.init256_modm(0)
def sc_0_into(r: Sc25519) -> Sc25519:
return tcry.init256_modm(r, 0)
def sc_init(x: int) -> Sc25519:
if x >= (1 << 64):
raise ValueError("Initialization works up to 64-bit only")
return tcry.init256_modm(x)
def sc_init_into(r: Sc25519, x: int) -> Sc25519:
if x >= (1 << 64):
raise ValueError("Initialization works up to 64-bit only")
return tcry.init256_modm(r, x)
sc_copy = tcry.init256_modm
sc_get64 = tcry.get256_modm
sc_check = tcry.check256_modm
check_sc = tcry.check256_modm
sc_add = tcry.add256_modm
sc_add_into = tcry.add256_modm
sc_sub = tcry.sub256_modm
sc_sub_into = tcry.sub256_modm
sc_mul = tcry.mul256_modm
sc_mul_into = tcry.mul256_modm
def sc_isnonzero(c: Sc25519) -> bool:
"""
Returns true if scalar is non-zero
"""
return not tcry.iszero256_modm(c)
sc_eq = tcry.eq256_modm
sc_mulsub = tcry.mulsub256_modm
sc_mulsub_into = tcry.mulsub256_modm
sc_muladd = tcry.muladd256_modm
sc_muladd_into = tcry.muladd256_modm
sc_inv_into = tcry.inv256_modm
def random_scalar(r=None) -> Sc25519:
return tcry.xmr_random_scalar(r if r is not None else new_scalar())
#
# GE - ed25519 group
#
def ge25519_double_scalarmult_base_vartime(a, A, b) -> Ge25519:
"""
void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const bignum256modm s1, const bignum256modm s2);
r = a * A + b * B
"""
R = tcry.ge25519_double_scalarmult_vartime(A, a, b)
return R
ge25519_double_scalarmult_vartime2 = tcry.xmr_add_keys3
def identity(byte_enc=False) -> Ge25519 | bytes:
idd = tcry.ge25519_set_neutral()
return idd if not byte_enc else encodepoint(idd)
identity_into = tcry.ge25519_set_neutral
"""
https://www.imperialviolet.org/2013/12/25/elligator.html
http://elligator.cr.yp.to/
http://elligator.cr.yp.to/elligator-20130828.pdf
"""
#
# Monero specific
#
cn_fast_hash = keccak_hash
def hash_to_scalar(data: bytes, length: int | None = None):
"""
H_s(P)
"""
dt = data[:length] if length else data
return tcry.xmr_hash_to_scalar(dt)
def hash_to_scalar_into(r: Sc25519, data: bytes, length: int | None = None):
dt = data[:length] if length else data
return tcry.xmr_hash_to_scalar(r, dt)
# H_p(buf)
#
# Code adapted from MiniNero: https://github.com/monero-project/mininero
# https://github.com/monero-project/research-lab/blob/master/whitepaper/ge_fromfe_writeup/ge_fromfe.pdf
# http://archive.is/yfINb
hash_to_point = tcry.xmr_hash_to_ec
hash_to_point_into = tcry.xmr_hash_to_ec
#
# XMR
#
xmr_H = tcry.ge25519_set_h
def scalarmult_h(i) -> Ge25519:
return scalarmult(xmr_H(), sc_init(i) if isinstance(i, int) else i)
add_keys2 = tcry.xmr_add_keys2_vartime
add_keys2_into = tcry.xmr_add_keys2_vartime
add_keys3 = tcry.xmr_add_keys3_vartime
add_keys3_into = tcry.xmr_add_keys3_vartime
gen_commitment = tcry.xmr_gen_c
def generate_key_derivation(pub: Ge25519, sec: Sc25519) -> Ge25519:
"""
Key derivation: 8*(key2*key1)
"""
sc_check(sec) # checks that the secret key is uniform enough...
check_ed25519point(pub)
return tcry.xmr_generate_key_derivation(pub, sec)
def derivation_to_scalar(derivation: Ge25519, output_index: int) -> Sc25519:
"""
H_s(derivation || varint(output_index))
"""
check_ed25519point(derivation)
return tcry.xmr_derivation_to_scalar(derivation, output_index)
def derive_public_key(derivation: Ge25519, output_index: int, B: Ge25519) -> Ge25519:
"""
H_s(derivation || varint(output_index))G + B
"""
check_ed25519point(B)
return tcry.xmr_derive_public_key(derivation, output_index, B)
def derive_secret_key(derivation: Ge25519, output_index: int, base: Sc25519) -> Sc25519:
"""
base + H_s(derivation || varint(output_index))
"""
sc_check(base)
return tcry.xmr_derive_private_key(derivation, output_index, base)
def get_subaddress_secret_key(
secret_key: Sc25519, major: int = 0, minor: int = 0
) -> Sc25519:
"""
Builds subaddress secret key from the subaddress index
Hs(SubAddr || a || index_major || index_minor)
"""
return tcry.xmr_get_subaddress_secret_key(major, minor, secret_key)
def generate_signature(data: bytes, priv: Sc25519) -> tuple[Sc25519, Sc25519, Ge25519]:
"""
Generate EC signature
crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
"""
pub = scalarmult_base(priv)
k = random_scalar()
comm = scalarmult_base(k)
buff = data + encodepoint(pub) + encodepoint(comm)
c = hash_to_scalar(buff)
r = sc_mulsub(priv, c, k)
return c, r, pub
def check_signature(data: bytes, c: Sc25519, r: Sc25519, pub: Ge25519) -> bool:
"""
EC signature verification
"""
check_ed25519point(pub)
if sc_check(c) != 0 or sc_check(r) != 0:
raise ValueError("Signature error")
tmp2 = point_add(scalarmult(pub, c), scalarmult_base(r))
buff = data + encodepoint(pub) + encodepoint(tmp2)
tmp_c = hash_to_scalar(buff)
res = sc_sub(tmp_c, c)
return not sc_isnonzero(res)
def xor8(buff: bytes, key: bytes) -> bytes:
for i in range(8):
buff[i] ^= key[i]
return buff

@ -0,0 +1,162 @@
# Author: Dusan Klinec, ph4r05, 2018
#
# Resources:
# https://cr.yp.to
# https://github.com/monero-project/mininero
# https://godoc.org/github.com/agl/ed25519/edwards25519
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-00#section-4
# https://github.com/monero-project/research-lab
from trezor.crypto import monero as tcry
from trezor.crypto.hashlib import sha3_256
NULL_KEY_ENC = b"\x00" * 32
def get_keccak(data: bytes | None = None) -> sha3_256:
return sha3_256(data=data, keccak=True)
def keccak_2hash(inp: bytes, buff: bytes | None = None) -> bytes:
buff = buff if buff else bytearray(32)
tcry.fast_hash_into(buff, inp)
tcry.fast_hash_into(buff, buff)
return buff
def compute_hmac(key: bytes, msg: bytes) -> bytes:
digestmod = get_keccak
inner = digestmod()
block_size = inner.block_size
if len(key) > block_size:
key = digestmod(key).digest()
key_block = bytearray(block_size)
for i in range(block_size):
key_block[i] = 0x36
for i in range(len(key)):
key_block[i] ^= key[i]
inner.update(key_block)
inner.update(msg)
outer = digestmod()
for i in range(block_size):
key_block[i] = 0x5C
for i in range(len(key)):
key_block[i] ^= key[i]
outer.update(key_block)
outer.update(inner.digest())
return outer.digest()
#
# EC
#
def decodepoint(x: bytes) -> tcry.Point:
return tcry.decodepoint_into(None, x)
def encodepoint(x: tcry.Point, offset: int = 0) -> bytes:
return tcry.encodepoint_into(None, x, offset)
def encodeint(x: tcry.Scalar, offset: int = 0) -> bytes:
return tcry.encodeint_into(None, x, offset)
def decodeint(x: bytes) -> tcry.Scalar:
return tcry.decodeint_into(None, x)
INV_EIGHT = b"\x79\x2f\xdc\xe2\x29\xe5\x06\x61\xd0\xda\x1c\x7d\xb3\x9d\xd3\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"
INV_EIGHT_SC = decodeint(INV_EIGHT)
def generate_key_derivation(pub: tcry.Point, sec: tcry.Scalar) -> tcry.Point:
"""
Key derivation: 8*(key2*key1)
"""
tcry.sc_check(sec) # checks that the secret key is uniform enough...
tcry.ge25519_check(pub)
return tcry.xmr_generate_key_derivation(None, pub, sec)
def derivation_to_scalar(derivation: tcry.Point, output_index: int) -> tcry.Scalar:
"""
H_s(derivation || varint(output_index))
"""
tcry.ge25519_check(derivation)
return tcry.xmr_derivation_to_scalar(None, derivation, output_index)
def derive_public_key(
derivation: tcry.Point, output_index: int, B: tcry.Point
) -> tcry.Point:
"""
H_s(derivation || varint(output_index))G + B
"""
tcry.ge25519_check(B)
return tcry.xmr_derive_public_key(None, derivation, output_index, B)
def derive_secret_key(
derivation: tcry.Point, output_index: int, base: tcry.Scalar
) -> tcry.Scalar:
"""
base + H_s(derivation || varint(output_index))
"""
tcry.sc_check(base)
return tcry.xmr_derive_private_key(None, derivation, output_index, base)
def get_subaddress_secret_key(
secret_key: tcry.Scalar, major: int = 0, minor: int = 0
) -> tcry.Scalar:
"""
Builds subaddress secret key from the subaddress index
Hs(SubAddr || a || index_major || index_minor)
"""
return tcry.xmr_get_subaddress_secret_key(None, major, minor, secret_key)
def generate_signature(
data: bytes, priv: tcry.Scalar
) -> tuple[tcry.Scalar, tcry.Scalar, tcry.Point]:
"""
Generate EC signature
crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
"""
pub = tcry.scalarmult_base_into(None, priv)
k = tcry.random_scalar()
comm = tcry.scalarmult_base_into(None, k)
buff = data + encodepoint(pub) + encodepoint(comm)
c = tcry.hash_to_scalar_into(None, buff)
r = tcry.sc_mulsub_into(None, priv, c, k)
return c, r, pub
def check_signature(
data: bytes, c: tcry.Scalar, r: tcry.Scalar, pub: tcry.Point
) -> bool:
"""
EC signature verification
"""
tcry.ge25519_check(pub)
if tcry.sc_check(c) != 0 or tcry.sc_check(r) != 0:
raise ValueError("Signature error")
tmp2 = tcry.point_add_into(
None, tcry.scalarmult_into(None, pub, c), tcry.scalarmult_base_into(None, r)
)
buff = data + encodepoint(pub) + encodepoint(tmp2)
tmp_c = tcry.hash_to_scalar_into(None, buff)
res = tcry.sc_sub_into(None, tmp_c, c)
return tcry.sc_iszero(res)
def xor8(buff: bytearray, key: bytes) -> bytes:
for i in range(8):
buff[i] ^= key[i]
return buff

@ -1,27 +1,32 @@
from typing import TYPE_CHECKING
from trezor.utils import HashWriter
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto_helpers
from apps.monero.xmr.serialize import int_serialize
if TYPE_CHECKING:
from trezor.utils import HashContext
class KeccakXmrArchive:
def __init__(self, ctx=None):
def __init__(self, ctx: HashContext | None = None) -> None:
self.kwriter = get_keccak_writer(ctx)
def get_digest(self):
def get_digest(self) -> bytes:
return self.kwriter.get_digest()
def buffer(self, buf):
def buffer(self, buf: bytes) -> None:
return self.kwriter.write(buf)
def uvarint(self, i):
def uvarint(self, i: int) -> None:
int_serialize.dump_uvarint(self.kwriter, i)
def uint(self, i, width):
def uint(self, i: int, width: int) -> None:
int_serialize.dump_uint(self.kwriter, i, width)
def get_keccak_writer(ctx=None):
def get_keccak_writer(ctx: HashContext | None = None) -> HashWriter:
if ctx is None:
ctx = crypto.get_keccak()
ctx = crypto_helpers.get_keccak()
return HashWriter(ctx)

@ -1,19 +1,18 @@
from typing import TYPE_CHECKING
from apps.monero.xmr import crypto, monero
from apps.monero.xmr import crypto, crypto_helpers, monero
from apps.monero.xmr.serialize.int_serialize import dump_uvarint_b
if TYPE_CHECKING:
from apps.monero.xmr.types import Ge25519, Sc25519
from apps.monero.xmr.credentials import AccountCreds
from trezor.messages import MoneroTransferDetails
Subaddresses = dict[bytes, tuple[int, int]]
Sig = list[list[Sc25519]]
Sig = list[list[crypto.Scalar]]
def compute_hash(rr: MoneroTransferDetails) -> bytes:
kck = crypto.get_keccak()
kck = crypto_helpers.get_keccak()
kck.update(rr.out_key)
kck.update(rr.tx_pub_key)
if rr.additional_tx_pub_keys:
@ -25,17 +24,17 @@ def compute_hash(rr: MoneroTransferDetails) -> bytes:
def export_key_image(
creds: AccountCreds, subaddresses: Subaddresses, td: MoneroTransferDetails
) -> tuple[Ge25519, Sig]:
out_key = crypto.decodepoint(td.out_key)
tx_pub_key = crypto.decodepoint(td.tx_pub_key)
) -> tuple[crypto.Point, Sig]:
out_key = crypto_helpers.decodepoint(td.out_key)
tx_pub_key = crypto_helpers.decodepoint(td.tx_pub_key)
additional_tx_pub_key = None
if len(td.additional_tx_pub_keys) == 1: # compression
additional_tx_pub_key = crypto.decodepoint(td.additional_tx_pub_keys[0])
additional_tx_pub_key = crypto_helpers.decodepoint(td.additional_tx_pub_keys[0])
elif td.additional_tx_pub_keys:
if td.internal_output_index >= len(td.additional_tx_pub_keys):
raise ValueError("Wrong number of additional derivations")
additional_tx_pub_key = crypto.decodepoint(
additional_tx_pub_key = crypto_helpers.decodepoint(
td.additional_tx_pub_keys[td.internal_output_index]
)
@ -56,14 +55,14 @@ def export_key_image(
def _export_key_image(
creds: AccountCreds,
subaddresses: Subaddresses,
pkey: Ge25519,
tx_pub_key: Ge25519,
additional_tx_pub_key: Ge25519 | None,
pkey: crypto.Point,
tx_pub_key: crypto.Point,
additional_tx_pub_key: crypto.Point | None,
out_idx: int,
test: bool = True,
sub_addr_major: int = None,
sub_addr_minor: int = None,
) -> tuple[Ge25519, Sig]:
sub_addr_major: int | None = None,
sub_addr_minor: int | None = None,
) -> tuple[crypto.Point, Sig]:
"""
Generates key image for the TXO + signature for the key image
"""
@ -79,7 +78,7 @@ def _export_key_image(
)
xi, ki, _ = r[:3]
phash = crypto.encodepoint(ki)
phash = crypto_helpers.encodepoint(ki)
sig = generate_ring_signature(phash, ki, [pkey], xi, 0, test)
return ki, sig
@ -87,9 +86,9 @@ def _export_key_image(
def generate_ring_signature(
prefix_hash: bytes,
image: Ge25519,
pubs: list[Ge25519],
sec: Sc25519,
image: crypto.Point,
pubs: list[crypto.Point],
sec: crypto.Scalar,
sec_idx: int,
test: bool = False,
) -> Sig:
@ -100,59 +99,57 @@ def generate_ring_signature(
from trezor.utils import memcpy
if test:
t = crypto.scalarmult_base(sec)
t = crypto.scalarmult_base_into(None, sec)
if not crypto.point_eq(t, pubs[sec_idx]):
raise ValueError("Invalid sec key")
k_i = monero.generate_key_image(crypto.encodepoint(pubs[sec_idx]), sec)
k_i = monero.generate_key_image(crypto_helpers.encodepoint(pubs[sec_idx]), sec)
if not crypto.point_eq(k_i, image):
raise ValueError("Key image invalid")
for k in pubs:
crypto.check_ed25519point(k)
crypto.ge25519_check(k)
buff_off = len(prefix_hash)
buff = bytearray(buff_off + 2 * 32 * len(pubs))
memcpy(buff, 0, prefix_hash, 0, buff_off)
mvbuff = memoryview(buff)
sum = crypto.sc_0()
k = crypto.sc_0()
sum = crypto.Scalar(0)
k = crypto.Scalar(0)
sig = []
for _ in range(len(pubs)):
sig.append([crypto.sc_0(), crypto.sc_0()]) # c, r
sig.append([crypto.Scalar(0), crypto.Scalar(0)]) # c, r
for i in range(len(pubs)):
if i == sec_idx:
k = crypto.random_scalar()
tmp3 = crypto.scalarmult_base(k)
tmp3 = crypto.scalarmult_base_into(None, k)
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp3)
buff_off += 32
tmp3 = crypto.hash_to_point(crypto.encodepoint(pubs[i]))
tmp2 = crypto.scalarmult(tmp3, k)
tmp3 = crypto.hash_to_point_into(None, crypto_helpers.encodepoint(pubs[i]))
tmp2 = crypto.scalarmult_into(None, tmp3, k)
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
buff_off += 32
else:
sig[i] = [crypto.random_scalar(), crypto.random_scalar()]
tmp3 = pubs[i]
tmp2 = crypto.ge25519_double_scalarmult_base_vartime(
sig[i][0], tmp3, sig[i][1]
tmp2 = crypto.ge25519_double_scalarmult_vartime_into(
None, tmp3, sig[i][0], sig[i][1]
)
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
buff_off += 32
tmp3 = crypto.hash_to_point(crypto.encodepoint(tmp3))
tmp2 = crypto.ge25519_double_scalarmult_vartime2(
sig[i][1], tmp3, sig[i][0], image
)
tmp3 = crypto.hash_to_point_into(None, crypto_helpers.encodepoint(tmp3))
tmp2 = crypto.add_keys3_into(None, sig[i][1], tmp3, sig[i][0], image)
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
buff_off += 32
sum = crypto.sc_add(sum, sig[i][0])
crypto.sc_add_into(sum, sum, sig[i][0])
h = crypto.hash_to_scalar(buff)
sig[sec_idx][0] = crypto.sc_sub(h, sum)
sig[sec_idx][1] = crypto.sc_mulsub(sig[sec_idx][0], sec, k)
h = crypto.hash_to_scalar_into(None, buff)
sig[sec_idx][0] = crypto.sc_sub_into(None, h, sum)
sig[sec_idx][1] = crypto.sc_mulsub_into(None, sig[sec_idx][0], sec, k)
return sig

@ -1,493 +0,0 @@
"""
Multilayer Linkable Spontaneous Anonymous Group (MLSAG)
Optimized versions with incremental hashing.
See https://eprint.iacr.org/2015/1098.pdf for details.
Also explained in From Zero to Monero section 3.3 and 5.
----------
Please note, that the MLSAG code is written in a generic manner,
where it is designed for multiple public keys (aka inputs). In another
words, MLSAG should be used to sign multiple inputs, but that is currently
not the case of Monero, where the inputs are signed one by one.
So the public keys matrix has always two rows (one for public keys,
one for commitments), although the algorithm is designed for `n` rows.
This has one unfortunate effect where `rows` is always equal to 2 and
dsRows always to 1, but the algorithm is still written as the numbers
are arbitrary. That's why there are loops such as `for i in range(dsRows)`
where it is run only once currently.
----------
Also note, that the matrix of public keys is indexed by columns first.
This is because the code was ported from the official Monero client,
which is written in C++ and where it does have some memory advantages.
For ring size = 3 and one input the matrix M will look like this:
|------------------------|------------------------|------------------------|
| public key 0 | public key 1 | public key 2 |
| cmt 0 - pseudo_out cmt | cmt 1 - pseudo_out cmt | cmt 2 - pseudo_out cmt |
and `sk` is equal to:
|--------------|-----------------------------------------------------|
| private key* | input secret key's mask - pseudo_out's mask (alpha) |
* corresponding to one of the public keys (`index` denotes which one)
----------
Mostly ported from official Monero client, but also inspired by Mininero.
Author: Dusan Klinec, ph4r05, 2018
"""
import gc
from typing import TYPE_CHECKING
from apps.monero.xmr import crypto
from apps.monero.xmr.serialize import int_serialize
if TYPE_CHECKING:
from apps.monero.xmr.types import Ge25519, Sc25519
from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey
from trezor.messages import MoneroRctKeyPublic
KeyM = list[list[bytes]]
_HASH_KEY_CLSAG_ROUND = b"CLSAG_round\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
_HASH_KEY_CLSAG_AGG_0 = b"CLSAG_agg_0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
_HASH_KEY_CLSAG_AGG_1 = b"CLSAG_agg_1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
def generate_mlsag_simple(
message: bytes,
pubs: list[MoneroRctKeyPublic],
in_sk: CtKey,
a: Sc25519,
cout: Ge25519,
index: int,
mg_buff: list[bytes],
) -> list[bytes]:
"""
MLSAG for RctType.Simple
:param message: the full message to be signed (actually its hash)
:param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
:param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
:param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
:param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
:param index: specifies corresponding public key to the `in_sk` in the pubs array
: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")
rows = 2
dsRows = 1
cols = len(pubs)
if cols == 0:
raise ValueError("Empty pubs")
sk = _key_vector(rows)
M = _key_matrix(rows, cols)
sk[0] = in_sk.dest
sk[1] = crypto.sc_sub(in_sk.mask, a)
tmp_pt = crypto.new_point()
for i in range(cols):
crypto.point_sub_into(
tmp_pt, crypto.decodepoint_into(tmp_pt, pubs[i].commitment), cout
)
M[i][0] = pubs[i].dest
M[i][1] = crypto.encodepoint(tmp_pt)
pubs[i] = None
del pubs
gc.collect()
return generate_mlsag(message, M, sk, index, dsRows, mg_buff)
def gen_mlsag_assert(pk: KeyM, xx: list[Sc25519], index: int, dsRows: int):
"""
Conditions check
"""
cols = len(pk)
if cols <= 1:
raise ValueError("Cols == 1")
if index >= cols:
raise ValueError("Index out of range")
rows = len(pk[0])
if rows == 0:
raise ValueError("Empty pk")
for i in range(cols):
if len(pk[i]) != rows:
raise ValueError("pk is not rectangular")
if len(xx) != rows:
raise ValueError("Bad xx size")
if dsRows > rows:
raise ValueError("Bad dsRows size")
return rows, cols
def generate_first_c_and_key_images(
message: bytes,
pk: KeyM,
xx: list[Sc25519],
index: int,
dsRows: int,
rows: int,
cols: int,
) -> tuple[Sc25519, list[Ge25519], list[Ge25519]]:
"""
MLSAG computation - the part with secret keys
:param message: the full message to be signed (actually its hash)
:param pk: matrix of public keys and commitments
:param xx: input secret array composed of a private key and commitment mask
:param index: specifies corresponding public key to the `xx`'s private key in the `pk` array
:param dsRows: row number where the pubkeys "end" (and commitments follow)
:param rows: total number of rows
:param cols: size of ring
"""
II = _key_vector(dsRows)
alpha = _key_vector(rows)
tmp_buff = bytearray(32)
Hi = crypto.new_point()
aGi = crypto.new_point()
aHPi = crypto.new_point()
hasher = _hasher_message(message)
for i in range(dsRows):
# this is somewhat extra as compared to the Ring Confidential Tx paper
# see footnote in From Zero to Monero section 3.3
hasher.update(pk[index][i])
crypto.hash_to_point_into(Hi, pk[index][i])
alpha[i] = crypto.random_scalar()
# L = alpha_i * G
crypto.scalarmult_base_into(aGi, alpha[i])
# Ri = alpha_i * H(P_i)
crypto.scalarmult_into(aHPi, Hi, alpha[i])
# key image
II[i] = crypto.scalarmult(Hi, xx[i])
_hash_point(hasher, aGi, tmp_buff)
_hash_point(hasher, aHPi, tmp_buff)
for i in range(dsRows, rows):
alpha[i] = crypto.random_scalar()
# L = alpha_i * G
crypto.scalarmult_base_into(aGi, alpha[i])
# for some reasons we omit calculating R here, which seems
# contrary to the paper, but it is in the Monero official client
# see https://github.com/monero-project/monero/blob/636153b2050aa0642ba86842c69ac55a5d81618d/src/ringct/rctSigs.cpp#L191
hasher.update(pk[index][i])
_hash_point(hasher, aGi, tmp_buff)
# the first c
c_old = hasher.digest()
c_old = crypto.decodeint(c_old)
return c_old, II, alpha
def generate_mlsag(
message: bytes,
pk: KeyM,
xx: list[Sc25519],
index: int,
dsRows: int,
mg_buff: list[bytes],
) -> list[bytes]:
"""
Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
:param message: the full message to be signed (actually its hash)
:param pk: matrix of public keys and commitments
:param xx: input secret array composed of a private key and commitment mask
:param index: specifies corresponding public key to the `xx`'s private key in the `pk` array
:param dsRows: separates pubkeys from commitment
:param mg_buff: mg signature buffer
"""
rows, cols = gen_mlsag_assert(pk, xx, index, dsRows)
rows_b_size = int_serialize.uvarint_size(rows)
# Preallocation of the chunked buffer, len + cols + cc
for _ in range(1 + cols + 1):
mg_buff.append(None)
mg_buff[0] = int_serialize.dump_uvarint_b(cols)
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, II, alpha = generate_first_c_and_key_images(
message, pk, xx, index, dsRows, rows, cols
)
i = (index + 1) % cols
if i == 0:
crypto.sc_copy(cc, c_old)
ss = [crypto.new_scalar() for _ in range(rows)]
tmp_buff = bytearray(32)
while i != index:
hasher = _hasher_message(message)
# Serialize size of the row
mg_buff[i + 1] = bytearray(rows_b_size + 32 * rows)
int_serialize.dump_uvarint_b_into(rows, mg_buff[i + 1])
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, 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, ss[j], Hi, c_old, II[j])
hasher.update(pk[i][j])
_hash_point(hasher, L, tmp_buff)
_hash_point(hasher, R, tmp_buff)
for j in range(dsRows, rows):
# again, omitting R here as discussed above
crypto.add_keys2_into(
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(mg_buff[i + 1], ss[si], rows_b_size + 32 * si)
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(cc, c_old)
gc.collect()
del II
# Finalizing rv.ss by processing rv.ss[index]
mg_buff[index + 1] = bytearray(rows_b_size + 32 * rows)
int_serialize.dump_uvarint_b_into(rows, mg_buff[index + 1])
for j in range(rows):
crypto.sc_mulsub_into(ss[j], c, xx[j], alpha[j])
crypto.encodeint_into(mg_buff[index + 1], ss[j], rows_b_size + 32 * j)
# rv.cc
mg_buff[-1] = crypto.encodeint(cc)
return mg_buff
def generate_clsag_simple(
message: bytes,
pubs: list[MoneroRctKeyPublic],
in_sk: CtKey,
a: Sc25519,
cout: Ge25519,
index: int,
mg_buff: list[bytes],
) -> list[bytes]:
"""
CLSAG for RctType.Simple
https://eprint.iacr.org/2019/654.pdf
Corresponds to proveRctCLSAGSimple in rctSigs.cpp
:param message: the full message to be signed (actually its hash)
:param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
:param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
:param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
:param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
:param index: specifies corresponding public key to the `in_sk` in the pubs array
:param mg_buff: buffer to store the signature to
"""
cols = len(pubs)
if cols == 0:
raise ValueError("Empty pubs")
P = _key_vector(cols)
C_nonzero = _key_vector(cols)
p = in_sk.dest
z = crypto.sc_sub(in_sk.mask, a)
for i in range(cols):
P[i] = pubs[i].dest
C_nonzero[i] = pubs[i].commitment
pubs[i] = None
del pubs
gc.collect()
return _generate_clsag(message, P, p, C_nonzero, z, cout, index, mg_buff)
def _generate_clsag(
message: bytes,
P: list[bytes],
p: Sc25519,
C_nonzero: list[bytes],
z: Sc25519,
Cout: Ge25519,
index: int,
mg_buff: list[bytes],
) -> list[bytes]:
sI = crypto.new_point() # sig.I
sD = crypto.new_point() # sig.D
sc1 = crypto.new_scalar() # sig.c1
a = crypto.random_scalar()
H = crypto.new_point()
D = crypto.new_point()
Cout_bf = crypto.encodepoint(Cout)
tmp_sc = crypto.new_scalar()
tmp = crypto.new_point()
tmp_bf = bytearray(32)
crypto.hash_to_point_into(H, P[index])
crypto.scalarmult_into(sI, H, p) # I = p*H
crypto.scalarmult_into(D, H, z) # D = z*H
crypto.sc_mul_into(tmp_sc, z, crypto.sc_inv_eight()) # 1/8*z
crypto.scalarmult_into(sD, H, tmp_sc) # sig.D = 1/8*z*H
sD = crypto.encodepoint(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):
nonlocal hsh_P, hsh_C
hsh_P.update(x)
hsh_C.update(x)
for x in P:
hsh_PC(x)
for x in C_nonzero:
hsh_PC(x)
hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
hsh_PC(sD)
hsh_PC(Cout_bf)
mu_P = crypto.decodeint(hsh_P.digest())
mu_C = crypto.decodeint(hsh_C.digest())
del (hsh_PC, hsh_P, hsh_C)
c_to_hash = crypto.get_keccak() # domain, P, C, C_offset, message, aG, aH
c_to_hash.update(_HASH_KEY_CLSAG_ROUND)
for i in range(len(P)):
c_to_hash.update(P[i])
for i in range(len(P)):
c_to_hash.update(C_nonzero[i])
c_to_hash.update(Cout_bf)
c_to_hash.update(message)
chasher = c_to_hash.copy()
crypto.scalarmult_base_into(tmp, a)
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aG
crypto.scalarmult_into(tmp, H, a)
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aH
c = crypto.decodeint(chasher.digest())
del (chasher, H)
L = crypto.new_point()
R = crypto.new_point()
c_p = crypto.new_scalar()
c_c = crypto.new_scalar()
i = (index + 1) % len(P)
if i == 0:
crypto.sc_copy(sc1, c)
mg_buff.append(int_serialize.dump_uvarint_b(len(P)))
for _ in range(len(P)):
mg_buff.append(bytearray(32))
while i != index:
crypto.random_scalar(tmp_sc)
crypto.encodeint_into(mg_buff[i + 1], tmp_sc)
crypto.sc_mul_into(c_p, mu_P, c)
crypto.sc_mul_into(c_c, mu_C, c)
# L = tmp_sc * G + c_P * P[i] + c_c * C[i]
crypto.add_keys2_into(L, tmp_sc, c_p, crypto.decodepoint_into(tmp, P[i]))
crypto.decodepoint_into(tmp, C_nonzero[i]) # C = C_nonzero - Cout
crypto.point_sub_into(tmp, tmp, Cout)
crypto.scalarmult_into(tmp, tmp, c_c)
crypto.point_add_into(L, L, tmp)
# R = tmp_sc * HP + c_p * I + c_c * D
crypto.hash_to_point_into(tmp, P[i])
crypto.add_keys3_into(R, tmp_sc, tmp, c_p, sI)
crypto.point_add_into(R, R, crypto.scalarmult_into(tmp, D, 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())
P[i] = None
C_nonzero[i] = None
i = (i + 1) % len(P)
if i == 0:
crypto.sc_copy(sc1, c)
if i & 3 == 0:
gc.collect()
# Final scalar = a - c * (mu_P * p + mu_c * Z)
crypto.sc_mul_into(tmp_sc, mu_P, p)
crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
crypto.encodeint_into(mg_buff[index + 1], tmp_sc)
mg_buff.append(crypto.encodeint(sc1))
mg_buff.append(sD)
return mg_buff
def _key_vector(rows):
return [None] * rows
def _key_matrix(rows, cols):
"""
first index is columns (so slightly backward from math)
"""
rv = [None] * cols
for i in range(0, cols):
rv[i] = _key_vector(rows)
return rv
def _hasher_message(message):
"""
Returns incremental hasher for MLSAG
"""
ctx = crypto.get_keccak()
ctx.update(message)
return ctx
def _hash_point(hasher, point, tmp_buff):
crypto.encodepoint_into(tmp_buff, point)
hasher.update(tmp_buff)

@ -1,10 +1,12 @@
from typing import TYPE_CHECKING
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto_helpers
from apps.monero.xmr.keccak_hasher import KeccakXmrArchive
from .serialize_messages.tx_rsig_bulletproof import Bulletproof
if TYPE_CHECKING:
from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof
from trezor.utils import HashContext
class PreMlsagHasher:
@ -12,50 +14,50 @@ class PreMlsagHasher:
Iterative construction of the pre_mlsag_hash
"""
def __init__(self):
def __init__(self) -> None:
self.state = 0
self.kc_master = crypto.get_keccak()
self.rsig_hasher = crypto.get_keccak()
self.rtcsig_hasher = KeccakXmrArchive()
self.kc_master: HashContext = crypto_helpers.get_keccak()
self.rsig_hasher: HashContext = crypto_helpers.get_keccak()
self.rtcsig_hasher: KeccakXmrArchive = KeccakXmrArchive()
def init(self):
def init(self) -> None:
if self.state != 0:
raise ValueError("State error")
self.state = 1
def set_message(self, message: bytes):
def set_message(self, message: bytes) -> None:
self.kc_master.update(message)
def set_type_fee(self, rv_type: int, fee: int):
def set_type_fee(self, rv_type: int, fee: int) -> None:
if self.state != 1:
raise ValueError("State error")
self.state = 2
self.rtcsig_hasher.uint(rv_type, 1) # UInt8
self.rtcsig_hasher.uvarint(fee) # UVarintType
def set_ecdh(self, ecdh: bytes):
def set_ecdh(self, ecdh: bytes) -> None:
if self.state not in (2, 3, 4):
raise ValueError("State error")
self.state = 4
self.rtcsig_hasher.buffer(ecdh)
def set_out_pk_commitment(self, out_pk_commitment: bytes):
def set_out_pk_commitment(self, out_pk_commitment: bytes) -> None:
if self.state not in (4, 5):
raise ValueError("State error")
self.state = 5
self.rtcsig_hasher.buffer(out_pk_commitment) # ECKey
def rctsig_base_done(self):
def rctsig_base_done(self) -> None:
if self.state != 5:
raise ValueError("State error")
self.state = 6
c_hash = self.rtcsig_hasher.get_digest()
self.kc_master.update(c_hash)
self.rtcsig_hasher = None
self.rtcsig_hasher = None # type: ignore
def rsig_val(self, p: bytes | list[bytes] | Bulletproof, raw: bool = False):
def rsig_val(self, p: bytes | list[bytes] | Bulletproof, raw: bool = False) -> None:
if self.state == 8:
raise ValueError("State error")
@ -68,9 +70,12 @@ class PreMlsagHasher:
for x in p:
self.rsig_hasher.update(x)
else:
assert isinstance(p, bytes)
self.rsig_hasher.update(p)
return
assert isinstance(p, Bulletproof)
# Hash Bulletproof
self.rsig_hasher.update(p.A)
self.rsig_hasher.update(p.S)
@ -92,7 +97,7 @@ class PreMlsagHasher:
self.state = 8
c_hash = self.rsig_hasher.digest()
self.rsig_hasher = None
self.rsig_hasher = None # type: ignore
self.kc_master.update(c_hash)
return self.kc_master.digest()

@ -1,9 +1,8 @@
from typing import TYPE_CHECKING
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto, crypto_helpers
if TYPE_CHECKING:
from apps.monero.xmr.types import Ge25519, Sc25519
from apps.monero.xmr.credentials import AccountCreds
Subaddresses = dict[bytes, tuple[int, int]]
@ -17,7 +16,9 @@ class XmrNoSuchAddressException(XmrException):
pass
def get_subaddress_secret_key(secret_key: Sc25519, major: int = 0, minor: int = 0):
def get_subaddress_secret_key(
secret_key: crypto.Scalar, major: int = 0, minor: int = 0
) -> crypto.Scalar:
"""
Builds subaddress secret key from the subaddress index
Hs(SubAddr || a || index_major || index_minor)
@ -25,12 +26,12 @@ def get_subaddress_secret_key(secret_key: Sc25519, major: int = 0, minor: int =
if major == 0 and minor == 0:
return secret_key
return crypto.get_subaddress_secret_key(secret_key, major, minor)
return crypto_helpers.get_subaddress_secret_key(secret_key, major, minor)
def get_subaddress_spend_public_key(
view_private: Sc25519, spend_public: Ge25519, major: int, minor: int
) -> Ge25519:
view_private: crypto.Scalar, spend_public: crypto.Point, major: int, minor: int
) -> crypto.Point:
"""
Generates subaddress spend public key D_{major, minor}
"""
@ -38,43 +39,43 @@ def get_subaddress_spend_public_key(
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)
M = crypto.scalarmult_base_into(None, m)
D = crypto.point_add_into(None, spend_public, M)
return D
def derive_subaddress_public_key(
out_key: Ge25519, derivation: Ge25519, output_index: int
) -> Ge25519:
out_key: crypto.Point, derivation: crypto.Point, output_index: int
) -> crypto.Point:
"""
out_key - H_s(derivation || varint(output_index))G
"""
crypto.check_ed25519point(out_key)
scalar = crypto.derivation_to_scalar(derivation, output_index)
point2 = crypto.scalarmult_base(scalar)
point4 = crypto.point_sub(out_key, point2)
crypto.ge25519_check(out_key)
scalar = crypto_helpers.derivation_to_scalar(derivation, output_index)
point2 = crypto.scalarmult_base_into(None, scalar)
point4 = crypto.point_sub_into(None, out_key, point2)
return point4
def generate_key_image(public_key: bytes, secret_key: Sc25519) -> Ge25519:
def generate_key_image(public_key: bytes, secret_key: crypto.Scalar) -> crypto.Point:
"""
Key image: secret_key * H_p(pub_key)
"""
point = crypto.hash_to_point(public_key)
point2 = crypto.scalarmult(point, secret_key)
point = crypto.hash_to_point_into(None, public_key)
point2 = crypto.scalarmult_into(None, point, secret_key)
return point2
def is_out_to_account(
subaddresses: Subaddresses,
out_key: Ge25519,
derivation: Ge25519,
additional_derivation: Ge25519,
out_key: crypto.Point,
derivation: crypto.Point,
additional_derivation: crypto.Point | None,
output_index: int,
creds: AccountCreds | None = None,
sub_addr_major: int = None,
sub_addr_minor: int = None,
):
creds: AccountCreds | None,
sub_addr_major: int | None,
sub_addr_minor: int | None,
) -> tuple[tuple[int, int], crypto.Point] | None:
"""
Checks whether the given transaction is sent to the account.
Searches subaddresses for the computed subaddress_spendkey.
@ -101,7 +102,7 @@ def is_out_to_account(
return (sub_addr_major, sub_addr_minor), derivation
if subaddresses:
subaddress_spendkey = crypto.encodepoint(subaddress_spendkey_obj)
subaddress_spendkey = crypto_helpers.encodepoint(subaddress_spendkey_obj)
if subaddress_spendkey in subaddresses:
return subaddresses[subaddress_spendkey], derivation
@ -111,10 +112,12 @@ def is_out_to_account(
)
if sub_pub_key and crypto.point_eq(subaddress_spendkey_obj, sub_pub_key):
# sub_pub_key is only set if sub_addr_{major, minor} are set
assert sub_addr_major is not None and sub_addr_minor is not None
return (sub_addr_major, sub_addr_minor), additional_derivation
if subaddresses:
subaddress_spendkey = crypto.encodepoint(subaddress_spendkey_obj)
subaddress_spendkey = crypto_helpers.encodepoint(subaddress_spendkey_obj)
if subaddress_spendkey in subaddresses:
return subaddresses[subaddress_spendkey], additional_derivation
@ -123,11 +126,11 @@ def is_out_to_account(
def generate_tx_spend_and_key_image(
ack: AccountCreds,
out_key: Ge25519,
recv_derivation: Ge25519,
out_key: crypto.Point,
recv_derivation: crypto.Point,
real_output_index: int,
received_index: tuple[int, int],
) -> tuple[Sc25519, Ge25519] | None:
) -> tuple[crypto.Scalar, crypto.Point]:
"""
Generates UTXO spending key and key image.
Corresponds to generate_key_image_helper_precomp() in the Monero codebase.
@ -140,11 +143,11 @@ def generate_tx_spend_and_key_image(
:param received_index: subaddress index this payment was received to
:return:
"""
if not crypto.sc_isnonzero(ack.spend_key_private):
if crypto.sc_iszero(ack.spend_key_private):
raise ValueError("Watch-only wallet not supported")
# derive secret key with subaddress - step 1: original CN derivation
scalar_step1 = crypto.derive_secret_key(
scalar_step1 = crypto_helpers.derive_secret_key(
recv_derivation, real_output_index, ack.spend_key_private
)
@ -156,10 +159,10 @@ def generate_tx_spend_and_key_image(
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)
scalar_step2 = crypto.sc_add_into(None, scalar_step1, subaddr_sk)
# When not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
pub_ver = crypto.scalarmult_base(scalar_step2)
pub_ver = crypto.scalarmult_base_into(None, scalar_step2)
# <Multisig>, branch deactivated until implemented
# # When in multisig, we only know the partial spend secret key. But we do know the full spend public key,
@ -179,20 +182,20 @@ def generate_tx_spend_and_key_image(
"key image helper precomp: given output pubkey doesn't match the derived one"
)
ki = generate_key_image(crypto.encodepoint(pub_ver), scalar_step2)
ki = generate_key_image(crypto_helpers.encodepoint(pub_ver), scalar_step2)
return scalar_step2, ki
def generate_tx_spend_and_key_image_and_derivation(
creds: AccountCreds,
subaddresses: Subaddresses,
out_key: Ge25519,
tx_public_key: Ge25519,
additional_tx_public_key: Ge25519,
real_output_index: int,
sub_addr_major: int = None,
sub_addr_minor: int = None,
) -> tuple[Sc25519, Ge25519, Ge25519]:
out_key: crypto.Point,
tx_public_key: crypto.Point,
additional_tx_public_key: crypto.Point | None,
real_output_index: int | None,
sub_addr_major: int | None,
sub_addr_minor: int | None,
) -> tuple[crypto.Scalar, crypto.Point, crypto.Point]:
"""
Generates UTXO spending key and key image and corresponding derivation.
Supports subaddresses.
@ -208,12 +211,14 @@ def generate_tx_spend_and_key_image_and_derivation(
:param sub_addr_minor: subaddress minor index
:return:
"""
recv_derivation = crypto.generate_key_derivation(
recv_derivation = crypto_helpers.generate_key_derivation(
tx_public_key, creds.view_key_private
)
additional_recv_derivation = (
crypto.generate_key_derivation(additional_tx_public_key, creds.view_key_private)
crypto_helpers.generate_key_derivation(
additional_tx_public_key, creds.view_key_private
)
if additional_tx_public_key
else None
)
@ -257,56 +262,55 @@ def compute_subaddresses(
for idx in indices:
if account == 0 and idx == 0:
subaddresses[crypto.encodepoint(creds.spend_key_public)] = (0, 0)
subaddresses[crypto_helpers.encodepoint(creds.spend_key_public)] = (0, 0)
continue
pub = get_subaddress_spend_public_key(
creds.view_key_private, creds.spend_key_public, major=account, minor=idx
)
pub = crypto.encodepoint(pub)
pub = crypto_helpers.encodepoint(pub)
subaddresses[pub] = (account, idx)
return subaddresses
def generate_keys(recovery_key: Sc25519) -> tuple[Sc25519, Ge25519]:
pub = crypto.scalarmult_base(recovery_key)
def generate_keys(recovery_key: crypto.Scalar) -> tuple[crypto.Scalar, crypto.Point]:
pub = crypto.scalarmult_base_into(None, recovery_key)
return recovery_key, pub
def generate_monero_keys(seed: bytes) -> tuple[Sc25519, Ge25519, Sc25519, Ge25519]:
def generate_monero_keys(
seed: bytes,
) -> tuple[crypto.Scalar, crypto.Point, crypto.Scalar, crypto.Point]:
"""
Generates spend key / view key from the seed in the same manner as Monero code does.
account.cpp:
crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random).
"""
spend_sec, spend_pub = generate_keys(crypto.decodeint(seed))
hash = crypto.cn_fast_hash(crypto.encodeint(spend_sec))
view_sec, view_pub = generate_keys(crypto.decodeint(hash))
spend_sec, spend_pub = generate_keys(crypto_helpers.decodeint(seed))
hash = crypto.fast_hash_into(None, crypto_helpers.encodeint(spend_sec))
view_sec, view_pub = generate_keys(crypto_helpers.decodeint(hash))
return spend_sec, spend_pub, view_sec, view_pub
def generate_sub_address_keys(
view_sec: Sc25519, spend_pub: Ge25519, major: int, minor: int
) -> tuple[Ge25519, Ge25519]:
view_sec: crypto.Scalar, spend_pub: crypto.Point, major: int, minor: int
) -> tuple[crypto.Point, crypto.Point]:
if major == 0 and minor == 0: # special case, Monero-defined
return spend_pub, crypto.scalarmult_base(view_sec)
return spend_pub, crypto.scalarmult_base_into(None, 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)
M = crypto.scalarmult_base_into(None, m)
D = crypto.point_add_into(None, spend_pub, M)
C = crypto.scalarmult_into(None, D, view_sec)
return D, C
def commitment_mask(key: bytes, buff: Sc25519 | None = None) -> Sc25519:
def commitment_mask(key: bytes, buff: crypto.Scalar | None = None) -> crypto.Scalar:
"""
Generates deterministic commitment mask for Bulletproof2
"""
data = bytearray(15 + 32)
data[0:15] = b"commitment_mask"
data[15:] = key
if buff:
return crypto.hash_to_scalar_into(buff, data)
else:
return crypto.hash_to_scalar(data)
return crypto.hash_to_scalar_into(buff, data)

@ -1,8 +1,4 @@
class NetworkTypes:
MAINNET = 0
TESTNET = 1
STAGENET = 2
FAKECHAIN = 3
from trezor.enums import MoneroNetworkType
class MainNet:
@ -24,8 +20,10 @@ class StageNet:
def net_version(
network_type=NetworkTypes.MAINNET, is_subaddr=False, is_integrated=False
):
network_type: MoneroNetworkType = MoneroNetworkType.MAINNET,
is_subaddr: bool = False,
is_integrated: bool = False,
) -> bytes:
"""
Network version bytes used for address construction
"""
@ -33,11 +31,11 @@ def net_version(
raise ValueError("Subaddress cannot be integrated")
c_net = None
if network_type is None or network_type == NetworkTypes.MAINNET:
if network_type == MoneroNetworkType.MAINNET:
c_net = MainNet
elif network_type == NetworkTypes.TESTNET:
elif network_type == MoneroNetworkType.TESTNET:
c_net = TestNet
elif network_type == NetworkTypes.STAGENET:
elif network_type == MoneroNetworkType.STAGENET:
c_net = StageNet
else:
raise ValueError(f"Unknown network type: {network_type}")

@ -11,35 +11,36 @@ Author: Dusan Klinec, ph4r05, 2018
import gc
from typing import TYPE_CHECKING
from apps.monero.xmr import crypto
from apps.monero.xmr import crypto, crypto_helpers
if TYPE_CHECKING:
from apps.monero.xmr.types import Sc25519
from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof
def prove_range_bp_batch(amounts: list[int], masks: list[Sc25519]) -> Bulletproof:
def prove_range_bp_batch(amounts: list[int], masks: list[crypto.Scalar]) -> Bulletproof:
"""Calculates Bulletproof in batches"""
from apps.monero.xmr import bulletproof as bp
bpi = bp.BulletProofBuilder()
bp_proof = bpi.prove_batch([crypto.sc_init(a) for a in amounts], masks)
bp_proof = bpi.prove_batch([crypto.Scalar(a) for a in amounts], masks)
del (bpi, bp)
gc.collect()
return bp_proof
def verify_bp(bp_proof: Bulletproof, amounts: list[int], masks: list[Sc25519]) -> bool:
def verify_bp(
bp_proof: Bulletproof, amounts: list[int], masks: list[crypto.Scalar]
) -> bool:
"""Verifies Bulletproof"""
from apps.monero.xmr import bulletproof as bp
if amounts:
bp_proof.V = []
for i in range(len(amounts)):
C = crypto.gen_commitment(masks[i], amounts[i])
crypto.scalarmult_into(C, C, crypto.sc_inv_eight())
bp_proof.V.append(crypto.encodepoint(C))
C = crypto.gen_commitment_into(None, masks[i], amounts[i])
crypto.scalarmult_into(C, C, crypto_helpers.INV_EIGHT_SC)
bp_proof.V.append(crypto_helpers.encodepoint(C))
bpi = bp.BulletProofBuilder()
res = bpi.verify(bp_proof)

@ -1,14 +1,24 @@
import gc
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import TypeVar
def parse_msg(buf: bytes, msg_type):
from .message_types import MessageType
T = TypeVar("T", bound=MessageType)
def parse_msg(buf: bytes, msg_type: type[T]) -> T:
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter
reader = MemoryReaderWriter(memoryview(buf))
return msg_type.load(reader)
def dump_msg(msg, preallocate: int = None, prefix: bytes = None) -> bytes:
def dump_msg(
msg: MessageType, preallocate: int | None = None, prefix: bytes | None = None
) -> bytes:
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter
writer = MemoryReaderWriter(preallocate=preallocate)
@ -20,7 +30,9 @@ def dump_msg(msg, preallocate: int = None, prefix: bytes = None) -> bytes:
return writer.get_buffer()
def dump_msg_gc(msg, preallocate: int = None, prefix: bytes = None) -> bytes:
def dump_msg_gc(
msg: MessageType, preallocate: int | None = None, prefix: bytes | None = None
) -> bytes:
buf = dump_msg(msg, preallocate, prefix)
del msg
gc.collect()

@ -1,3 +1,5 @@
from typing import TYPE_CHECKING
from apps.monero.xmr.serialize.int_serialize import (
dump_uint,
dump_uvarint,
@ -5,30 +7,60 @@ from apps.monero.xmr.serialize.int_serialize import (
load_uvarint,
)
if TYPE_CHECKING:
from typing import Protocol, TypeVar, Union
T = TypeVar("T")
XT = TypeVar("XT", bound="XmrType")
ST = TypeVar("ST", bound="XmrStructuredType")
XmrFieldType = Union[
tuple[str, XT],
tuple[str, ST, XT],
]
XmrFspec = tuple[XmrFieldType, ...]
class Writer(Protocol):
def write(self, __data: bytes) -> None:
...
class Reader(Protocol):
def readinto(self, __buffer: bytearray | memoryview) -> int:
...
class XmrType(Protocol[T]):
def load(self, __reader: Reader) -> T:
...
def dump(self, __writer: Writer, __value: T) -> None:
...
class XmrType:
pass
class XmrStructuredType(XmrType):
def f_specs(self) -> XmrFspec:
...
class UVarintType(XmrType):
class UVarintType:
@staticmethod
def load(reader) -> int:
def load(reader: Reader) -> int:
return load_uvarint(reader)
@staticmethod
def dump(writer, n: int):
def dump(writer: Writer, n: int) -> None:
return dump_uvarint(writer, n)
class IntType(XmrType):
class IntType:
WIDTH = 0
@classmethod
def load(cls, reader) -> int:
def load(cls, reader: Reader) -> int:
return load_uint(reader, cls.WIDTH)
@classmethod
def dump(cls, writer, n: int):
def dump(cls, writer: Writer, n: int):
return dump_uint(writer, n, cls.WIDTH)

@ -1,7 +1,12 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .base_types import Reader, Writer
_UINT_BUFFER = bytearray(1)
def load_uint(reader, width):
def load_uint(reader: Reader, width: int) -> int:
"""
Constant-width integer serialization
"""
@ -15,7 +20,7 @@ def load_uint(reader, width):
return result
def dump_uint(writer, n, width):
def dump_uint(writer: Writer, n: int, width: int) -> None:
"""
Constant-width integer serialization
"""
@ -26,7 +31,7 @@ def dump_uint(writer, n, width):
n >>= 8
def uvarint_size(n):
def uvarint_size(n: int) -> int:
"""
Returns size in bytes n would occupy serialized as varint
"""
@ -37,7 +42,7 @@ def uvarint_size(n):
return bts
def load_uvarint_b(buffer):
def load_uvarint_b(buffer: bytes) -> int:
"""
Variable int deserialization, synchronous from buffer.
"""
@ -51,7 +56,7 @@ def load_uvarint_b(buffer):
return result
def dump_uvarint_b(n):
def dump_uvarint_b(n: int) -> bytearray:
"""
Serializes uvarint to the buffer
"""
@ -59,7 +64,7 @@ def dump_uvarint_b(n):
return dump_uvarint_b_into(n, buffer, 0)
def dump_uvarint_b_into(n, buffer, offset=0):
def dump_uvarint_b_into(n: int, buffer: bytearray, offset: int = 0) -> bytearray:
"""
Serializes n as variable size integer to the provided buffer.
"""
@ -74,7 +79,9 @@ def dump_uvarint_b_into(n, buffer, offset=0):
return buffer
def dump_uint_b_into(n, width, buffer, offset=0):
def dump_uint_b_into(
n: int, width: int, buffer: bytearray, offset: int = 0
) -> bytearray:
"""
Serializes fixed size integer to the buffer
"""
@ -84,7 +91,7 @@ def dump_uint_b_into(n, width, buffer, offset=0):
return buffer
def load_uvarint(reader):
def load_uvarint(reader: Reader) -> int:
buffer = _UINT_BUFFER
result = 0
shift = 0
@ -97,7 +104,7 @@ def load_uvarint(reader):
return result
def dump_uvarint(writer, n):
def dump_uvarint(writer: Writer, n: int) -> None:
if n < 0:
raise ValueError("Cannot dump signed value, convert it to unsigned first.")
buffer = _UINT_BUFFER

@ -1,33 +1,41 @@
from typing import TYPE_CHECKING
from trezor.utils import obj_eq, obj_repr
from apps.monero.xmr.serialize.base_types import XmrType
from apps.monero.xmr.serialize.int_serialize import (
dump_uint,
dump_uvarint,
load_uint,
load_uvarint,
)
from apps.monero.xmr.serialize.int_serialize import dump_uvarint, load_uvarint
if TYPE_CHECKING:
from typing import TypeVar, Generic
from .base_types import XmrType, XmrFspec, Reader, Writer
T = TypeVar("T", bound=XmrType)
MT = TypeVar("MT", bound="MessageType")
else:
Generic = XmrType = [object]
T = 0
class UnicodeType(XmrType):
class UnicodeType:
"""
Unicode data in UTF-8 encoding.
"""
@staticmethod
def dump(writer, s):
def dump(writer: Writer, s: str) -> None:
dump_uvarint(writer, len(s))
writer.write(bytes(s))
writer.write(s.encode())
@staticmethod
def load(reader):
def load(reader: Reader) -> str:
ivalue = load_uvarint(reader)
fvalue = bytearray(ivalue)
reader.readinto(fvalue)
return str(fvalue)
class BlobType(XmrType):
class BlobType:
"""
Binary data, represented as bytearray. BlobType is only a scheme
descriptor. Behaves in the same way as primitive types.
@ -37,7 +45,7 @@ class BlobType(XmrType):
SIZE = 0
@classmethod
def dump(cls, writer, elem: bytes):
def dump(cls, writer: Writer, elem: bytes) -> None:
if cls.FIX_SIZE:
if cls.SIZE != len(elem):
raise ValueError("Size mismatch")
@ -46,7 +54,7 @@ class BlobType(XmrType):
writer.write(elem)
@classmethod
def load(cls, reader) -> bytearray:
def load(cls, reader: Reader) -> bytes:
if cls.FIX_SIZE:
size = cls.SIZE
else:
@ -56,7 +64,7 @@ class BlobType(XmrType):
return elem
class ContainerType(XmrType):
class ContainerType(Generic[T]):
"""
Array of elements, represented as a list of items. ContainerType is only a
scheme descriptor.
@ -64,10 +72,12 @@ class ContainerType(XmrType):
FIX_SIZE = 0
SIZE = 0
ELEM_TYPE = None
ELEM_TYPE: XmrType[T]
@classmethod
def dump(cls, writer, elems, elem_type=None):
def dump(
cls, writer: Writer, elems: list[T], elem_type: XmrType[T] | None = None
) -> None:
if elem_type is None:
elem_type = cls.ELEM_TYPE
if cls.FIX_SIZE:
@ -79,7 +89,7 @@ class ContainerType(XmrType):
elem_type.dump(writer, elem)
@classmethod
def load(cls, reader, elem_type=None):
def load(cls, reader: Reader, elem_type: XmrType[T] | None = None) -> list[T]:
if elem_type is None:
elem_type = cls.ELEM_TYPE
if cls.FIX_SIZE:
@ -93,42 +103,7 @@ class ContainerType(XmrType):
return elems
class VariantType(XmrType):
"""
Union of types, differentiated by variant tags. VariantType is only a scheme
descriptor.
"""
@classmethod
def dump(cls, writer, elem):
for field in cls.f_specs():
ftype = field[1]
if isinstance(elem, ftype):
break
else:
raise ValueError(f"Unrecognized variant: {elem}")
dump_uint(writer, ftype.VARIANT_CODE, 1)
ftype.dump(writer, elem)
@classmethod
def load(cls, reader):
tag = load_uint(reader, 1)
for field in cls.f_specs():
ftype = field[1]
if ftype.VARIANT_CODE == tag:
fvalue = ftype.load(reader)
break
else:
raise ValueError(f"Unknown tag: {tag}")
return fvalue
@classmethod
def f_specs(cls):
return ()
class MessageType(XmrType):
class MessageType:
"""
Message composed of fields with specific types.
"""
@ -141,7 +116,7 @@ class MessageType(XmrType):
__repr__ = obj_repr
@classmethod
def dump(cls, writer, msg):
def dump(cls: type[MT], writer: Writer, msg: MT) -> None:
defs = cls.f_specs()
for field in defs:
fname, ftype, *fparams = field
@ -149,7 +124,7 @@ class MessageType(XmrType):
ftype.dump(writer, fvalue, *fparams)
@classmethod
def load(cls, reader):
def load(cls: type[MT], reader: Reader) -> MT:
msg = cls()
defs = cls.f_specs()
for field in defs:
@ -159,5 +134,5 @@ class MessageType(XmrType):
return msg
@classmethod
def f_specs(cls):
def f_specs(cls) -> XmrFspec:
return ()

@ -4,14 +4,12 @@ import gc
class MemoryReaderWriter:
def __init__(
self,
buffer=None,
read_empty=False,
threshold=None,
do_gc=False,
preallocate=None,
**kwargs
):
self.buffer = buffer
buffer: bytearray | memoryview | None = None,
read_empty: bool = False,
threshold: int | None = None,
do_gc: bool = False,
preallocate: int | None = None,
) -> None:
self.nread = 0
self.nwritten = 0
@ -25,20 +23,21 @@ class MemoryReaderWriter:
if preallocate is not None:
self.preallocate(preallocate)
elif self.buffer is None:
elif buffer is None:
self.buffer = bytearray(0)
else:
self.buffer = buffer
self.woffset = len(buffer)
def is_empty(self):
def is_empty(self) -> bool:
return self.offset == len(self.buffer) or self.offset == self.woffset
def preallocate(self, size):
def preallocate(self, size: int) -> None:
self.buffer = bytearray(size)
self.offset = 0
self.woffset = 0
def readinto(self, buf):
def readinto(self, buf: bytearray | memoryview) -> int:
ln = len(buf)
if not self.read_empty and ln > 0 and self.offset == len(self.buffer):
raise EOFError
@ -62,7 +61,8 @@ class MemoryReaderWriter:
return nread
def write(self, buf):
def write(self, buf: bytes) -> None:
assert isinstance(self.buffer, bytearray)
nwritten = len(buf)
nall = len(self.buffer)
towrite = nwritten
@ -93,8 +93,8 @@ class MemoryReaderWriter:
self.nwritten += nwritten
self.ndata += nwritten
return nwritten
# return nwritten
def get_buffer(self):
def get_buffer(self) -> bytes:
mv = memoryview(self.buffer)
return mv[self.offset : self.woffset]

@ -1,6 +1,12 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .. import crypto
class CtKey:
__slots__ = ("dest", "mask")
def __init__(self, dest, mask):
def __init__(self, dest: crypto.Scalar, mask: crypto.Scalar) -> None:
self.dest = dest
self.mask = mask

@ -10,7 +10,7 @@ class TxinToKey(MessageType):
VARIANT_CODE = const(0x2)
@classmethod
def f_specs(cls):
def f_specs(cls) -> tuple:
return (
("amount", UVarintType),
("key_offsets", ContainerType, UVarintType),

@ -1,19 +1,23 @@
from micropython import const
from typing import TYPE_CHECKING
from apps.monero.xmr.serialize.message_types import ContainerType, MessageType
from apps.monero.xmr.serialize_messages.base import ECKey
if TYPE_CHECKING:
from ..serialize.base_types import XmrType
class _KeyV(ContainerType):
FIX_SIZE = const(0)
ELEM_TYPE = ECKey
ELEM_TYPE: XmrType[bytes] = ECKey
class Bulletproof(MessageType):
__slots__ = ("A", "S", "T1", "T2", "taux", "mu", "L", "R", "a", "b", "t")
__slots__ = ("A", "S", "T1", "T2", "taux", "mu", "L", "R", "a", "b", "t", "V")
@classmethod
def f_specs(cls):
def f_specs(cls) -> tuple:
return (
("A", ECKey),
("S", ECKey),

@ -1,7 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.crypto import monero as tcry
Ge25519 = tcry.ge25519
Sc25519 = tcry.bignum256modm

@ -0,0 +1,8 @@
# Automatically generated by pb2py
# fmt: off
# isort:skip_file
MAINNET = 0
TESTNET = 1
STAGENET = 2
FAKECHAIN = 3

@ -456,6 +456,12 @@ if TYPE_CHECKING:
ARRAY = 7
STRUCT = 8
class MoneroNetworkType(IntEnum):
MAINNET = 0
TESTNET = 1
STAGENET = 2
FAKECHAIN = 3
class NEMMosaicLevy(IntEnum):
MosaicLevy_Absolute = 1
MosaicLevy_Percentile = 2

@ -39,6 +39,7 @@ if TYPE_CHECKING:
from trezor.enums import FailureType # noqa: F401
from trezor.enums import InputScriptType # noqa: F401
from trezor.enums import MessageType # noqa: F401
from trezor.enums import MoneroNetworkType # noqa: F401
from trezor.enums import NEMImportanceTransferMode # noqa: F401
from trezor.enums import NEMModificationType # noqa: F401
from trezor.enums import NEMMosaicLevy # noqa: F401
@ -3691,7 +3692,7 @@ if TYPE_CHECKING:
class MoneroGetAddress(protobuf.MessageType):
address_n: "list[int]"
show_display: "bool | None"
network_type: "int | None"
network_type: "MoneroNetworkType"
account: "int | None"
minor: "int | None"
payment_id: "bytes | None"
@ -3701,7 +3702,7 @@ if TYPE_CHECKING:
*,
address_n: "list[int] | None" = None,
show_display: "bool | None" = None,
network_type: "int | None" = None,
network_type: "MoneroNetworkType | None" = None,
account: "int | None" = None,
minor: "int | None" = None,
payment_id: "bytes | None" = None,
@ -3728,13 +3729,13 @@ if TYPE_CHECKING:
class MoneroGetWatchKey(protobuf.MessageType):
address_n: "list[int]"
network_type: "int | None"
network_type: "MoneroNetworkType"
def __init__(
self,
*,
address_n: "list[int] | None" = None,
network_type: "int | None" = None,
network_type: "MoneroNetworkType | None" = None,
) -> None:
pass
@ -3761,7 +3762,7 @@ if TYPE_CHECKING:
class MoneroTransactionInitRequest(protobuf.MessageType):
version: "int | None"
address_n: "list[int]"
network_type: "int | None"
network_type: "MoneroNetworkType"
tsx_data: "MoneroTransactionData | None"
def __init__(
@ -3769,7 +3770,7 @@ if TYPE_CHECKING:
*,
address_n: "list[int] | None" = None,
version: "int | None" = None,
network_type: "int | None" = None,
network_type: "MoneroNetworkType | None" = None,
tsx_data: "MoneroTransactionData | None" = None,
) -> None:
pass
@ -4051,20 +4052,20 @@ if TYPE_CHECKING:
return isinstance(msg, cls)
class MoneroKeyImageExportInitRequest(protobuf.MessageType):
num: "int | None"
hash: "bytes | None"
num: "int"
hash: "bytes"
address_n: "list[int]"
network_type: "int | None"
network_type: "MoneroNetworkType"
subs: "list[MoneroSubAddressIndicesList]"
def __init__(
self,
*,
num: "int",
hash: "bytes",
address_n: "list[int] | None" = None,
subs: "list[MoneroSubAddressIndicesList] | None" = None,
num: "int | None" = None,
hash: "bytes | None" = None,
network_type: "int | None" = None,
network_type: "MoneroNetworkType | None" = None,
) -> None:
pass
@ -4128,23 +4129,23 @@ if TYPE_CHECKING:
class MoneroGetTxKeyRequest(protobuf.MessageType):
address_n: "list[int]"
network_type: "int | None"
salt1: "bytes | None"
salt2: "bytes | None"
tx_enc_keys: "bytes | None"
tx_prefix_hash: "bytes | None"
network_type: "MoneroNetworkType"
salt1: "bytes"
salt2: "bytes"
tx_enc_keys: "bytes"
tx_prefix_hash: "bytes"
reason: "int | None"
view_public_key: "bytes | None"
def __init__(
self,
*,
salt1: "bytes",
salt2: "bytes",
tx_enc_keys: "bytes",
tx_prefix_hash: "bytes",
address_n: "list[int] | None" = None,
network_type: "int | None" = None,
salt1: "bytes | None" = None,
salt2: "bytes | None" = None,
tx_enc_keys: "bytes | None" = None,
tx_prefix_hash: "bytes | None" = None,
network_type: "MoneroNetworkType | None" = None,
reason: "int | None" = None,
view_public_key: "bytes | None" = None,
) -> None:
@ -4174,13 +4175,13 @@ if TYPE_CHECKING:
class MoneroLiveRefreshStartRequest(protobuf.MessageType):
address_n: "list[int]"
network_type: "int | None"
network_type: "MoneroNetworkType"
def __init__(
self,
*,
address_n: "list[int] | None" = None,
network_type: "int | None" = None,
network_type: "MoneroNetworkType | None" = None,
) -> None:
pass
@ -4195,20 +4196,20 @@ if TYPE_CHECKING:
return isinstance(msg, cls)
class MoneroLiveRefreshStepRequest(protobuf.MessageType):
out_key: "bytes | None"
recv_deriv: "bytes | None"
real_out_idx: "int | None"
sub_addr_major: "int | None"
sub_addr_minor: "int | None"
out_key: "bytes"
recv_deriv: "bytes"
real_out_idx: "int"
sub_addr_major: "int"
sub_addr_minor: "int"
def __init__(
self,
*,
out_key: "bytes | None" = None,
recv_deriv: "bytes | None" = None,
real_out_idx: "int | None" = None,
sub_addr_major: "int | None" = None,
sub_addr_minor: "int | None" = None,
out_key: "bytes",
recv_deriv: "bytes",
real_out_idx: "int",
sub_addr_major: "int",
sub_addr_minor: "int",
) -> None:
pass
@ -4329,14 +4330,14 @@ if TYPE_CHECKING:
return isinstance(msg, cls)
class MoneroRctKeyPublic(protobuf.MessageType):
dest: "bytes | None"
commitment: "bytes | None"
dest: "bytes"
commitment: "bytes"
def __init__(
self,
*,
dest: "bytes | None" = None,
commitment: "bytes | None" = None,
dest: "bytes",
commitment: "bytes",
) -> None:
pass
@ -4421,14 +4422,14 @@ if TYPE_CHECKING:
return isinstance(msg, cls)
class MoneroSubAddressIndicesList(protobuf.MessageType):
account: "int | None"
account: "int"
minor_indices: "list[int]"
def __init__(
self,
*,
account: "int",
minor_indices: "list[int] | None" = None,
account: "int | None" = None,
) -> None:
pass
@ -4437,20 +4438,20 @@ if TYPE_CHECKING:
return isinstance(msg, cls)
class MoneroTransferDetails(protobuf.MessageType):
out_key: "bytes | None"
tx_pub_key: "bytes | None"
out_key: "bytes"
tx_pub_key: "bytes"
additional_tx_pub_keys: "list[bytes]"
internal_output_index: "int | None"
internal_output_index: "int"
sub_addr_major: "int | None"
sub_addr_minor: "int | None"
def __init__(
self,
*,
out_key: "bytes",
tx_pub_key: "bytes",
internal_output_index: "int",
additional_tx_pub_keys: "list[bytes] | None" = None,
out_key: "bytes | None" = None,
tx_pub_key: "bytes | None" = None,
internal_output_index: "int | None" = None,
sub_addr_major: "int | None" = None,
sub_addr_minor: "int | None" = None,
) -> None:

@ -12,8 +12,8 @@ class TestMoneroBulletproof(unittest.TestCase):
pass
def mask_consistency_check(self, bpi):
sv = [crypto.sc_init(123)]
gamma = [crypto.sc_init(432)]
sv = [crypto.Scalar(123)]
gamma = [crypto.Scalar(432)]
M, logM, aL, aR, V, gamma = bpi.prove_setup(sv, gamma)
x = bp._ensure_dst_key()
@ -293,15 +293,15 @@ class TestMoneroBulletproof(unittest.TestCase):
def test_prove(self):
bpi = bp.BulletProofBuilder()
val = crypto.sc_init(123)
mask = crypto.sc_init(432)
val = crypto.Scalar(123)
mask = crypto.Scalar(432)
bp_res = bpi.prove(val, mask)
bpi.verify(bp_res)
def test_prove_2(self):
bpi = bp.BulletProofBuilder()
val = crypto.sc_init((1 << 30) - 1 + 16)
val = crypto.Scalar((1 << 30) - 1 + 16)
mask = crypto.random_scalar()
bp_res = bpi.prove(val, mask)
@ -322,7 +322,7 @@ class TestMoneroBulletproof(unittest.TestCase):
def test_prove_random_masks(self):
bpi = bp.BulletProofBuilder()
bpi.use_det_masks = False # trully randomly generated mask vectors
val = crypto.sc_init((1 << 30) - 1 + 16)
val = crypto.Scalar((1 << 30) - 1 + 16)
mask = crypto.random_scalar()
bp_res = bpi.prove(val, mask)
@ -331,8 +331,8 @@ class TestMoneroBulletproof(unittest.TestCase):
def ctest_multiexp(self):
scalars = [0, 1, 2, 3, 4, 99]
point_base = [0, 2, 4, 7, 12, 18]
scalar_sc = [crypto.sc_init(x) for x in scalars]
points = [crypto.scalarmult_base(crypto.sc_init(x)) for x in point_base]
scalar_sc = [crypto.Scalar(x) for x in scalars]
points = [crypto.scalarmult_base_into(None, crypto.Scalar(x)) for x in point_base]
muex = bp.MultiExp(scalars=[crypto.encodeint(x) for x in scalar_sc],
point_fnc=lambda i, d: crypto.encodepoint(points[i]))
@ -340,24 +340,24 @@ class TestMoneroBulletproof(unittest.TestCase):
self.assertEqual(len(muex), len(scalars))
res = bp.multiexp(None, muex)
res2 = bp.vector_exponent_custom(
A=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base(crypto.sc_init(point_base[i])), d)),
B=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base(crypto.sc_init(point_base[3+i])), d)),
a=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.sc_init(scalars[i]), d),),
b=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.sc_init(scalars[i+3]), d)),
A=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base_into(None, crypto.Scalar(point_base[i])), d)),
B=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base_into(None, crypto.Scalar(point_base[3+i])), d)),
a=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.Scalar(scalars[i]), d),),
b=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.Scalar(scalars[i+3]), d)),
)
self.assertEqual(res, res2)
def test_prove_batch(self):
bpi = bp.BulletProofBuilder()
sv = [crypto.sc_init(123), crypto.sc_init(768)]
gamma = [crypto.sc_init(456), crypto.sc_init(901)]
sv = [crypto.Scalar(123), crypto.Scalar(768)]
gamma = [crypto.Scalar(456), crypto.Scalar(901)]
proof = bpi.prove_batch(sv, gamma)
bpi.verify_batch([proof])
def test_prove_batch16(self):
bpi = bp.BulletProofBuilder()
sv = [crypto.sc_init(137*i) for i in range(16)]
gamma = [crypto.sc_init(991*i) for i in range(16)]
sv = [crypto.Scalar(137*i) for i in range(16)]
gamma = [crypto.Scalar(991*i) for i in range(16)]
proof = bpi.prove_batch(sv, gamma)
bpi.verify_batch([proof])

@ -1,11 +1,14 @@
from common import *
if not utils.BITCOIN_ONLY:
from apps.monero.xmr import crypto, mlsag
from apps.monero.xmr import crypto, crypto_helpers, clsag
from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey
from trezor.crypto import monero as tcry
from trezor.crypto import random
import ubinascii
point_mul8_into = tcry.ge25519_mul8
class TmpKey:
def __init__(self, d, c):
@ -17,18 +20,18 @@ class TmpKey:
class TestMoneroClsag(unittest.TestCase):
def verify_clsag(self, msg, ss, sc1, sI, sD, pubs, C_offset):
n = len(pubs)
c = crypto.new_scalar()
D_8 = crypto.new_point()
c = crypto.Scalar()
D_8 = crypto.Point()
tmp_bf = bytearray(32)
C_offset_bf = crypto.encodepoint(C_offset)
C_offset_bf = crypto_helpers.encodepoint(C_offset)
crypto.sc_copy(c, sc1)
crypto.point_mul8_into(D_8, sD)
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(mlsag._HASH_KEY_CLSAG_AGG_0)
hsh_C.update(mlsag._HASH_KEY_CLSAG_AGG_1)
hsh_P = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
hsh_C = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
hsh_P.update(clsag._HASH_KEY_CLSAG_AGG_0)
hsh_C.update(clsag._HASH_KEY_CLSAG_AGG_1)
def hsh_PC(x):
hsh_P.update(x)
@ -43,11 +46,11 @@ class TestMoneroClsag(unittest.TestCase):
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())
mu_P = crypto_helpers.decodeint(hsh_P.digest())
mu_C = crypto_helpers.decodeint(hsh_C.digest())
c_to_hash = crypto.get_keccak() # domain, P, C, C_offset, message, L, R
c_to_hash.update(mlsag._HASH_KEY_CLSAG_ROUND)
c_to_hash = crypto_helpers.get_keccak() # domain, P, C, C_offset, message, L, R
c_to_hash.update(clsag._HASH_KEY_CLSAG_ROUND)
for i in range(len(pubs)):
c_to_hash.update(pubs[i].dest)
for i in range(len(pubs)):
@ -55,25 +58,25 @@ class TestMoneroClsag(unittest.TestCase):
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()
c_p = crypto.Scalar()
c_c = crypto.Scalar()
L = crypto.Point()
R = crypto.Point()
tmp_pt = crypto.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
C_P = crypto.point_sub_into(
None, 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)
HP = crypto.hash_to_point_into(None, 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))
@ -82,8 +85,8 @@ class TestMoneroClsag(unittest.TestCase):
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()):
res = crypto.sc_sub_into(None, c, sc1)
if not crypto.sc_eq(res, crypto.Scalar(0)):
raise ValueError("Signature error")
def gen_clsag_test(self, ring_size=11, index=None):
@ -93,60 +96,67 @@ class TestMoneroClsag(unittest.TestCase):
def gen_clsag_sig(self, ring_size=11, index=None):
msg = random.bytes(32)
amnt = crypto.sc_init(random.uniform(0xFFFFFF) + 12)
amnt = crypto.Scalar(random.uniform(0xFFFFFF) + 12)
priv = crypto.random_scalar()
msk = crypto.random_scalar()
alpha = crypto.random_scalar()
P = crypto.scalarmult_base(priv)
C = crypto.add_keys2(msk, amnt, crypto.xmr_H())
Cp = crypto.add_keys2(alpha, amnt, crypto.xmr_H())
P = crypto.scalarmult_base_into(None, priv)
C = crypto.add_keys2_into(None, msk, amnt, crypto.xmr_H())
Cp = crypto.add_keys2_into(None, alpha, amnt, crypto.xmr_H())
ring = []
for i in range(ring_size - 1):
tk = TmpKey(
crypto.encodepoint(crypto.scalarmult_base(crypto.random_scalar())),
crypto.encodepoint(crypto.scalarmult_base(crypto.random_scalar())),
crypto_helpers.encodepoint(
crypto.scalarmult_base_into(None, crypto.random_scalar())
),
crypto_helpers.encodepoint(
crypto.scalarmult_base_into(None, crypto.random_scalar())
),
)
ring.append(tk)
index = index if index is not None else random.uniform(len(ring))
ring.insert(index, TmpKey(crypto.encodepoint(P), crypto.encodepoint(C)))
ring.insert(index, TmpKey(crypto_helpers.encodepoint(P), crypto_helpers.encodepoint(C)))
ring2 = list(ring)
mg_buffer = []
self.assertTrue(
crypto.point_eq(
crypto.scalarmult_base(priv), crypto.decodepoint(ring[index].dest)
crypto.scalarmult_base_into(None, priv),
crypto_helpers.decodepoint(ring[index].dest),
)
)
self.assertTrue(
crypto.point_eq(
crypto.scalarmult_base(crypto.sc_sub(msk, alpha)),
crypto.point_sub(crypto.decodepoint(ring[index].commitment), Cp),
crypto.scalarmult_base_into(None, crypto.sc_sub_into(None, msk, alpha)),
crypto.point_sub_into(
None, crypto_helpers.decodepoint(ring[index].commitment), Cp
),
)
)
mlsag.generate_clsag_simple(
clsag.generate_clsag_simple(
msg, ring, CtKey(priv, msk), alpha, Cp, index, mg_buffer,
)
sD = crypto.decodepoint(mg_buffer[-1])
sc1 = crypto.decodeint(mg_buffer[-2])
scalars = [crypto.decodeint(x) for x in mg_buffer[1:-2]]
H = crypto.new_point()
sI = crypto.new_point()
sD = crypto_helpers.decodepoint(mg_buffer[-1])
sc1 = crypto_helpers.decodeint(mg_buffer[-2])
scalars = [crypto_helpers.decodeint(x) for x in mg_buffer[1:-2]]
H = crypto.Point()
sI = crypto.Point()
crypto.hash_to_point_into(H, crypto.encodepoint(P))
crypto.hash_to_point_into(H, crypto_helpers.encodepoint(P))
crypto.scalarmult_into(sI, H, priv) # I = p*H
return msg, scalars, sc1, sI, sD, ring2, Cp
def verify_monero_generated(self, clsag):
msg = ubinascii.unhexlify(clsag["msg"])
sI = crypto.decodepoint(ubinascii.unhexlify(clsag["sI"]))
sD = crypto.decodepoint(ubinascii.unhexlify(clsag["sD"]))
sc1 = crypto.decodeint(ubinascii.unhexlify(clsag["sc1"]))
Cout = crypto.decodepoint(ubinascii.unhexlify(clsag["cout"]))
scalars = [crypto.decodeint(ubinascii.unhexlify(x)) for x in clsag["ss"]]
sI = crypto_helpers.decodepoint(ubinascii.unhexlify(clsag["sI"]))
sD = crypto_helpers.decodepoint(ubinascii.unhexlify(clsag["sD"]))
sc1 = crypto_helpers.decodeint(ubinascii.unhexlify(clsag["sc1"]))
Cout = crypto_helpers.decodepoint(ubinascii.unhexlify(clsag["cout"]))
scalars = [crypto_helpers.decodeint(ubinascii.unhexlify(x)) for x in clsag["ss"]]
ring = []
for e in clsag["ring"]:
ring.append(TmpKey(ubinascii.unhexlify(e[0]), ubinascii.unhexlify(e[1])))
@ -160,43 +170,65 @@ class TestMoneroClsag(unittest.TestCase):
"sI": "a1c7f4a316ddd16374fe495d402be60566047ae5a1352554e98ebff118705303",
"sD": "cd80b5c7f3f597de6e20bcef669a4ba9eb3eb89ead12ab1c24c92acd609afcb2",
"sc1": "cf4f48ed60771d4e8d02e9e0af37281ceeb66573bd528ac256a7e17794a75602",
"ss":
["aaeffa564b5b0ff1e4ed72c9b595cd0241ac64eeb41b902a35688e369922d704"
, "1defc134a853252d734d19b29d8f2fabc85a8ae24ebcf8f050d4daf8a335e901"
, "cdf9ac576f0c7ceb7eb22c1a1254a801d0d2915e59870be8b1ab68cd1281120d"
, "d1973493d8224aaa9732878b9a88d448ea16185f94e5bafd82816277682fa108"
, "a130e076845e512687575942bf3694bcb44eb19eb1181af9a1fc2254949b7c0f"
, "26f5b6ea154d6bd4a969c742563d75f1bfcd5ded3af78669e45ba95e76c48605"
, "5b695d3be46b826fd11e043028dee2aa25cf36910e86537fcd1cd3f5cb49650e"
, "37e811ebb4a2b9c35556b4af911a03a93468f599956c034092c3ece9e1169208"
, "a361ceec9aacd65da6d3e686fbcd0c1aef26096321be7f01653157ee6096a201"
, "f9b762ef1df69bb12ca76a97dce11f7840b8ec63c3dc2683f7ae71cb79c49103"
, "ea010fa6a35f3bd3d7899a7a2a8df4d3ef9c9dfbbd56fe43ff5c7442821d3508"
]
, "ring": [
["241c0295b4c3a149e5ac7997963e125d0fc6cc8adad9349df3b01ff611936c87",
"3a24a4c418ccb2ceb83672d01534a73ff1e9f548937d5ddd7f1971c9b398868c"],
["ec432ccfbf730077cb2d8c59968e2796148a590eec7928ecf268d883ced0de5b",
"2973d6e9c27538fd0f7c003e014311e9403dcb6e7d86b66df65176a579943bda"],
["0cfeafc313a6a2e60110778d53d61fa1705e9049b8afba0f51c1127f6855c07f",
"ffa4d4c77202907832294243a96886920017b67fbe5b3800bcc1457c4a4a1ff0"],
["bd4eca22dc010a214524901b88bdda27e427217ff784c47520ee76743caba036",
"e07135f8398459133c2969184e70610b9b995f73e44acf54b6eaed6227e68bbc"],
["73c8d57d0128c99fc2ab0be8cee5fe5c1288b98e51822a6681846035fcc53fea",
"2987499fde3f4353013206d89fe2d7c6ad3cd9a66c9a36d17749e39112513572"],
["385c538901b79c6bd2ddea5191e808b1414c9dfdcaf424841d843dd788cb89ad",
"ec5f987fe138c6cb1d47ff75d77852b7c0a94ba1f0b93d22c0463f75986605bd"],
["fed06cb761745a6f087d1af13f84670ecbf1523d72b46e8bd0698d1cdfb398bc",
"5d81df981fb885f947b9404cb63cb06fe4e001be281f2bdfb3c638d54ec6e49e"],
["667d1edfb83a17bd81fcf7831362b6c9038f26340ee1fe56d41f62cb0b32e989",
"e9ceba97867b43cd5420c94fa61cc5f11e440e261df74dfc8b1c07ec4b13aa3c"],
["e1e76da5bd52fc065f9af40efde5f733f9673974d14c6af8d200d8576ac3a90d",
"97358d6ddad38b2707fb864bfcaaab935851af66d50bcbac569d159d740bdf71"],
["4fd5d0db88283c63905d5095a76b11a75337e43f403f8469175ba9c49741552e",
"af0ab85872a6355d5c82c1f9a2a41488146e19b272887a1f7385cc26bef3f1d8"],
["37e1a4c49a22340fa5ac2c22c1b7a891e7191cdc53911700a317c0d8b92bbf4e",
"5c89d29dad77de7d76ece8bb81c7c8cd15008f63c5a14ab1c984b3833e7bbce3"]
]
"ss": [
"aaeffa564b5b0ff1e4ed72c9b595cd0241ac64eeb41b902a35688e369922d704",
"1defc134a853252d734d19b29d8f2fabc85a8ae24ebcf8f050d4daf8a335e901",
"cdf9ac576f0c7ceb7eb22c1a1254a801d0d2915e59870be8b1ab68cd1281120d",
"d1973493d8224aaa9732878b9a88d448ea16185f94e5bafd82816277682fa108",
"a130e076845e512687575942bf3694bcb44eb19eb1181af9a1fc2254949b7c0f",
"26f5b6ea154d6bd4a969c742563d75f1bfcd5ded3af78669e45ba95e76c48605",
"5b695d3be46b826fd11e043028dee2aa25cf36910e86537fcd1cd3f5cb49650e",
"37e811ebb4a2b9c35556b4af911a03a93468f599956c034092c3ece9e1169208",
"a361ceec9aacd65da6d3e686fbcd0c1aef26096321be7f01653157ee6096a201",
"f9b762ef1df69bb12ca76a97dce11f7840b8ec63c3dc2683f7ae71cb79c49103",
"ea010fa6a35f3bd3d7899a7a2a8df4d3ef9c9dfbbd56fe43ff5c7442821d3508",
],
"ring": [
[
"241c0295b4c3a149e5ac7997963e125d0fc6cc8adad9349df3b01ff611936c87",
"3a24a4c418ccb2ceb83672d01534a73ff1e9f548937d5ddd7f1971c9b398868c",
],
[
"ec432ccfbf730077cb2d8c59968e2796148a590eec7928ecf268d883ced0de5b",
"2973d6e9c27538fd0f7c003e014311e9403dcb6e7d86b66df65176a579943bda",
],
[
"0cfeafc313a6a2e60110778d53d61fa1705e9049b8afba0f51c1127f6855c07f",
"ffa4d4c77202907832294243a96886920017b67fbe5b3800bcc1457c4a4a1ff0",
],
[
"bd4eca22dc010a214524901b88bdda27e427217ff784c47520ee76743caba036",
"e07135f8398459133c2969184e70610b9b995f73e44acf54b6eaed6227e68bbc",
],
[
"73c8d57d0128c99fc2ab0be8cee5fe5c1288b98e51822a6681846035fcc53fea",
"2987499fde3f4353013206d89fe2d7c6ad3cd9a66c9a36d17749e39112513572",
],
[
"385c538901b79c6bd2ddea5191e808b1414c9dfdcaf424841d843dd788cb89ad",
"ec5f987fe138c6cb1d47ff75d77852b7c0a94ba1f0b93d22c0463f75986605bd",
],
[
"fed06cb761745a6f087d1af13f84670ecbf1523d72b46e8bd0698d1cdfb398bc",
"5d81df981fb885f947b9404cb63cb06fe4e001be281f2bdfb3c638d54ec6e49e",
],
[
"667d1edfb83a17bd81fcf7831362b6c9038f26340ee1fe56d41f62cb0b32e989",
"e9ceba97867b43cd5420c94fa61cc5f11e440e261df74dfc8b1c07ec4b13aa3c",
],
[
"e1e76da5bd52fc065f9af40efde5f733f9673974d14c6af8d200d8576ac3a90d",
"97358d6ddad38b2707fb864bfcaaab935851af66d50bcbac569d159d740bdf71",
],
[
"4fd5d0db88283c63905d5095a76b11a75337e43f403f8469175ba9c49741552e",
"af0ab85872a6355d5c82c1f9a2a41488146e19b272887a1f7385cc26bef3f1d8",
],
[
"37e1a4c49a22340fa5ac2c22c1b7a891e7191cdc53911700a317c0d8b92bbf4e",
"5c89d29dad77de7d76ece8bb81c7c8cd15008f63c5a14ab1c984b3833e7bbce3",
],
],
}
self.verify_monero_generated(clsag)
@ -207,43 +239,65 @@ class TestMoneroClsag(unittest.TestCase):
"sI": "917fdd3086c056503ffdb1840f03c78d48bfe6d9d60b4efb194bd9798d03acaa",
"sD": "769d0ca9b272ac02c5efad7df6b5c00f2995c99ca80f4597136decba9a0dd36f",
"sc1": "fe5c7eb39a32d2aea12e6d127d847b72ea810bfbf3d5bbe23c40e7abdd12900e",
"ss":
["da2940c66cc2405032d959325c8804e216f76b36e71b2ae6b76417ed9c10a80a"
, "ca763505c2e5ebacf72098f8cba89ea6826aa448501f03d439c7a838a88bba0e"
, "b2eadee4c121e85b0c2a09d56c665ba19ee8ebc451f1e9e96cf72c874f945104"
, "5a79523fdc0df9a54ab3937c878bd5a02e62bff77efc338728deb060ecda4509"
, "dfadddc51866cde5206269270f44ca2f6350ca0b1328a968773fcacf57031502"
, "a964f3549a10fc8bdb2f8217df0e9b08e90477be19a665b94b73ce417622450b"
, "48e805427109268b04bf378c869501dbebb79c0cbe664bf7eb0ca222376d1c0f"
, "33f36d9a699e92a66d4b9fdf6c1123ae99701b117fbe8f0af9faec51e45eb409"
, "25ef746a03aaf59701d1d47ea3b9e9f092662cebc9d44902ce18e81cc5035f01"
, "2ba3022d4f9b57da7429499715592073f1608cf270318840a5fd3890bbf5950a"
, "8149ec0d965c9881d6a4adedca7d3c9090359dbfae56dbab526be102722aab09"
]
, "ring": [
["081b048be784e1ff6f3b7ebe602690c27723b5d9952405bcdcbed31d16125067",
"6090eccb73d2e1fc7bc7644a4fad04e5fe93d953a1258307c44d5b23cd636bf9"],
["e2f0f100f1634d7c7dd5a09bc6dd7ee53506d73536aa743e8ea049528e4cb2aa",
"632438f9aeda72eb9c6c434391cf9fa2f71788bea598a5d5729a5d502865932a"],
["6744197cfde37ad1901d518f112c0f4d820c23122a016949e300eec2ab88916c",
"1b251d5b32e22de29a4f99a0ed1de32754636175075e21b25d7283036eb85541"],
["0e86bb7ee0b4728f2fedde7ac5019b54de7b2bb19b44d1864e6346dac6c171ab",
"5a3c85e93890f802d4148140733dcdcd676353fce1bd774ce28034fc2ec00253"],
["1847ce49d9552651395b2fa80637c131a31036f0bfc5abb63526701cd1a32320",
"a9cb55bc24e6e1fb894c511f2edd4b7bda4c75a608657d952e85bab83ec98a52"],
["5c5d0b678f5045b0304e3c48027bd7e9ccaee1dac4449ed1f34b204868ca5651",
"badf83ccba38f2194f924a4f7fb7c2fd966b1e16c1fddeb3658033aa009febe0"],
["81961aa4c241a91d498d8f3057b31373d9fc72b6e7d7f98bf497e3dfe705eeaa",
"a0e632fbb801d6bce99ef97d7bb6acd945aff5cd7fab56c0e6fec6900a3babd7"],
["cbd89f10ddf152bd9c756d145ef4cda1d56a31f1e1936759bee04b7a8a815c76",
"8b835b8180f36e79ba79528e0d3401f439cc1c7f99e4bcfb3cb4aa2b60b1afc1"],
["a7bc55e955a825730f5dcdc3f8126717d7647cbca8a6b90e08b77269aeed3533",
"8da31e80698c9b5181b2e8d9773136083a34e3e72c92134d8201d9c368d89284"],
["a7902cec90d3f2de25c8ddc87075159fd00f219a51a1e7dcac17c2b8a91887e9",
"2b1e848b6649abefbd6b399504a169252358e7ff6bde8fa7a773b9cf0a167069"],
["9fc3d5fb7de8cfc59982f7b20f3f5c145ad191088e7f59c10908dc5d55863bee",
"b8de2bc9bb46d475007230a92af14afb6f9dd2804b5c31355a282b40ccdadc92"]
]
"ss": [
"da2940c66cc2405032d959325c8804e216f76b36e71b2ae6b76417ed9c10a80a",
"ca763505c2e5ebacf72098f8cba89ea6826aa448501f03d439c7a838a88bba0e",
"b2eadee4c121e85b0c2a09d56c665ba19ee8ebc451f1e9e96cf72c874f945104",
"5a79523fdc0df9a54ab3937c878bd5a02e62bff77efc338728deb060ecda4509",
"dfadddc51866cde5206269270f44ca2f6350ca0b1328a968773fcacf57031502",
"a964f3549a10fc8bdb2f8217df0e9b08e90477be19a665b94b73ce417622450b",
"48e805427109268b04bf378c869501dbebb79c0cbe664bf7eb0ca222376d1c0f",
"33f36d9a699e92a66d4b9fdf6c1123ae99701b117fbe8f0af9faec51e45eb409",
"25ef746a03aaf59701d1d47ea3b9e9f092662cebc9d44902ce18e81cc5035f01",
"2ba3022d4f9b57da7429499715592073f1608cf270318840a5fd3890bbf5950a",
"8149ec0d965c9881d6a4adedca7d3c9090359dbfae56dbab526be102722aab09",
],
"ring": [
[
"081b048be784e1ff6f3b7ebe602690c27723b5d9952405bcdcbed31d16125067",
"6090eccb73d2e1fc7bc7644a4fad04e5fe93d953a1258307c44d5b23cd636bf9",
],
[
"e2f0f100f1634d7c7dd5a09bc6dd7ee53506d73536aa743e8ea049528e4cb2aa",
"632438f9aeda72eb9c6c434391cf9fa2f71788bea598a5d5729a5d502865932a",
],
[
"6744197cfde37ad1901d518f112c0f4d820c23122a016949e300eec2ab88916c",
"1b251d5b32e22de29a4f99a0ed1de32754636175075e21b25d7283036eb85541",
],
[
"0e86bb7ee0b4728f2fedde7ac5019b54de7b2bb19b44d1864e6346dac6c171ab",
"5a3c85e93890f802d4148140733dcdcd676353fce1bd774ce28034fc2ec00253",
],
[
"1847ce49d9552651395b2fa80637c131a31036f0bfc5abb63526701cd1a32320",
"a9cb55bc24e6e1fb894c511f2edd4b7bda4c75a608657d952e85bab83ec98a52",
],
[
"5c5d0b678f5045b0304e3c48027bd7e9ccaee1dac4449ed1f34b204868ca5651",
"badf83ccba38f2194f924a4f7fb7c2fd966b1e16c1fddeb3658033aa009febe0",
],
[
"81961aa4c241a91d498d8f3057b31373d9fc72b6e7d7f98bf497e3dfe705eeaa",
"a0e632fbb801d6bce99ef97d7bb6acd945aff5cd7fab56c0e6fec6900a3babd7",
],
[
"cbd89f10ddf152bd9c756d145ef4cda1d56a31f1e1936759bee04b7a8a815c76",
"8b835b8180f36e79ba79528e0d3401f439cc1c7f99e4bcfb3cb4aa2b60b1afc1",
],
[
"a7bc55e955a825730f5dcdc3f8126717d7647cbca8a6b90e08b77269aeed3533",
"8da31e80698c9b5181b2e8d9773136083a34e3e72c92134d8201d9c368d89284",
],
[
"a7902cec90d3f2de25c8ddc87075159fd00f219a51a1e7dcac17c2b8a91887e9",
"2b1e848b6649abefbd6b399504a169252358e7ff6bde8fa7a773b9cf0a167069",
],
[
"9fc3d5fb7de8cfc59982f7b20f3f5c145ad191088e7f59c10908dc5d55863bee",
"b8de2bc9bb46d475007230a92af14afb6f9dd2804b5c31355a282b40ccdadc92",
],
],
}
self.verify_monero_generated(clsag)
@ -261,22 +315,22 @@ class TestMoneroClsag(unittest.TestCase):
res = self.gen_clsag_sig(ring_size=11, index=5)
msg, scalars, sc1, sI, sD, ring2, Cp = res
with self.assertRaises(ValueError):
sI = crypto.point_mul8(sI)
sI = point_mul8_into(None, sI)
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
def test_clsag_invalid_sD(self):
res = self.gen_clsag_sig(ring_size=11, index=5)
msg, scalars, sc1, sI, sD, ring2, Cp = res
with self.assertRaises(ValueError):
sD = crypto.scalarmult_base(crypto.random_scalar())
sD = crypto.scalarmult_base_into(None, crypto.random_scalar())
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
def test_clsag_invalid_P(self):
res = self.gen_clsag_sig(ring_size=11, index=5)
msg, scalars, sc1, sI, sD, ring2, Cp = res
with self.assertRaises(ValueError):
ring2[5].dest = crypto.encodepoint(
crypto.point_mul8(crypto.decodepoint(ring2[5].dest))
ring2[5].dest = crypto_helpers.encodepoint(
point_mul8_into(None, crypto_helpers.decodepoint(ring2[5].dest))
)
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
@ -284,8 +338,8 @@ class TestMoneroClsag(unittest.TestCase):
res = self.gen_clsag_sig(ring_size=11, index=5)
msg, scalars, sc1, sI, sD, ring2, Cp = res
with self.assertRaises(ValueError):
ring2[5].commitment = crypto.encodepoint(
crypto.point_mul8(crypto.decodepoint(ring2[5].dest))
ring2[5].commitment = crypto_helpers.encodepoint(
point_mul8_into(None, crypto_helpers.decodepoint(ring2[5].dest))
)
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
@ -293,7 +347,9 @@ class TestMoneroClsag(unittest.TestCase):
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)))
Cp = crypto.point_add_into(
None, Cp, crypto.scalarmult_base_into(None, crypto.Scalar(1))
)
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
def test_clsag_invalid_index(self):

@ -1,29 +1,29 @@
from common import *
if not utils.BITCOIN_ONLY:
from apps.monero.xmr import crypto, monero
from trezor.enums import MoneroNetworkType
from apps.monero.xmr import crypto, crypto_helpers, monero
from apps.monero.xmr.addresses import encode_addr
from apps.monero.xmr.credentials import AccountCreds
from apps.monero.xmr.networks import NetworkTypes, net_version
from apps.monero.xmr.networks import net_version
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestMoneroCrypto(unittest.TestCase):
def test_encoding(self):
point = unhexlify(
b"2486224797d05cae3cba4be043be2db0df381f3f19cfa113f86ab38e3d8d2bd0"
)
self.assertEqual(point, crypto.encodepoint(crypto.decodepoint(point)))
self.assertEqual(point, crypto_helpers.encodepoint(crypto_helpers.decodepoint(point)))
self.assertTrue(
crypto.point_eq(
crypto.decodepoint(point),
crypto.decodepoint(crypto.encodepoint(crypto.decodepoint(point))),
crypto_helpers.decodepoint(point),
crypto_helpers.decodepoint(crypto_helpers.encodepoint(crypto_helpers.decodepoint(point))),
)
)
def test_scalarmult_base(self):
scalar = crypto.decodeint(
scalar = crypto_helpers.decodeint(
unhexlify(
b"a0eea49140a3b036da30eacf64bd9d56ce3ef68ba82ef13571ec511edbcf8303"
)
@ -32,11 +32,11 @@ class TestMoneroCrypto(unittest.TestCase):
b"16bb4a3c44e2ced511fc0d4cd86b13b3af21efc99fb0356199fac489f2544c09"
)
res = crypto.scalarmult_base(scalar)
self.assertEqual(exp, crypto.encodepoint(res))
self.assertTrue(crypto.point_eq(crypto.decodepoint(exp), res))
res = crypto.scalarmult_base_into(None, scalar)
self.assertEqual(exp, crypto_helpers.encodepoint(res))
self.assertTrue(crypto.point_eq(crypto_helpers.decodepoint(exp), res))
scalar = crypto.decodeint(
scalar = crypto_helpers.decodeint(
unhexlify(
b"fd290dce39f781aebbdbd24584ed6d48bd300de19d9c3decfda0a6e2c6751d0f"
)
@ -45,9 +45,9 @@ class TestMoneroCrypto(unittest.TestCase):
b"123daf90fc26f13c6529e6b49bfed498995ac383ef19c0db6771143f24ba8dd5"
)
res = crypto.scalarmult_base(scalar)
self.assertEqual(exp, crypto.encodepoint(res))
self.assertTrue(crypto.point_eq(crypto.decodepoint(exp), res))
res = crypto.scalarmult_base_into(None, scalar)
self.assertEqual(exp, crypto_helpers.encodepoint(res))
self.assertTrue(crypto.point_eq(crypto_helpers.decodepoint(exp), res))
def test_scalarmult(self):
priv = unhexlify(
@ -60,15 +60,15 @@ class TestMoneroCrypto(unittest.TestCase):
b"adcd1f5881f46f254900a03c654e71950a88a0236fa0a3a946c9b8daed6ef43d"
)
res = crypto.scalarmult(crypto.decodepoint(pub), crypto.decodeint(priv))
self.assertEqual(exp, crypto.encodepoint(res))
self.assertTrue(crypto.point_eq(crypto.decodepoint(exp), res))
res = crypto.scalarmult_into(None, crypto_helpers.decodepoint(pub), crypto_helpers.decodeint(priv))
self.assertEqual(exp, crypto_helpers.encodepoint(res))
self.assertTrue(crypto.point_eq(crypto_helpers.decodepoint(exp), res))
def test_cn_fast_hash(self):
inp = unhexlify(
b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff6405"
)
res = crypto.cn_fast_hash(inp)
res = crypto.fast_hash_into(None, inp)
self.assertEqual(
res,
unhexlify(
@ -81,8 +81,8 @@ class TestMoneroCrypto(unittest.TestCase):
b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff6405"
)
res = crypto.hash_to_scalar(inp)
exp = crypto.decodeint(
res = crypto.hash_to_scalar_into(None, inp)
exp = crypto_helpers.decodeint(
unhexlify(
b"9907925b254e12162609fc0dfd0fef2aa4d605b0d10e6507cac253dd31a3ec06"
)
@ -93,8 +93,8 @@ class TestMoneroCrypto(unittest.TestCase):
data = unhexlify(
b"42f6835bf83114a1f5f6076fe79bdfa0bd67c74b88f127d54572d3910dd09201"
)
res = crypto.hash_to_point(data)
res_p = crypto.encodepoint(res)
res = crypto.hash_to_point_into(None, data)
res_p = crypto_helpers.encodepoint(res)
self.assertEqual(
res_p,
unhexlify(
@ -110,16 +110,16 @@ class TestMoneroCrypto(unittest.TestCase):
b"25d08763414c379aa9cf989cdcb3cadd36bd5193b500107d6bf5f921f18e470e"
)
sc_int = crypto.derivation_to_scalar(crypto.decodepoint(derivation), 0)
self.assertEqual(scalar, crypto.encodeint(sc_int))
sc_int = crypto_helpers.derivation_to_scalar(crypto_helpers.decodepoint(derivation), 0)
self.assertEqual(scalar, crypto_helpers.encodeint(sc_int))
def test_generate_key_derivation(self):
key_pub = crypto.decodepoint(
key_pub = crypto_helpers.decodepoint(
unhexlify(
b"7739c95d3298e2f87362dba9e0e0b3980a692ae8e2f16796b0e382098cd6bd83"
)
)
key_priv = crypto.decodeint(
key_priv = crypto_helpers.decodeint(
unhexlify(
b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a"
)
@ -130,18 +130,18 @@ class TestMoneroCrypto(unittest.TestCase):
self.assertEqual(
deriv_exp,
crypto.encodepoint(crypto.generate_key_derivation(key_pub, key_priv)),
crypto_helpers.encodepoint(crypto_helpers.generate_key_derivation(key_pub, key_priv)),
)
def test_h(self):
H = unhexlify(
b"8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"
)
self.assertEqual(crypto.encodepoint(crypto.xmr_H()), H)
self.assertEqual(crypto_helpers.encodepoint(crypto.xmr_H()), H)
def test_sc_inversion(self):
res = crypto.new_scalar()
inp = crypto.decodeint(
res = crypto.Scalar()
inp = crypto_helpers.decodeint(
unhexlify(
b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a"
)
@ -149,7 +149,7 @@ class TestMoneroCrypto(unittest.TestCase):
crypto.sc_inv_into(res, inp)
self.assertEqual(
hexlify(crypto.encodeint(res)),
hexlify(crypto_helpers.encodeint(res)),
b"bcf365a551e6358f3f281a6241d4a25eded60230b60a1d48c67b51a85e33d70e",
)
@ -166,39 +166,39 @@ class TestMoneroCrypto(unittest.TestCase):
self.assertEqual(
addr,
b"43tpGG9PKbwCpjRvNLn1jwXPpnacw2uVUcszAtgmDiVcZK4VgHwjJT9BJz1WGF9eMxSYASp8yNMkuLjeQfWqJn3CNWdWfzV",
"43tpGG9PKbwCpjRvNLn1jwXPpnacw2uVUcszAtgmDiVcZK4VgHwjJT9BJz1WGF9eMxSYASp8yNMkuLjeQfWqJn3CNWdWfzV",
)
w = AccountCreds.new_wallet(
crypto.decodeint(
crypto_helpers.decodeint(
unhexlify(
b"4ce88c168e0f5f8d6524f712d5f8d7d83233b1e7a2a60b5aba5206cc0ea2bc08"
)
),
crypto.decodeint(
crypto_helpers.decodeint(
unhexlify(
b"f2644a3dd97d43e87887e74d1691d52baa0614206ad1b0c239ff4aa3b501750a"
)
),
network_type=NetworkTypes.TESTNET,
network_type=MoneroNetworkType.TESTNET,
)
self.assertEqual(
w.address,
b"9vacMKaj8JJV6MnwDzh2oNVdwTLJfTDyNRiB6NzV9TT7fqvzLivH2dB8Tv7VYR3ncn8vCb3KdNMJzQWrPAF1otYJ9cPKpkr",
"9vacMKaj8JJV6MnwDzh2oNVdwTLJfTDyNRiB6NzV9TT7fqvzLivH2dB8Tv7VYR3ncn8vCb3KdNMJzQWrPAF1otYJ9cPKpkr",
)
def test_derive_subaddress_public_key(self):
out_key = crypto.decodepoint(
out_key = crypto_helpers.decodepoint(
unhexlify(
b"f4efc29da4ccd6bc6e81f52a6f47b2952966442a7efb49901cce06a7a3bef3e5"
)
)
deriv = crypto.decodepoint(
deriv = crypto_helpers.decodepoint(
unhexlify(
b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff64"
)
)
res = crypto.encodepoint(monero.derive_subaddress_public_key(out_key, deriv, 5))
res = crypto_helpers.encodepoint(monero.derive_subaddress_public_key(out_key, deriv, 5))
self.assertEqual(
res,
unhexlify(
@ -207,14 +207,14 @@ class TestMoneroCrypto(unittest.TestCase):
)
def test_get_subaddress_secret_key(self):
a = crypto.decodeint(
a = crypto_helpers.decodeint(
unhexlify(
b"4ce88c168e0f5f8d6524f712d5f8d7d83233b1e7a2a60b5aba5206cc0ea2bc08"
)
)
m = monero.get_subaddress_secret_key(secret_key=a, major=0, minor=1)
self.assertEqual(
crypto.encodeint(m),
crypto_helpers.encodeint(m),
unhexlify(
b"b6ff4d689b95e3310efbf683850c075bcde46361923054e42ef30016b287ff0c"
),
@ -231,10 +231,10 @@ class TestMoneroCrypto(unittest.TestCase):
b"0846cae7405077b6b7800f0b932c10a186448370b6db318f8c9e13f781dab546"
)
pkey_comp = crypto.derive_public_key(
crypto.decodepoint(derivation), 0, crypto.decodepoint(base)
pkey_comp = crypto_helpers.derive_public_key(
crypto_helpers.decodepoint(derivation), 0, crypto_helpers.decodepoint(base)
)
self.assertEqual(pkey_ex, crypto.encodepoint(pkey_comp))
self.assertEqual(pkey_ex, crypto_helpers.encodepoint(pkey_comp))
if __name__ == "__main__":

@ -11,33 +11,53 @@ if not utils.BITCOIN_ONLY:
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestMoneroProto(unittest.TestCase):
def test_sign_keys(self):
mst = ubinascii.unhexlify(b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c")
self.assertEqual(offloading_keys.key_signature(mst, 0, True)[:12], ubinascii.unhexlify(b'bb665d97ac7c77995578e352'))
self.assertEqual(offloading_keys.key_signature(mst, 0, False), ubinascii.unhexlify(b'87bb70af81bb7325f73e8b962167579454d126ff8ee51472922d7c103fc60f5f'))
self.assertEqual(offloading_keys.key_signature(mst, 3, True)[:12], ubinascii.unhexlify(b'b2ef8e4e4eec72ce3096622a'))
self.assertEqual(offloading_keys.key_signature(mst, 3, False), ubinascii.unhexlify(b'e4331602a83a68c892a83693a1b961564048d9349111b85b8b4b52a1adcf36da'))
mst = ubinascii.unhexlify(
b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c"
)
self.assertEqual(
offloading_keys.key_signature(mst, 0, True)[:12],
ubinascii.unhexlify(b"bb665d97ac7c77995578e352"),
)
self.assertEqual(
offloading_keys.key_signature(mst, 0, False),
ubinascii.unhexlify(
b"87bb70af81bb7325f73e8b962167579454d126ff8ee51472922d7c103fc60f5f"
),
)
self.assertEqual(
offloading_keys.key_signature(mst, 3, True)[:12],
ubinascii.unhexlify(b"b2ef8e4e4eec72ce3096622a"),
)
self.assertEqual(
offloading_keys.key_signature(mst, 3, False),
ubinascii.unhexlify(
b"e4331602a83a68c892a83693a1b961564048d9349111b85b8b4b52a1adcf36da"
),
)
def test_sig_seal(self):
mst = ubinascii.unhexlify(b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c")
mst = ubinascii.unhexlify(
b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c"
)
st = State(None)
st.last_step = st.STEP_SIGN
st.opening_key = mst
st.current_input_index = 3
mg_buff = [
'0b',
'02fe9ee789007254215b41351109f186620624a3c1ad2ba89628194528672adf04f900ebf9ad3b0cc1ac9ae1f03167f74d6e04175df5001c91d09d29dbefd6bc0b',
'021d46f6db8a349caca48a4dfee155b9dee927d0f25cdf5bcd724358c611b47906de6cedad47fd26070927f3954bcaf7a0e126699bf961ca4e8124abefe8aaeb05',
'02ae933994effe2b348b09bfab783bf9adb58b09659d8f5bd058cca252d763b600541807dcb0ea9fe253e59f23ce36cc811d627acae5e2abdc00b7ed155f3e6b0f',
'0203dd7138c7378444fe3c1b1572a351f88505aeab2d9f8ed4a8f67d66e76983072d8ae6e496b3953a8603543c2dc64749ee15fe3575e4505b502bfe696f06690e',
'0287b572b6c096bc11a8c10fe1fc4ba2085633f8e1bdd2e39df8f46c9bf733ca068261d8006f22ee2bfaf4366e26d42b00befdddd9058a5c87a0f39c757f121909',
'021e2ea38aa07601e07a3d7623a97e68d3251525304d2a748548c7b46d07c20b0c78506b19cae49d569d0a8c4979c74f7d8d19f7e595d307ddf00faf3d8f621c0d',
'0214f758c8fb4a521a1e3d25b9fb535974f6aab1c1dda5988e986dda7e17140909a7b7bdb3d5e17a2ebd5deb3530d10c6f5d6966f525c1cbca408059949ff65304',
'02f707c4a37066a692986ddfdd2ca71f68c6f45a956d45eaf6e8e7a2e5272ac3033eb26ca2b55bf86e90ab8ddcdbad88a82ded88deb552614190440169afcee004',
'02edb8a5b8cc02a2e03b95ea068084ae2496f21d4dfd0842c63836137e37047b06d5a0160994396c98630d8b47878e9c18fea4fb824588c143e05c4b18bfea2301',
'02aa59c2ef76ac97c261279a1c6ed3724d66a437fe8df0b85e8858703947a2b10f04e49912a0626c09849c3b4a3ea46166cd909b9fd561257730c91cbccf4abe07',
'02c64a98c59c4a3d7c583de65404c5a54b350a25011dfca70cd84e3f6e570428026236028fce31bfd8d9fc5401867ab5349eb0859c65df05b380899a7bdfee9003',
'03da465e27f7feec31353cb668f0e8965391f983b06c0684b35b00af38533603',
"0b",
"02fe9ee789007254215b41351109f186620624a3c1ad2ba89628194528672adf04f900ebf9ad3b0cc1ac9ae1f03167f74d6e04175df5001c91d09d29dbefd6bc0b",
"021d46f6db8a349caca48a4dfee155b9dee927d0f25cdf5bcd724358c611b47906de6cedad47fd26070927f3954bcaf7a0e126699bf961ca4e8124abefe8aaeb05",
"02ae933994effe2b348b09bfab783bf9adb58b09659d8f5bd058cca252d763b600541807dcb0ea9fe253e59f23ce36cc811d627acae5e2abdc00b7ed155f3e6b0f",
"0203dd7138c7378444fe3c1b1572a351f88505aeab2d9f8ed4a8f67d66e76983072d8ae6e496b3953a8603543c2dc64749ee15fe3575e4505b502bfe696f06690e",
"0287b572b6c096bc11a8c10fe1fc4ba2085633f8e1bdd2e39df8f46c9bf733ca068261d8006f22ee2bfaf4366e26d42b00befdddd9058a5c87a0f39c757f121909",
"021e2ea38aa07601e07a3d7623a97e68d3251525304d2a748548c7b46d07c20b0c78506b19cae49d569d0a8c4979c74f7d8d19f7e595d307ddf00faf3d8f621c0d",
"0214f758c8fb4a521a1e3d25b9fb535974f6aab1c1dda5988e986dda7e17140909a7b7bdb3d5e17a2ebd5deb3530d10c6f5d6966f525c1cbca408059949ff65304",
"02f707c4a37066a692986ddfdd2ca71f68c6f45a956d45eaf6e8e7a2e5272ac3033eb26ca2b55bf86e90ab8ddcdbad88a82ded88deb552614190440169afcee004",
"02edb8a5b8cc02a2e03b95ea068084ae2496f21d4dfd0842c63836137e37047b06d5a0160994396c98630d8b47878e9c18fea4fb824588c143e05c4b18bfea2301",
"02aa59c2ef76ac97c261279a1c6ed3724d66a437fe8df0b85e8858703947a2b10f04e49912a0626c09849c3b4a3ea46166cd909b9fd561257730c91cbccf4abe07",
"02c64a98c59c4a3d7c583de65404c5a54b350a25011dfca70cd84e3f6e570428026236028fce31bfd8d9fc5401867ab5349eb0859c65df05b380899a7bdfee9003",
"03da465e27f7feec31353cb668f0e8965391f983b06c0684b35b00af38533603",
]
mg_buff = [ubinascii.unhexlify(x) for x in mg_buff]

@ -10,9 +10,7 @@ if not utils.BITCOIN_ONLY:
)
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter
from apps.monero.xmr.serialize_messages.base import ECPoint
from apps.monero.xmr.serialize_messages.tx_prefix import (
TxinToKey,
)
from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
@ -66,7 +64,8 @@ class TestMoneroSerializer(unittest.TestCase):
test_deser = TxinToKey.load(MemoryReaderWriter(writer.get_buffer()))
self.assertEqual(msg.amount, test_deser.amount)
self.assertEqual(msg, test_deser)
self.assertEqual(msg.k_image, test_deser.k_image)
self.assertEqual(msg.key_offsets, test_deser.key_offsets)
if __name__ == "__main__":

@ -491,6 +491,13 @@ class EthereumDataType(IntEnum):
STRUCT = 8
class MoneroNetworkType(IntEnum):
MAINNET = 0
TESTNET = 1
STAGENET = 2
FAKECHAIN = 3
class NEMMosaicLevy(IntEnum):
MosaicLevy_Absolute = 1
MosaicLevy_Percentile = 2
@ -5148,7 +5155,7 @@ class MoneroGetAddress(protobuf.MessageType):
FIELDS = {
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
2: protobuf.Field("show_display", "bool", repeated=False, required=False),
3: protobuf.Field("network_type", "uint32", repeated=False, required=False),
3: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
4: protobuf.Field("account", "uint32", repeated=False, required=False),
5: protobuf.Field("minor", "uint32", repeated=False, required=False),
6: protobuf.Field("payment_id", "bytes", repeated=False, required=False),
@ -5159,7 +5166,7 @@ class MoneroGetAddress(protobuf.MessageType):
*,
address_n: Optional[Sequence["int"]] = None,
show_display: Optional["bool"] = None,
network_type: Optional["int"] = None,
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
account: Optional["int"] = None,
minor: Optional["int"] = None,
payment_id: Optional["bytes"] = None,
@ -5190,14 +5197,14 @@ class MoneroGetWatchKey(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 542
FIELDS = {
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
2: protobuf.Field("network_type", "uint32", repeated=False, required=False),
2: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
}
def __init__(
self,
*,
address_n: Optional[Sequence["int"]] = None,
network_type: Optional["int"] = None,
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.network_type = network_type
@ -5225,7 +5232,7 @@ class MoneroTransactionInitRequest(protobuf.MessageType):
FIELDS = {
1: protobuf.Field("version", "uint32", repeated=False, required=False),
2: protobuf.Field("address_n", "uint32", repeated=True, required=False),
3: protobuf.Field("network_type", "uint32", repeated=False, required=False),
3: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
4: protobuf.Field("tsx_data", "MoneroTransactionData", repeated=False, required=False),
}
@ -5234,7 +5241,7 @@ class MoneroTransactionInitRequest(protobuf.MessageType):
*,
address_n: Optional[Sequence["int"]] = None,
version: Optional["int"] = None,
network_type: Optional["int"] = None,
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
tsx_data: Optional["MoneroTransactionData"] = None,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
@ -5543,21 +5550,21 @@ class MoneroTransactionFinalAck(protobuf.MessageType):
class MoneroKeyImageExportInitRequest(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 530
FIELDS = {
1: protobuf.Field("num", "uint64", repeated=False, required=False),
2: protobuf.Field("hash", "bytes", repeated=False, required=False),
1: protobuf.Field("num", "uint64", repeated=False, required=True),
2: protobuf.Field("hash", "bytes", repeated=False, required=True),
3: protobuf.Field("address_n", "uint32", repeated=True, required=False),
4: protobuf.Field("network_type", "uint32", repeated=False, required=False),
4: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
5: protobuf.Field("subs", "MoneroSubAddressIndicesList", repeated=True, required=False),
}
def __init__(
self,
*,
num: "int",
hash: "bytes",
address_n: Optional[Sequence["int"]] = None,
subs: Optional[Sequence["MoneroSubAddressIndicesList"]] = None,
num: Optional["int"] = None,
hash: Optional["bytes"] = None,
network_type: Optional["int"] = None,
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.subs: Sequence["MoneroSubAddressIndicesList"] = subs if subs is not None else []
@ -5620,11 +5627,11 @@ class MoneroGetTxKeyRequest(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 550
FIELDS = {
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
2: protobuf.Field("network_type", "uint32", repeated=False, required=False),
3: protobuf.Field("salt1", "bytes", repeated=False, required=False),
4: protobuf.Field("salt2", "bytes", repeated=False, required=False),
5: protobuf.Field("tx_enc_keys", "bytes", repeated=False, required=False),
6: protobuf.Field("tx_prefix_hash", "bytes", repeated=False, required=False),
2: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
3: protobuf.Field("salt1", "bytes", repeated=False, required=True),
4: protobuf.Field("salt2", "bytes", repeated=False, required=True),
5: protobuf.Field("tx_enc_keys", "bytes", repeated=False, required=True),
6: protobuf.Field("tx_prefix_hash", "bytes", repeated=False, required=True),
7: protobuf.Field("reason", "uint32", repeated=False, required=False),
8: protobuf.Field("view_public_key", "bytes", repeated=False, required=False),
}
@ -5632,21 +5639,21 @@ class MoneroGetTxKeyRequest(protobuf.MessageType):
def __init__(
self,
*,
salt1: "bytes",
salt2: "bytes",
tx_enc_keys: "bytes",
tx_prefix_hash: "bytes",
address_n: Optional[Sequence["int"]] = None,
network_type: Optional["int"] = None,
salt1: Optional["bytes"] = None,
salt2: Optional["bytes"] = None,
tx_enc_keys: Optional["bytes"] = None,
tx_prefix_hash: Optional["bytes"] = None,
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
reason: Optional["int"] = None,
view_public_key: Optional["bytes"] = None,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.network_type = network_type
self.salt1 = salt1
self.salt2 = salt2
self.tx_enc_keys = tx_enc_keys
self.tx_prefix_hash = tx_prefix_hash
self.network_type = network_type
self.reason = reason
self.view_public_key = view_public_key
@ -5675,14 +5682,14 @@ class MoneroLiveRefreshStartRequest(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 552
FIELDS = {
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
2: protobuf.Field("network_type", "uint32", repeated=False, required=False),
2: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
}
def __init__(
self,
*,
address_n: Optional[Sequence["int"]] = None,
network_type: Optional["int"] = None,
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.network_type = network_type
@ -5695,21 +5702,21 @@ class MoneroLiveRefreshStartAck(protobuf.MessageType):
class MoneroLiveRefreshStepRequest(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 554
FIELDS = {
1: protobuf.Field("out_key", "bytes", repeated=False, required=False),
2: protobuf.Field("recv_deriv", "bytes", repeated=False, required=False),
3: protobuf.Field("real_out_idx", "uint64", repeated=False, required=False),
4: protobuf.Field("sub_addr_major", "uint32", repeated=False, required=False),
5: protobuf.Field("sub_addr_minor", "uint32", repeated=False, required=False),
1: protobuf.Field("out_key", "bytes", repeated=False, required=True),
2: protobuf.Field("recv_deriv", "bytes", repeated=False, required=True),
3: protobuf.Field("real_out_idx", "uint64", repeated=False, required=True),
4: protobuf.Field("sub_addr_major", "uint32", repeated=False, required=True),
5: protobuf.Field("sub_addr_minor", "uint32", repeated=False, required=True),
}
def __init__(
self,
*,
out_key: Optional["bytes"] = None,
recv_deriv: Optional["bytes"] = None,
real_out_idx: Optional["int"] = None,
sub_addr_major: Optional["int"] = None,
sub_addr_minor: Optional["int"] = None,
out_key: "bytes",
recv_deriv: "bytes",
real_out_idx: "int",
sub_addr_major: "int",
sub_addr_minor: "int",
) -> None:
self.out_key = out_key
self.recv_deriv = recv_deriv
@ -5844,15 +5851,15 @@ class MoneroMultisigKLRki(protobuf.MessageType):
class MoneroRctKeyPublic(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("dest", "bytes", repeated=False, required=False),
2: protobuf.Field("commitment", "bytes", repeated=False, required=False),
1: protobuf.Field("dest", "bytes", repeated=False, required=True),
2: protobuf.Field("commitment", "bytes", repeated=False, required=True),
}
def __init__(
self,
*,
dest: Optional["bytes"] = None,
commitment: Optional["bytes"] = None,
dest: "bytes",
commitment: "bytes",
) -> None:
self.dest = dest
self.commitment = commitment
@ -5954,15 +5961,15 @@ class MoneroRingCtSig(protobuf.MessageType):
class MoneroSubAddressIndicesList(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("account", "uint32", repeated=False, required=False),
1: protobuf.Field("account", "uint32", repeated=False, required=True),
2: protobuf.Field("minor_indices", "uint32", repeated=True, required=False),
}
def __init__(
self,
*,
account: "int",
minor_indices: Optional[Sequence["int"]] = None,
account: Optional["int"] = None,
) -> None:
self.minor_indices: Sequence["int"] = minor_indices if minor_indices is not None else []
self.account = account
@ -5971,10 +5978,10 @@ class MoneroSubAddressIndicesList(protobuf.MessageType):
class MoneroTransferDetails(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("out_key", "bytes", repeated=False, required=False),
2: protobuf.Field("tx_pub_key", "bytes", repeated=False, required=False),
1: protobuf.Field("out_key", "bytes", repeated=False, required=True),
2: protobuf.Field("tx_pub_key", "bytes", repeated=False, required=True),
3: protobuf.Field("additional_tx_pub_keys", "bytes", repeated=True, required=False),
4: protobuf.Field("internal_output_index", "uint64", repeated=False, required=False),
4: protobuf.Field("internal_output_index", "uint64", repeated=False, required=True),
5: protobuf.Field("sub_addr_major", "uint32", repeated=False, required=False),
6: protobuf.Field("sub_addr_minor", "uint32", repeated=False, required=False),
}
@ -5982,10 +5989,10 @@ class MoneroTransferDetails(protobuf.MessageType):
def __init__(
self,
*,
out_key: "bytes",
tx_pub_key: "bytes",
internal_output_index: "int",
additional_tx_pub_keys: Optional[Sequence["bytes"]] = None,
out_key: Optional["bytes"] = None,
tx_pub_key: Optional["bytes"] = None,
internal_output_index: Optional["int"] = None,
sub_addr_major: Optional["int"] = None,
sub_addr_minor: Optional["int"] = None,
) -> None:

Loading…
Cancel
Save