From 33c174491f45d11bbc9587e953f09b95ef834657 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 4 Apr 2022 13:39:02 +0200 Subject: [PATCH] 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 Co-authored-by: Martin Milata Co-authored-by: matejcik --- common/protob/messages-monero.proto | 53 +- core/.changelog.d/642.changed | 1 + core/.changelog.d/642.removed | 1 + .../modtrezorcrypto/modtrezorcrypto-monero.h | 1060 +++++++---------- core/mocks/generated/trezorcrypto/monero.pyi | 210 ++-- core/mocks/micropython.pyi | 3 +- core/src/all_modules.py | 20 +- core/src/apps/monero/diag.py | 16 +- core/src/apps/monero/get_address.py | 46 +- core/src/apps/monero/get_tx_keys.py | 25 +- core/src/apps/monero/get_watch_only.py | 20 +- core/src/apps/monero/key_image_sync.py | 47 +- core/src/apps/monero/layout.py | 67 +- core/src/apps/monero/live_refresh.py | 47 +- core/src/apps/monero/misc.py | 41 +- core/src/apps/monero/sign_tx.py | 19 +- core/src/apps/monero/signing/__init__.py | 3 +- .../apps/monero/signing/offloading_keys.py | 44 +- core/src/apps/monero/signing/state.py | 61 +- .../signing/step_01_init_transaction.py | 72 +- .../apps/monero/signing/step_02_set_input.py | 33 +- .../signing/step_03_inputs_permutation.py | 57 - .../apps/monero/signing/step_04_input_vini.py | 8 +- .../monero/signing/step_05_all_inputs_set.py | 2 +- .../apps/monero/signing/step_06_set_output.py | 67 +- .../monero/signing/step_07_all_outputs_set.py | 7 +- .../apps/monero/signing/step_09_sign_input.py | 86 +- .../apps/monero/signing/step_10_sign_final.py | 19 +- core/src/apps/monero/xmr/__init__.py | 1 + core/src/apps/monero/xmr/addresses.py | 22 +- core/src/apps/monero/xmr/bulletproof.py | 412 ++++--- .../monero/xmr/{crypto => }/chacha_poly.py | 14 +- core/src/apps/monero/xmr/clsag.py | 243 ++++ core/src/apps/monero/xmr/credentials.py | 43 +- core/src/apps/monero/xmr/crypto/__init__.py | 325 ----- core/src/apps/monero/xmr/crypto_helpers.py | 162 +++ core/src/apps/monero/xmr/keccak_hasher.py | 21 +- core/src/apps/monero/xmr/key_image.py | 73 +- core/src/apps/monero/xmr/mlsag.py | 493 -------- core/src/apps/monero/xmr/mlsag_hasher.py | 35 +- core/src/apps/monero/xmr/monero.py | 128 +- core/src/apps/monero/xmr/networks.py | 18 +- core/src/apps/monero/xmr/range_signatures.py | 17 +- .../src/apps/monero/xmr/serialize/__init__.py | 18 +- .../apps/monero/xmr/serialize/base_types.py | 48 +- .../monero/xmr/serialize/int_serialize.py | 25 +- .../monero/xmr/serialize/message_types.py | 89 +- .../apps/monero/xmr/serialize/readwriter.py | 30 +- .../xmr/serialize_messages/tx_ct_key.py | 8 +- .../xmr/serialize_messages/tx_prefix.py | 2 +- .../serialize_messages/tx_rsig_bulletproof.py | 10 +- core/src/apps/monero/xmr/types.py | 7 - core/src/trezor/enums/MoneroNetworkType.py | 8 + core/src/trezor/enums/__init__.py | 6 + core/src/trezor/messages.py | 93 +- core/tests/test_apps.monero.bulletproof.py | 32 +- core/tests/test_apps.monero.clsag.py | 308 +++-- core/tests/test_apps.monero.crypto.py | 88 +- core/tests/test_apps.monero.proto.py | 58 +- core/tests/test_apps.monero.serializer.py | 7 +- python/src/trezorlib/messages.py | 101 +- 61 files changed, 2361 insertions(+), 2719 deletions(-) create mode 100644 core/.changelog.d/642.changed create mode 100644 core/.changelog.d/642.removed delete mode 100644 core/src/apps/monero/signing/step_03_inputs_permutation.py create mode 100644 core/src/apps/monero/xmr/__init__.py rename core/src/apps/monero/xmr/{crypto => }/chacha_poly.py (71%) create mode 100644 core/src/apps/monero/xmr/clsag.py delete mode 100644 core/src/apps/monero/xmr/crypto/__init__.py create mode 100644 core/src/apps/monero/xmr/crypto_helpers.py delete mode 100644 core/src/apps/monero/xmr/mlsag.py delete mode 100644 core/src/apps/monero/xmr/types.py create mode 100644 core/src/trezor/enums/MoneroNetworkType.py diff --git a/common/protob/messages-monero.proto b/common/protob/messages-monero.proto index 1bcda2d559d..dd40ea05c2d 100644 --- a/common/protob/messages-monero.proto +++ b/common/protob/messages-monero.proto @@ -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; } /** diff --git a/core/.changelog.d/642.changed b/core/.changelog.d/642.changed new file mode 100644 index 00000000000..c19556c2944 --- /dev/null +++ b/core/.changelog.d/642.changed @@ -0,0 +1 @@ +Refactor and cleanup of Monero code. diff --git a/core/.changelog.d/642.removed b/core/.changelog.d/642.removed new file mode 100644 index 00000000000..66b75e098c4 --- /dev/null +++ b/core/.changelog.d/642.removed @@ -0,0 +1 @@ +Removed support for obsolete Monero hardfork 12 and below diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-monero.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-monero.h index dc9194d6c3b..86aae6823a1 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-monero.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-monero.h @@ -136,12 +136,12 @@ STATIC void mp_unpack_scalar(bignum256modm r, const mp_obj_t arg, // Constructors // -/// 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 /// """ @@ -174,12 +174,12 @@ STATIC mp_obj_t mod_trezorcrypto_monero_ge25519___del__(mp_obj_t self) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_ge25519___del___obj, mod_trezorcrypto_monero_ge25519___del__); -/// 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 /// """ @@ -274,210 +274,174 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_hasher___del___obj, /// mock:global -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_init256_modm(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_sc_copy(const mp_obj_t dest, + const mp_obj_t src) { + mp_obj_t res = mp_obj_new_scalar_r(dest); - if (n_args == 0 || args[0] == mp_const_none) { - set256_modm(MP_OBJ_SCALAR(res), 0); - } else if (n_args > 0 && MP_OBJ_IS_SCALAR(args[1 + off])) { - copy256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1 + off])); - } else if (n_args > 0 && MP_OBJ_IS_STR_OR_BYTES(args[1 + off])) { - mp_unpack_scalar(MP_OBJ_SCALAR(res), args[1 + off], 0); - } else if (n_args > 0 && mp_obj_is_integer(args[1 + off])) { - uint64_t v = trezor_obj_get_uint64(args[1 + off]); + if (MP_OBJ_IS_SCALAR(src)) { + copy256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(src)); + } else if (MP_OBJ_IS_STR_OR_BYTES(src)) { + mp_unpack_scalar(MP_OBJ_SCALAR(res), src, 0); + } else if (mp_obj_is_integer(src)) { + uint64_t v = trezor_obj_get_uint64(src); set256_modm(MP_OBJ_SCALAR(res), v); } else { mp_raise_ValueError("Invalid scalar def"); } return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_init256_modm_obj, 0, 2, - mod_trezorcrypto_monero_init256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_sc_copy_obj, + mod_trezorcrypto_monero_sc_copy); -/// def check256_modm(val: Sc25519) -> None: +/// def sc_check(val: Scalar) -> None: /// """ /// Throws exception if scalar is invalid /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_check256_modm(const mp_obj_t arg) { +STATIC mp_obj_t mod_trezorcrypto_monero_sc_check(const mp_obj_t arg) { assert_scalar(arg); if (check256_modm(MP_OBJ_C_SCALAR(arg)) != 1) { mp_raise_ValueError("Ed25519 scalar invalid"); } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_check256_modm_obj, - mod_trezorcrypto_monero_check256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_sc_check_obj, + mod_trezorcrypto_monero_sc_check); -/// def iszero256_modm(val: Sc25519) -> bool: +/// def sc_iszero(val: Scalar) -> bool: /// """ /// Returns False if the scalar is zero /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_iszero256_modm(const mp_obj_t arg) { +STATIC mp_obj_t mod_trezorcrypto_monero_sc_iszero(const mp_obj_t arg) { assert_scalar(arg); const int r = iszero256_modm(MP_OBJ_C_SCALAR(arg)); return mp_obj_new_int(r); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_iszero256_modm_obj, - mod_trezorcrypto_monero_iszero256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_sc_iszero_obj, + mod_trezorcrypto_monero_sc_iszero); -/// def eq256_modm(a: Sc25519, b: Sc25519) -> int: +/// def sc_eq(a: Scalar, b: Scalar) -> int: /// """ /// Compares scalars, returns 1 on the same value /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_eq256_modm(const mp_obj_t a, - const mp_obj_t b) { +STATIC mp_obj_t mod_trezorcrypto_monero_sc_eq(const mp_obj_t a, + const mp_obj_t b) { assert_scalar(a); assert_scalar(b); int r = eq256_modm(MP_OBJ_C_SCALAR(a), MP_OBJ_C_SCALAR(b)); return MP_OBJ_NEW_SMALL_INT(r); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_eq256_modm_obj, - mod_trezorcrypto_monero_eq256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_sc_eq_obj, + mod_trezorcrypto_monero_sc_eq); -/// def get256_modm(a: Sc25519) -> int: -/// """ -/// Extracts 64bit integer from the scalar. Raises exception if scalar is -/// bigger than 2^64 -/// """ -STATIC mp_obj_t mod_trezorcrypto_monero_get256_modm(const mp_obj_t arg) { - assert_scalar(arg); - uint64_t v = 0; - if (!get256_modm(&v, MP_OBJ_C_SCALAR(arg))) { - mp_raise_ValueError("Ed25519 scalar too big"); - } - return mp_obj_new_int_from_ull(v); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_get256_modm_obj, - mod_trezorcrypto_monero_get256_modm); - -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_add256_modm(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_sc_add_into(const mp_obj_t dest, + const mp_obj_t a, + const mp_obj_t b) { + mp_obj_t res = mp_obj_new_scalar_r(dest); - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - add256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off])); + assert_scalar(a); + assert_scalar(b); + add256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(a), MP_OBJ_C_SCALAR(b)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_add256_modm_obj, 2, 3, - mod_trezorcrypto_monero_add256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_monero_sc_add_into_obj, + mod_trezorcrypto_monero_sc_add_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_sub256_modm(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_sc_sub_into(const mp_obj_t dest, + const mp_obj_t a, + const mp_obj_t b) { + mp_obj_t res = mp_obj_new_scalar_r(dest); - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - sub256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off])); + assert_scalar(a); + assert_scalar(b); + sub256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(a), MP_OBJ_C_SCALAR(b)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_sub256_modm_obj, 2, 3, - mod_trezorcrypto_monero_sub256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_monero_sc_sub_into_obj, + mod_trezorcrypto_monero_sc_sub_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_mul256_modm(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_sc_mul_into(const mp_obj_t dest, + const mp_obj_t a, + const mp_obj_t b) { + mp_obj_t res = mp_obj_new_scalar_r(dest); - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - mul256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off])); + assert_scalar(a); + assert_scalar(b); + mul256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(a), MP_OBJ_C_SCALAR(b)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_mul256_modm_obj, 2, 3, - mod_trezorcrypto_monero_mul256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_monero_sc_mul_into_obj, + mod_trezorcrypto_monero_sc_mul_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_mulsub256_modm(size_t n_args, +STATIC mp_obj_t mod_trezorcrypto_monero_sc_mulsub_into(size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); - - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - assert_scalar(args[3 + off]); - mulsub256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off]), - MP_OBJ_C_SCALAR(args[3 + off])); + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); + + assert_scalar(args[1]); + assert_scalar(args[2]); + assert_scalar(args[3]); + mulsub256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1]), + MP_OBJ_C_SCALAR(args[2]), MP_OBJ_C_SCALAR(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_mulsub256_modm_obj, 3, 4, - mod_trezorcrypto_monero_mulsub256_modm); + mod_trezorcrypto_monero_sc_mulsub_into_obj, 4, 4, + mod_trezorcrypto_monero_sc_mulsub_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_muladd256_modm(size_t n_args, +STATIC mp_obj_t mod_trezorcrypto_monero_sc_muladd_into(size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); - - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - assert_scalar(args[3 + off]); - muladd256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off]), - MP_OBJ_C_SCALAR(args[3 + off])); + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); + + assert_scalar(args[1]); + assert_scalar(args[2]); + assert_scalar(args[3]); + muladd256_modm(MP_OBJ_SCALAR(res), MP_OBJ_C_SCALAR(args[1]), + MP_OBJ_C_SCALAR(args[2]), MP_OBJ_C_SCALAR(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_muladd256_modm_obj, 3, 4, - mod_trezorcrypto_monero_muladd256_modm); + mod_trezorcrypto_monero_sc_muladd_into_obj, 4, 4, + mod_trezorcrypto_monero_sc_muladd_into); -/// def inv256_modm(r: Sc25519 | None, a: Sc25519) -> Sc25519: +/// def sc_inv_into(r: Scalar | None, a: Scalar) -> Scalar: /// """ /// Scalar modular inversion /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_inv256_modm(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[1 + off]); +STATIC mp_obj_t mod_trezorcrypto_monero_sc_inv_into(const mp_obj_t dest, + const mp_obj_t arg) { + mp_obj_t res = mp_obj_new_scalar_r(dest); + assert_scalar(arg); // bn_prime = curve order, little endian encoded bignum256 bn_prime = {.val = {0x1cf5d3ed, 0x9318d2, 0x1de73596, 0x1df3bd45, @@ -486,7 +450,7 @@ STATIC mp_obj_t mod_trezorcrypto_monero_inv256_modm(size_t n_args, bignum256modm bm_x = {0}; uint8_t raw_x[32] = {0}; - memcpy(bm_x, MP_OBJ_C_SCALAR(args[1 + off]), sizeof(bignum256modm)); + memcpy(bm_x, MP_OBJ_C_SCALAR(arg), sizeof(bignum256modm)); contract256_modm(raw_x, bm_x); bn_read_le(raw_x, &bn_x); @@ -498,23 +462,23 @@ STATIC mp_obj_t mod_trezorcrypto_monero_inv256_modm(size_t n_args, return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_inv256_modm_obj, 1, 2, - mod_trezorcrypto_monero_inv256_modm); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_sc_inv_into_obj, + mod_trezorcrypto_monero_sc_inv_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_pack256_modm(size_t n_args, - const mp_obj_t *args) { - if (n_args == 1 || args[0] == mp_const_none) { - assert_scalar(args[0]); +STATIC mp_obj_t mod_trezorcrypto_monero_encodeint_into(size_t n_args, + const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 2, 3, false); + if (args[0] == mp_const_none) { + assert_scalar(args[1]); vstr_t out = {0}; vstr_init_len(&out, 32); - contract256_modm((uint8_t *)out.buf, MP_OBJ_C_SCALAR(args[0])); + contract256_modm((uint8_t *)out.buf, MP_OBJ_C_SCALAR(args[1])); return mp_obj_new_str_from_vstr(&mp_type_bytes, &out); } else { mp_buffer_info_t bufm = {0}; @@ -529,43 +493,41 @@ STATIC mp_obj_t mod_trezorcrypto_monero_pack256_modm(size_t n_args, } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_pack256_modm_obj, 1, 3, - mod_trezorcrypto_monero_pack256_modm); + mod_trezorcrypto_monero_encodeint_into_obj, 2, 3, + mod_trezorcrypto_monero_encodeint_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_unpack256_modm(size_t n_args, +STATIC mp_obj_t mod_trezorcrypto_monero_decodeint_into(size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args >= 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); + mp_arg_check_num(n_args, 0, 2, 3, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); const mp_int_t offset = n_args >= 3 ? mp_obj_get_int(args[2]) : 0; - mp_unpack_scalar(MP_OBJ_SCALAR(res), args[1 + off], offset); + mp_unpack_scalar(MP_OBJ_SCALAR(res), args[1], offset); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_unpack256_modm_obj, 1, 3, - mod_trezorcrypto_monero_unpack256_modm); + mod_trezorcrypto_monero_decodeint_into_obj, 2, 3, + mod_trezorcrypto_monero_decodeint_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_unpack256_modm_noreduce( +STATIC mp_obj_t mod_trezorcrypto_monero_decodeint_into_noreduce( size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args >= 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); + mp_arg_check_num(n_args, 0, 2, 3, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); const mp_int_t offset = n_args >= 3 ? mp_obj_get_int(args[2]) : 0; mp_buffer_info_t buff = {0}; - mp_get_buffer_raise(args[1 + off], &buff, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &buff, MP_BUFFER_READ); if (buff.len != 32 + offset) { mp_raise_ValueError("Invalid length of secret key"); } @@ -574,42 +536,41 @@ STATIC mp_obj_t mod_trezorcrypto_monero_unpack256_modm_noreduce( return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_unpack256_modm_noreduce_obj, 1, 3, - mod_trezorcrypto_monero_unpack256_modm_noreduce); + mod_trezorcrypto_monero_decodeint_into_noreduce_obj, 2, 3, + mod_trezorcrypto_monero_decodeint_into_noreduce); // // GE25519 Defs // -/// def ge25519_set_neutral(r: Ge25519 | None) -> Ge25519: +/// def identity_into(r: Point | None = None) -> Point: /// """ /// Sets neutral point /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_set_neutral( - size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t mod_trezorcrypto_monero_identity_into(size_t n_args, + const mp_obj_t *args) { mp_obj_t res = mp_obj_new_ge25519_r(n_args == 1 ? args[0] : mp_const_none); ge25519_set_neutral(&MP_OBJ_GE25519(res)); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_set_neutral_obj, 0, 1, - mod_trezorcrypto_monero_ge25519_set_neutral); + mod_trezorcrypto_monero_identity_into_obj, 0, 1, + mod_trezorcrypto_monero_identity_into); -/// def ge25519_set_xmr_h(r: Ge25519 | None) -> Ge25519: +/// def xmr_H(r: Point | None = None) -> Point: /// """ /// Sets H point /// """ -STATIC mp_obj_t -mod_trezorcrypto_monero_ge25519_set_xmr_h(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t mod_trezorcrypto_monero_xmr_H(size_t n_args, + const mp_obj_t *args) { mp_obj_t res = mp_obj_new_ge25519_r(n_args == 1 ? args[0] : mp_const_none); ge25519_set_xmr_h(&MP_OBJ_GE25519(res)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_set_xmr_h_obj, 0, 1, - mod_trezorcrypto_monero_ge25519_set_xmr_h); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_monero_xmr_H_obj, 0, + 1, mod_trezorcrypto_monero_xmr_H); -/// def ge25519_check(r: Ge25519) -> None: +/// def ge25519_check(r: Point) -> None: /// """ /// Checks point, throws if not on curve /// """ @@ -623,167 +584,108 @@ STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_check(const mp_obj_t arg) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_monero_ge25519_check_obj, mod_trezorcrypto_monero_ge25519_check); -/// def ge25519_eq(a: Ge25519, b: Ge25519) -> bool: +/// def point_eq(a: Point, b: Point) -> bool: /// """ /// Compares EC points /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_eq(const mp_obj_t a, - const mp_obj_t b) { +STATIC mp_obj_t mod_trezorcrypto_monero_point_eq(const mp_obj_t a, + const mp_obj_t b) { assert_ge25519(a); assert_ge25519(b); int r = ge25519_eq(&MP_OBJ_C_GE25519(a), &MP_OBJ_C_GE25519(b)); return MP_OBJ_NEW_SMALL_INT(r); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_ge25519_eq_obj, - mod_trezorcrypto_monero_ge25519_eq); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_point_eq_obj, + mod_trezorcrypto_monero_point_eq); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_add(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_point_add_into(const mp_obj_t dest, + const mp_obj_t a, + const mp_obj_t b) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); - assert_ge25519(args[1 + off]); - assert_ge25519(args[2 + off]); - ge25519_add(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - &MP_OBJ_C_GE25519(args[2 + off]), 0); + assert_ge25519(a); + assert_ge25519(b); + ge25519_add(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(a), &MP_OBJ_C_GE25519(b), + 0); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_add_obj, 2, 3, - mod_trezorcrypto_monero_ge25519_add); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_monero_point_add_into_obj, + mod_trezorcrypto_monero_point_add_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_sub(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_point_sub_into(const mp_obj_t dest, + const mp_obj_t a, + const mp_obj_t b) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); - assert_ge25519(args[1 + off]); - assert_ge25519(args[2 + off]); - ge25519_add(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - &MP_OBJ_C_GE25519(args[2 + off]), 1); - return res; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_sub_obj, 2, 3, - mod_trezorcrypto_monero_ge25519_sub); - -/// def ge25519_double(r: Ge25519 | None, p: Ge25519) -> Ge25519: -/// """ -/// EC point doubling -/// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_double(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 2; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - mp_obj_t src = res_arg ? args[1] : args[0]; - assert_ge25519(src); - ge25519_double(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(src)); + assert_ge25519(a); + assert_ge25519(b); + ge25519_add(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(a), &MP_OBJ_C_GE25519(b), + 1); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_double_obj, 1, 2, - mod_trezorcrypto_monero_ge25519_double); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_monero_point_sub_into_obj, + mod_trezorcrypto_monero_point_sub_into); -/// def ge25519_mul8(r: Ge25519 | None, p: Ge25519) -> Ge25519: +/// def ge25519_mul8(r: Point | None, p: Point) -> Point: /// """ /// EC point * 8 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_mul8(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 2; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - mp_obj_t src = res_arg ? args[1] : args[0]; +STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_mul8(const mp_obj_t dest, + const mp_obj_t src) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); assert_ge25519(src); ge25519_mul8(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(src)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_mul8_obj, 1, 2, - mod_trezorcrypto_monero_ge25519_mul8); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_monero_ge25519_mul8_obj, + mod_trezorcrypto_monero_ge25519_mul8); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime( +STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime_into( size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_ge25519(args[1 + off]); - assert_scalar(args[2 + off]); - assert_scalar(args[3 + off]); + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_ge25519_r(args[0]); + assert_ge25519(args[1]); + assert_scalar(args[2]); + assert_scalar(args[3]); ge25519_double_scalarmult_vartime( - &MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off]), MP_OBJ_C_SCALAR(args[3 + off])); - return res; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime_obj, 3, 4, - mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime); - -/// def ge25519_double_scalarmult_vartime2( -/// r: Ge25519 | None, -/// p1: Ge25519, -/// s1: Sc25519, -/// p2: Ge25519, -/// s2: Sc25519, -/// ) -> Ge25519: -/// """ -/// s1 * p1 + s2 * p2 -/// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime2( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 5; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - - assert_ge25519(args[1 + off]); - assert_scalar(args[2 + off]); - assert_ge25519(args[3 + off]); - assert_scalar(args[4 + off]); - - ge25519_double_scalarmult_vartime2( - &MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off]), &MP_OBJ_C_GE25519(args[3 + off]), - MP_OBJ_C_SCALAR(args[4 + off])); + &MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1]), + MP_OBJ_C_SCALAR(args[2]), MP_OBJ_C_SCALAR(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime2_obj, 4, 5, - mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime2); + mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime_into_obj, 4, 4, + mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime_into); -/// def ge25519_scalarmult_base( -/// r: Ge25519 | None, s: Sc25519 | int -/// ) -> Ge25519: +/// def scalarmult_base_into( +/// r: Point | None, s: Scalar | int +/// ) -> Point: /// """ /// s * G /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_scalarmult_base( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - - if (MP_OBJ_IS_SCALAR(args[1 + off])) { - ge25519_scalarmult_base_wrapper(&MP_OBJ_GE25519(res), - MP_OBJ_C_SCALAR(args[1 + off])); - } else if (mp_obj_is_integer(args[1 + off])) { +STATIC mp_obj_t mod_trezorcrypto_monero_scalarmult_base_into( + const mp_obj_t dest, const mp_obj_t src) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); + + if (MP_OBJ_IS_SCALAR(src)) { + ge25519_scalarmult_base_wrapper(&MP_OBJ_GE25519(res), MP_OBJ_C_SCALAR(src)); + } else if (mp_obj_is_integer(src)) { bignum256modm mlt = {0}; - set256_modm(mlt, mp_obj_get_int(args[1 + off])); + set256_modm(mlt, mp_obj_get_int(src)); ge25519_scalarmult_base_wrapper(&MP_OBJ_GE25519(res), mlt); } else { mp_raise_ValueError("unknown base mult type"); @@ -791,52 +693,50 @@ STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_scalarmult_base( return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_scalarmult_base_obj, 1, 2, - mod_trezorcrypto_monero_ge25519_scalarmult_base); +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + mod_trezorcrypto_monero_scalarmult_base_into_obj, + mod_trezorcrypto_monero_scalarmult_base_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_scalarmult( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_ge25519(args[1 + off]); - - if (MP_OBJ_IS_SCALAR(args[2 + off])) { - ge25519_scalarmult(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off])); - } else if (mp_obj_is_integer(args[2 + off])) { +STATIC mp_obj_t mod_trezorcrypto_monero_scalarmult_into(const mp_obj_t dest, + const mp_obj_t p, + const mp_obj_t s) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); + assert_ge25519(p); + + if (MP_OBJ_IS_SCALAR(s)) { + ge25519_scalarmult(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(p), + MP_OBJ_C_SCALAR(s)); + } else if (mp_obj_is_integer(s)) { bignum256modm mlt = {0}; - set256_modm(mlt, mp_obj_get_int(args[2 + off])); - ge25519_scalarmult(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - mlt); + set256_modm(mlt, mp_obj_get_int(s)); + ge25519_scalarmult(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(p), mlt); } else { mp_raise_ValueError("unknown mult type"); } return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_scalarmult_obj, 2, 3, - mod_trezorcrypto_monero_ge25519_scalarmult); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_monero_scalarmult_into_obj, + mod_trezorcrypto_monero_scalarmult_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_pack(size_t n_args, - const mp_obj_t *args) { - if (n_args == 1 || args[0] == mp_const_none) { - assert_ge25519(args[0]); +STATIC mp_obj_t mod_trezorcrypto_monero_encodepoint_into(size_t n_args, + const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 2, 3, false); + if (args[0] == mp_const_none) { + assert_ge25519(args[1]); vstr_t out = {0}; vstr_init_len(&out, 32); - ge25519_pack((uint8_t *)out.buf, &MP_OBJ_C_GE25519(args[0])); + ge25519_pack((uint8_t *)out.buf, &MP_OBJ_C_GE25519(args[1])); return mp_obj_new_str_from_vstr(&mp_type_bytes, &out); } else { mp_buffer_info_t bufm = {0}; @@ -851,70 +751,69 @@ STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_pack(size_t n_args, } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_pack_obj, 1, 3, - mod_trezorcrypto_monero_ge25519_pack); + mod_trezorcrypto_monero_encodepoint_into_obj, 2, 3, + mod_trezorcrypto_monero_encodepoint_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_ge25519_unpack_vartime( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args >= 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_decodepoint_into(size_t n_args, + const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 2, 3, false); + mp_obj_t res = mp_obj_new_ge25519_r(args[0]); const mp_int_t offset = n_args >= 3 ? mp_obj_get_int(args[2]) : 0; - mp_unpack_ge25519(&MP_OBJ_GE25519(res), args[1 + off], offset); + mp_unpack_ge25519(&MP_OBJ_GE25519(res), args[1], offset); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_ge25519_unpack_vartime_obj, 1, 3, - mod_trezorcrypto_monero_ge25519_unpack_vartime); + mod_trezorcrypto_monero_decodepoint_into_obj, 2, 3, + mod_trezorcrypto_monero_decodepoint_into); // // XMR defs // -/// 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 /// """ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_base58_addr_encode_check( - size_t n_args, const mp_obj_t *args) { + const mp_obj_t tag, const mp_obj_t buff) { vstr_t out = {0}; vstr_init_len(&out, 128); mp_buffer_info_t data = {0}; - mp_get_buffer_raise(args[1], &data, MP_BUFFER_READ); + mp_get_buffer_raise(buff, &data, MP_BUFFER_READ); - int sz = xmr_base58_addr_encode_check(mp_obj_get_int(args[0]), data.buf, - data.len, out.buf, out.alloc); + int sz = xmr_base58_addr_encode_check(mp_obj_get_int(tag), data.buf, data.len, + out.buf, out.alloc); if (sz <= 0) { vstr_clear(&out); mp_raise_ValueError("b58 encoding error"); } out.len = sz; - return mp_obj_new_str_from_vstr(&mp_type_bytes, &out); + return mp_obj_new_str_from_vstr(&mp_type_str, &out); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_base58_addr_encode_check_obj, 2, 2, +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + mod_trezorcrypto_monero_xmr_base58_addr_encode_check_obj, mod_trezorcrypto_monero_xmr_base58_addr_encode_check); -/// 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. /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_base58_addr_decode_check( - size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t +mod_trezorcrypto_monero_xmr_base58_addr_decode_check(const mp_obj_t buff) { uint64_t tag = 0; vstr_t out = {0}; vstr_init_len(&out, 128); mp_buffer_info_t data = {0}; - mp_get_buffer_raise(args[0], &data, MP_BUFFER_READ); + mp_get_buffer_raise(buff, &data, MP_BUFFER_READ); int sz = xmr_base58_addr_decode_check(data.buf, data.len, &tag, out.buf, out.alloc); @@ -929,36 +828,39 @@ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_base58_addr_decode_check( tuple->items[1] = mp_obj_new_int_from_ull(tag); return MP_OBJ_FROM_PTR(tuple); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_base58_addr_decode_check_obj, 1, 1, +STATIC MP_DEFINE_CONST_FUN_OBJ_1( + mod_trezorcrypto_monero_xmr_base58_addr_decode_check_obj, mod_trezorcrypto_monero_xmr_base58_addr_decode_check); -/// def xmr_random_scalar(r: Sc25519 | None = None) -> Sc25519: +/// def random_scalar(r: Scalar | None = None) -> Scalar: /// """ /// Generates a random scalar /// """ -STATIC mp_obj_t -mod_trezorcrypto_monero_xmr_random_scalar(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t mod_trezorcrypto_monero_random_scalar(size_t n_args, + const mp_obj_t *args) { mp_obj_t res = mp_obj_new_scalar_r(n_args == 1 ? args[0] : mp_const_none); xmr_random_scalar(MP_OBJ_SCALAR(res)); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_random_scalar_obj, 0, 1, - mod_trezorcrypto_monero_xmr_random_scalar); - -// clang-format off -/// def xmr_fast_hash(r: bytes | None, buff: bytes, length: int, offset: int) -> bytes: -// clang-format on + mod_trezorcrypto_monero_random_scalar_obj, 0, 1, + mod_trezorcrypto_monero_random_scalar); + +/// def fast_hash_into( +/// r: bytes | None, +/// buff: bytes, +/// length: int | None = None, +/// offset: int = 0, +/// ) -> bytes: /// """ /// XMR fast hash /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_fast_hash(size_t n_args, - const mp_obj_t *args) { - const int off = n_args >= 2 ? 0 : -1; +STATIC mp_obj_t mod_trezorcrypto_monero_fast_hash_into(size_t n_args, + const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 2, 4, false); vstr_t out = {0}; uint8_t *buff_use = NULL; - if (n_args >= 2) { + if (args[0] != mp_const_none) { mp_buffer_info_t odata = {0}; mp_get_buffer_raise(args[0], &odata, MP_BUFFER_WRITE); if (odata.len < HASHER_DIGEST_LENGTH) { @@ -972,7 +874,7 @@ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_fast_hash(size_t n_args, } mp_buffer_info_t data = {0}; - mp_get_buffer_raise(args[1 + off], &data, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &data, MP_BUFFER_READ); mp_int_t length = n_args >= 3 ? mp_obj_get_int(args[2]) : data.len; mp_int_t offset = n_args >= 4 ? mp_obj_get_int(args[3]) : 0; if (length < 0) length += data.len; @@ -981,26 +883,29 @@ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_fast_hash(size_t n_args, mp_raise_ValueError("Illegal offset/length"); } xmr_fast_hash(buff_use, (const char *)data.buf + offset, length); - return n_args >= 2 ? args[0] : mp_obj_new_str_from_vstr(&mp_type_bytes, &out); + return args[0] != mp_const_none + ? args[0] + : mp_obj_new_str_from_vstr(&mp_type_bytes, &out); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_fast_hash_obj, 1, 4, - mod_trezorcrypto_monero_xmr_fast_hash); + mod_trezorcrypto_monero_fast_hash_into_obj, 2, 4, + mod_trezorcrypto_monero_fast_hash_into); -// clang-format off -/// def xmr_hash_to_ec(r: Ge25519 | None, buff: bytes, length: int, offset: -/// int) -> Ge25519: -// clang-format on +/// def hash_to_point_into( +/// r: Point | None, +/// buff: bytes, +/// length: int | None = None, +/// offset: int = 0, +/// ) -> Point: /// """ /// XMR hashing to EC point /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_hash_to_ec(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args >= 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); +STATIC mp_obj_t mod_trezorcrypto_monero_hash_to_point_into( + size_t n_args, const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 2, 4, false); + mp_obj_t res = mp_obj_new_ge25519_r(args[0]); mp_buffer_info_t data = {0}; - mp_get_buffer_raise(args[1 + off], &data, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &data, MP_BUFFER_READ); mp_int_t length = n_args >= 3 ? mp_obj_get_int(args[2]) : data.len; mp_int_t offset = n_args >= 4 ? mp_obj_get_int(args[3]) : 0; if (length < 0) length += data.len; @@ -1013,23 +918,24 @@ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_hash_to_ec(size_t n_args, return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_hash_to_ec_obj, 1, 4, - mod_trezorcrypto_monero_xmr_hash_to_ec); + mod_trezorcrypto_monero_hash_to_point_into_obj, 2, 4, + mod_trezorcrypto_monero_hash_to_point_into); -// clang-format off -/// def xmr_hash_to_scalar(r: Sc25519 | None, buff: bytes, length: int, -/// offset: int) -> Sc25519: -// clang-format on +/// def hash_to_scalar_into( +/// r: Scalar | None, +/// buff: bytes, +/// length: int | None = None, +/// offset: int = 0, +/// ) -> Scalar: /// """ /// XMR hashing to EC scalar /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_hash_to_scalar( +STATIC mp_obj_t mod_trezorcrypto_monero_hash_to_scalar_into( size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args >= 2; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); + mp_arg_check_num(n_args, 0, 2, 4, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); mp_buffer_info_t data = {0}; - mp_get_buffer_raise(args[1 + off], &data, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &data, MP_BUFFER_READ); mp_int_t length = n_args >= 3 ? mp_obj_get_int(args[2]) : data.len; mp_int_t offset = n_args >= 4 ? mp_obj_get_int(args[3]) : 0; if (length < 0) length += data.len; @@ -1042,227 +948,165 @@ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_hash_to_scalar( return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_hash_to_scalar_obj, 1, 4, - mod_trezorcrypto_monero_xmr_hash_to_scalar); + mod_trezorcrypto_monero_hash_to_scalar_into_obj, 2, 4, + mod_trezorcrypto_monero_hash_to_scalar_into); /// 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)) /// """ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_derivation_to_scalar( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); - assert_ge25519(args[1 + off]); - xmr_derivation_to_scalar(MP_OBJ_SCALAR(res), &MP_OBJ_C_GE25519(args[1 + off]), - mp_obj_get_int(args[2 + off])); + const mp_obj_t dest, const mp_obj_t p, const mp_obj_t output_index) { + mp_obj_t res = mp_obj_new_scalar_r(dest); + assert_ge25519(p); + xmr_derivation_to_scalar(MP_OBJ_SCALAR(res), &MP_OBJ_C_GE25519(p), + mp_obj_get_int(output_index)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_derivation_to_scalar_obj, 2, 3, +STATIC MP_DEFINE_CONST_FUN_OBJ_3( + mod_trezorcrypto_monero_xmr_derivation_to_scalar_obj, mod_trezorcrypto_monero_xmr_derivation_to_scalar); /// def xmr_generate_key_derivation( -/// r: Ge25519 | None, A: Ge25519, b: Sc25519 -/// ) -> Ge25519: +/// r: Point | None, A: Point, b: Scalar +/// ) -> Point: /// """ /// 8*(key2*key1) /// """ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_generate_key_derivation( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_ge25519(args[1 + off]); - assert_scalar(args[2 + off]); - xmr_generate_key_derivation(&MP_OBJ_GE25519(res), - &MP_OBJ_C_GE25519(args[1 + off]), - MP_OBJ_C_SCALAR(args[2 + off])); + const mp_obj_t dest, const mp_obj_t A, const mp_obj_t b) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); + assert_ge25519(A); + assert_scalar(b); + xmr_generate_key_derivation(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(A), + MP_OBJ_C_SCALAR(b)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_generate_key_derivation_obj, 2, 3, +STATIC MP_DEFINE_CONST_FUN_OBJ_3( + mod_trezorcrypto_monero_xmr_generate_key_derivation_obj, mod_trezorcrypto_monero_xmr_generate_key_derivation); /// 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)) /// """ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_derive_private_key( size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); - assert_ge25519(args[1 + off]); - assert_scalar(args[3 + off]); - xmr_derive_private_key(MP_OBJ_SCALAR(res), &MP_OBJ_C_GE25519(args[1 + off]), - mp_obj_get_int(args[2 + off]), - MP_OBJ_C_SCALAR(args[3 + off])); + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); + assert_ge25519(args[1]); + assert_scalar(args[3]); + xmr_derive_private_key(MP_OBJ_SCALAR(res), &MP_OBJ_C_GE25519(args[1]), + mp_obj_get_int(args[2]), MP_OBJ_C_SCALAR(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_derive_private_key_obj, 3, 4, + mod_trezorcrypto_monero_xmr_derive_private_key_obj, 4, 4, mod_trezorcrypto_monero_xmr_derive_private_key); /// 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 /// """ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_derive_public_key( size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_ge25519(args[1 + off]); - assert_ge25519(args[3 + off]); - xmr_derive_public_key(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1 + off]), - mp_obj_get_int(args[2 + off]), - &MP_OBJ_C_GE25519(args[3 + off])); + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_ge25519_r(args[0]); + assert_ge25519(args[1]); + assert_ge25519(args[3]); + xmr_derive_public_key(&MP_OBJ_GE25519(res), &MP_OBJ_C_GE25519(args[1]), + mp_obj_get_int(args[2]), &MP_OBJ_C_GE25519(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_derive_public_key_obj, 3, 4, + mod_trezorcrypto_monero_xmr_derive_public_key_obj, 4, 4, mod_trezorcrypto_monero_xmr_derive_public_key); -/// def xmr_add_keys2( -/// r: Ge25519 | None, a: Sc25519, b: Sc25519, B: Ge25519 -/// ) -> Ge25519: -/// """ -/// aG + bB, G is basepoint -/// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_add_keys2(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - assert_ge25519(args[3 + off]); - xmr_add_keys2(&MP_OBJ_GE25519(res), MP_OBJ_SCALAR(args[1 + off]), - MP_OBJ_SCALAR(args[2 + off]), &MP_OBJ_C_GE25519(args[3 + off])); - return res; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_add_keys2_obj, 3, 4, - mod_trezorcrypto_monero_xmr_add_keys2); - -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_add_keys2_vartime( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[1 + off]); - assert_scalar(args[2 + off]); - assert_ge25519(args[3 + off]); - xmr_add_keys2_vartime(&MP_OBJ_GE25519(res), MP_OBJ_SCALAR(args[1 + off]), - MP_OBJ_SCALAR(args[2 + off]), - &MP_OBJ_C_GE25519(args[3 + off])); - return res; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_add_keys2_vartime_obj, 3, 4, - mod_trezorcrypto_monero_xmr_add_keys2_vartime); - -/// def xmr_add_keys3( -/// r: Ge25519 | None, a: Sc25519, A: Ge25519, b: Sc25519, B: Ge25519 -/// ) -> Ge25519: -/// """ -/// aA + bB -/// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_add_keys3(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 5; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[1 + off]); - assert_ge25519(args[2 + off]); - assert_scalar(args[3 + off]); - assert_ge25519(args[4 + off]); - xmr_add_keys3(&MP_OBJ_GE25519(res), MP_OBJ_SCALAR(args[1 + off]), - &MP_OBJ_C_GE25519(args[2 + off]), MP_OBJ_SCALAR(args[3 + off]), - &MP_OBJ_C_GE25519(args[4 + off])); +STATIC mp_obj_t mod_trezorcrypto_monero_add_keys2_into(size_t n_args, + const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_ge25519_r(args[0]); + assert_scalar(args[1]); + assert_scalar(args[2]); + assert_ge25519(args[3]); + xmr_add_keys2_vartime(&MP_OBJ_GE25519(res), MP_OBJ_SCALAR(args[1]), + MP_OBJ_SCALAR(args[2]), &MP_OBJ_C_GE25519(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_add_keys3_obj, 4, 5, - mod_trezorcrypto_monero_xmr_add_keys3); + mod_trezorcrypto_monero_add_keys2_into_obj, 4, 4, + mod_trezorcrypto_monero_add_keys2_into); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_add_keys3_vartime( - size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 5; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[1 + off]); - assert_ge25519(args[2 + off]); - assert_scalar(args[3 + off]); - assert_ge25519(args[4 + off]); - xmr_add_keys3_vartime(&MP_OBJ_GE25519(res), MP_OBJ_SCALAR(args[1 + off]), - &MP_OBJ_C_GE25519(args[2 + off]), - MP_OBJ_SCALAR(args[3 + off]), - &MP_OBJ_C_GE25519(args[4 + off])); +STATIC mp_obj_t mod_trezorcrypto_monero_add_keys3_into(size_t n_args, + const mp_obj_t *args) { + mp_arg_check_num(n_args, 0, 5, 5, false); + mp_obj_t res = mp_obj_new_ge25519_r(args[0]); + assert_scalar(args[1]); + assert_ge25519(args[2]); + assert_scalar(args[3]); + assert_ge25519(args[4]); + xmr_add_keys3_vartime(&MP_OBJ_GE25519(res), MP_OBJ_SCALAR(args[1]), + &MP_OBJ_C_GE25519(args[2]), MP_OBJ_SCALAR(args[3]), + &MP_OBJ_C_GE25519(args[4])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_add_keys3_vartime_obj, 4, 5, - mod_trezorcrypto_monero_xmr_add_keys3_vartime); + mod_trezorcrypto_monero_add_keys3_into_obj, 5, 5, + mod_trezorcrypto_monero_add_keys3_into); /// 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) /// """ STATIC mp_obj_t mod_trezorcrypto_monero_xmr_get_subaddress_secret_key( size_t n_args, const mp_obj_t *args) { - const bool res_arg = n_args == 4; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_scalar_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[3 + off]); - xmr_get_subaddress_secret_key( - MP_OBJ_SCALAR(res), mp_obj_get_int(args[1 + off]), - mp_obj_get_int(args[2 + off]), MP_OBJ_C_SCALAR(args[3 + off])); + mp_arg_check_num(n_args, 0, 4, 4, false); + mp_obj_t res = mp_obj_new_scalar_r(args[0]); + assert_scalar(args[3]); + xmr_get_subaddress_secret_key(MP_OBJ_SCALAR(res), mp_obj_get_int(args[1]), + mp_obj_get_int(args[2]), + MP_OBJ_C_SCALAR(args[3])); return res; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_get_subaddress_secret_key_obj, 3, 4, + mod_trezorcrypto_monero_xmr_get_subaddress_secret_key_obj, 4, 4, mod_trezorcrypto_monero_xmr_get_subaddress_secret_key); -/// 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 /// """ -STATIC mp_obj_t mod_trezorcrypto_monero_xmr_gen_c(size_t n_args, - const mp_obj_t *args) { - const bool res_arg = n_args == 3; - const int off = res_arg ? 0 : -1; - mp_obj_t res = mp_obj_new_ge25519_r(res_arg ? args[0] : mp_const_none); - assert_scalar(args[1 + off]); - xmr_gen_c(&MP_OBJ_GE25519(res), MP_OBJ_C_SCALAR(args[1 + off]), - trezor_obj_get_uint64(args[2 + off])); +STATIC mp_obj_t mod_trezorcrypto_monero_gen_commitment_into( + const mp_obj_t dest, const mp_obj_t a, const mp_obj_t amount) { + mp_obj_t res = mp_obj_new_ge25519_r(dest); + assert_scalar(a); + xmr_gen_c(&MP_OBJ_GE25519(res), MP_OBJ_C_SCALAR(a), + trezor_obj_get_uint64(amount)); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_monero_xmr_gen_c_obj, 2, 3, - mod_trezorcrypto_monero_xmr_gen_c); +STATIC MP_DEFINE_CONST_FUN_OBJ_3( + mod_trezorcrypto_monero_gen_commitment_into_obj, + mod_trezorcrypto_monero_gen_commitment_into); /// def ct_equals(a: bytes, b: bytes) -> bool: /// """ @@ -1351,7 +1195,7 @@ STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_monero_ge25519_locals_dict, STATIC const mp_obj_type_t mod_trezorcrypto_monero_ge25519_type = { {&mp_type_type}, - .name = MP_QSTR_Ge25519, + .name = MP_QSTR_Point, .make_new = mod_trezorcrypto_monero_ge25519_make_new, .locals_dict = (void *)&mod_trezorcrypto_monero_ge25519_locals_dict, }; @@ -1367,7 +1211,7 @@ STATIC MP_DEFINE_CONST_DICT( STATIC const mp_obj_type_t mod_trezorcrypto_monero_bignum256modm_type = { {&mp_type_type}, - .name = MP_QSTR_Sc25519, + .name = MP_QSTR_Scalar, .make_new = mod_trezorcrypto_monero_bignum256modm_make_new, .locals_dict = (void *)&mod_trezorcrypto_monero_bignum256modm_locals_dict, }; @@ -1395,6 +1239,7 @@ STATIC const mp_obj_type_t mod_trezorcrypto_monero_hasher_type = { .locals_dict = (void *)&mod_trezorcrypto_monero_hasher_locals_dict, }; +/// BP_GI_PRE: bytes STATIC const mp_obj_str_t mod_trezorcrypto_monero_BP_GI_PRE_obj = {{&mp_type_bytes}, 0, 8192, (const byte*)"" "\x0b\x48\xbe\x50\xe4\x9c\xad\x13\xfb\x3e\x01\x4f\x3f\xa7\xd6\x8b" "\xac\xa7\xc8\xa9\x10\x83\xdc\x9c\x59\xb3\x79\xaa\xab\x21\x8f\x15" @@ -1910,6 +1755,7 @@ STATIC const mp_obj_str_t mod_trezorcrypto_monero_BP_GI_PRE_obj = {{&mp_type_byt "\x3f\x7e\x8f\x1e\x61\x54\x4d\x51\x89\xc9\xdc\x1e\xc1\x3c\x25\x65" }; +/// BP_HI_PRE: bytes STATIC const mp_obj_str_t mod_trezorcrypto_monero_BP_HI_PRE_obj = {{&mp_type_bytes}, 0, 8192, (const byte*)"" "\x42\xba\x66\x8a\x00\x7d\x0f\xcd\x6f\xea\x40\x09\xde\x8a\x64\x37" "\x24\x8f\x2d\x44\x52\x30\xaf\x00\x4a\x89\xfd\x04\x27\x9b\xc2\x97" @@ -2425,6 +2271,7 @@ STATIC const mp_obj_str_t mod_trezorcrypto_monero_BP_HI_PRE_obj = {{&mp_type_byt "\x21\xd1\x91\xe1\x87\x48\x43\xc1\xbe\x60\xd4\xf3\x57\x06\x9a\xda" }; +/// BP_TWO_N: bytes STATIC const mp_obj_str_t mod_trezorcrypto_monero_BP_TWO_N_obj = {{&mp_type_bytes}, 0, 2048, (const byte*)"" "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" @@ -2558,76 +2405,75 @@ STATIC const mp_obj_str_t mod_trezorcrypto_monero_BP_TWO_N_obj = {{&mp_type_byte STATIC const mp_rom_map_elem_t mod_trezorcrypto_monero_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_monero)}, - {MP_ROM_QSTR(MP_QSTR_init256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_init256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_check256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_check256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_iszero256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_iszero256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_eq256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_eq256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_get256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_get256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_add256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_add256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_sub256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_sub256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_mul256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_mul256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_mulsub256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_mulsub256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_muladd256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_muladd256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_inv256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_inv256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_pack256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_pack256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_unpack256_modm), - MP_ROM_PTR(&mod_trezorcrypto_monero_unpack256_modm_obj)}, - {MP_ROM_QSTR(MP_QSTR_unpack256_modm_noreduce), - MP_ROM_PTR(&mod_trezorcrypto_monero_unpack256_modm_noreduce_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_set_neutral), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_set_neutral_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_set_h), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_set_xmr_h_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_pack), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_pack_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_unpack_vartime), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_unpack_vartime_obj)}, + // types + {MP_ROM_QSTR(MP_QSTR_Point), + MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_type)}, + {MP_ROM_QSTR(MP_QSTR_Scalar), + MP_ROM_PTR(&mod_trezorcrypto_monero_bignum256modm_type)}, + // functions + {MP_ROM_QSTR(MP_QSTR_sc_copy), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_copy_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_check), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_check_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_iszero), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_iszero_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_eq), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_eq_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_add_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_add_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_sub_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_sub_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_mul_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_mul_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_mulsub_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_mulsub_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_muladd_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_muladd_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_sc_inv_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_sc_inv_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_encodeint_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_encodeint_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_decodeint_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_decodeint_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_decodeint_into_noreduce), + MP_ROM_PTR(&mod_trezorcrypto_monero_decodeint_into_noreduce_obj)}, + {MP_ROM_QSTR(MP_QSTR_identity_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_identity_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_xmr_H), + MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_H_obj)}, + {MP_ROM_QSTR(MP_QSTR_encodepoint_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_encodepoint_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_decodepoint_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_decodepoint_into_obj)}, {MP_ROM_QSTR(MP_QSTR_ge25519_check), MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_check_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_eq), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_eq_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_add), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_add_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_sub), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_sub_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_double), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_double_obj)}, + {MP_ROM_QSTR(MP_QSTR_point_eq), + MP_ROM_PTR(&mod_trezorcrypto_monero_point_eq_obj)}, + {MP_ROM_QSTR(MP_QSTR_point_add_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_point_add_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_point_sub_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_point_sub_into_obj)}, {MP_ROM_QSTR(MP_QSTR_ge25519_mul8), MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_mul8_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_double_scalarmult_vartime), - MP_ROM_PTR( - &mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_double_scalarmult_vartime2), + {MP_ROM_QSTR(MP_QSTR_ge25519_double_scalarmult_vartime_into), MP_ROM_PTR( - &mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime2_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_scalarmult_base), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_scalarmult_base_obj)}, - {MP_ROM_QSTR(MP_QSTR_ge25519_scalarmult), - MP_ROM_PTR(&mod_trezorcrypto_monero_ge25519_scalarmult_obj)}, + &mod_trezorcrypto_monero_ge25519_double_scalarmult_vartime_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_scalarmult_base_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_scalarmult_base_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_scalarmult_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_scalarmult_into_obj)}, {MP_ROM_QSTR(MP_QSTR_xmr_base58_addr_encode_check), MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_base58_addr_encode_check_obj)}, {MP_ROM_QSTR(MP_QSTR_xmr_base58_addr_decode_check), MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_base58_addr_decode_check_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_random_scalar), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_random_scalar_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_fast_hash), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_fast_hash_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_hash_to_ec), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_hash_to_ec_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_hash_to_scalar), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_hash_to_scalar_obj)}, + {MP_ROM_QSTR(MP_QSTR_random_scalar), + MP_ROM_PTR(&mod_trezorcrypto_monero_random_scalar_obj)}, + {MP_ROM_QSTR(MP_QSTR_fast_hash_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_fast_hash_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_hash_to_point_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_hash_to_point_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_hash_to_scalar_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_hash_to_scalar_into_obj)}, {MP_ROM_QSTR(MP_QSTR_xmr_derivation_to_scalar), MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_derivation_to_scalar_obj)}, {MP_ROM_QSTR(MP_QSTR_xmr_generate_key_derivation), @@ -2636,18 +2482,14 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_monero_globals_table[] = { MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_derive_private_key_obj)}, {MP_ROM_QSTR(MP_QSTR_xmr_derive_public_key), MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_derive_public_key_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_add_keys2), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_add_keys2_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_add_keys2_vartime), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_add_keys2_vartime_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_add_keys3), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_add_keys3_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_add_keys3_vartime), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_add_keys3_vartime_obj)}, + {MP_ROM_QSTR(MP_QSTR_add_keys2_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_add_keys2_into_obj)}, + {MP_ROM_QSTR(MP_QSTR_add_keys3_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_add_keys3_into_obj)}, {MP_ROM_QSTR(MP_QSTR_xmr_get_subaddress_secret_key), MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_get_subaddress_secret_key_obj)}, - {MP_ROM_QSTR(MP_QSTR_xmr_gen_c), - MP_ROM_PTR(&mod_trezorcrypto_monero_xmr_gen_c_obj)}, + {MP_ROM_QSTR(MP_QSTR_gen_commitment_into), + MP_ROM_PTR(&mod_trezorcrypto_monero_gen_commitment_into_obj)}, {MP_ROM_QSTR(MP_QSTR_ct_equals), MP_ROM_PTR(&mod_trezorcrypto_ct_equals_obj)}, // bulletproof constants diff --git a/core/mocks/generated/trezorcrypto/monero.pyi b/core/mocks/generated/trezorcrypto/monero.pyi index 9b9e3b6f53b..9f8da32f340 100644 --- a/core/mocks/generated/trezorcrypto/monero.pyi +++ b/core/mocks/generated/trezorcrypto/monero.pyi @@ -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 diff --git a/core/mocks/micropython.pyi b/core/mocks/micropython.pyi index 22238a298a7..e751ce09fd1 100644 --- a/core/mocks/micropython.pyi +++ b/core/mocks/micropython.pyi @@ -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: ... diff --git a/core/src/all_modules.py b/core/src/all_modules.py index f6c279f8cf7..ce46ef21d59 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -434,6 +434,8 @@ 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 @@ 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 @@ 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 @@ 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 diff --git a/core/src/apps/monero/diag.py b/core/src/apps/monero/diag.py index 5bc05fd1c96..430a79a9a2b 100644 --- a/core/src/apps/monero/diag.py +++ b/core/src/apps/monero/diag.py @@ -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 @@ 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 @@ def log_trace(x=None): 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 @@ def check_mem(x=""): 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 @@ async def diag(ctx, msg, **kwargs): 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") diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 195b51aaf2c..547371ec5c9 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -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()) diff --git a/core/src/apps/monero/get_tx_keys.py b/core/src/apps/monero/get_tx_keys.py index c10b060289b..1a2a964a41d 100644 --- a/core/src/apps/monero/get_tx_keys.py +++ b/core/src/apps/monero/get_tx_keys.py @@ -14,22 +14,26 @@ 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) diff --git a/core/src/apps/monero/get_watch_only.py b/core/src/apps/monero/get_watch_only.py index e36b6cfec01..7c9d6fcebf6 100644 --- a/core/src/apps/monero/get_watch_only.py +++ b/core/src/apps/monero/get_watch_only.py @@ -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()) diff --git a/core/src/apps/monero/key_image_sync.py b/core/src/apps/monero/key_image_sync.py index 983e996187f..6e48a0f7858 100644 --- a/core/src/apps/monero/key_image_sync.py +++ b/core/src/apps/monero/key_image_sync.py @@ -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 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") diff --git a/core/src/apps/monero/layout.py b/core/src/apps/monero/layout.py index e2b2ec2e8c1..f2e8b83496b 100644 --- a/core/src/apps/monero/layout.py +++ b/core/src/apps/monero/layout.py @@ -14,18 +14,21 @@ 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 @@ def on_render(self): 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 @@ def on_render(self): 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 @@ def on_render(self): ) -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)) diff --git a/core/src/apps/monero/live_refresh.py b/core/src/apps/monero/live_refresh.py index 7af7f240356..1cec5a1d60f 100644 --- a/core/src/apps/monero/live_refresh.py +++ b/core/src/apps/monero/live_refresh.py @@ -1,4 +1,5 @@ import gc +from typing import TYPE_CHECKING import storage.cache from trezor import log @@ -6,7 +7,6 @@ from trezor.messages import ( MoneroLiveRefreshFinalAck, MoneroLiveRefreshStartAck, - MoneroLiveRefreshStartRequest, MoneroLiveRefreshStepAck, MoneroLiveRefreshStepRequest, ) @@ -14,40 +14,49 @@ 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 diff --git a/core/src/apps/monero/misc.py b/core/src/apps/monero/misc.py index 883e6a99d78..83b8ce1ab29 100644 --- a/core/src/apps/monero/misc.py +++ b/core/src/apps/monero/misc.py @@ -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 diff --git a/core/src/apps/monero/sign_tx.py b/core/src/apps/monero/sign_tx.py index 108bb3e6163..e67bbdce703 100644 --- a/core/src/apps/monero/sign_tx.py +++ b/core/src/apps/monero/sign_tx.py @@ -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 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 diff --git a/core/src/apps/monero/signing/__init__.py b/core/src/apps/monero/signing/__init__.py index 088cf4f46e2..d767910d475 100644 --- a/core/src/apps/monero/signing/__init__.py +++ b/core/src/apps/monero/signing/__init__.py @@ -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 diff --git a/core/src/apps/monero/signing/offloading_keys.py b/core/src/apps/monero/signing/offloading_keys.py index 10e5fd991a5..d463e116d68 100644 --- a/core/src/apps/monero/signing/offloading_keys.py +++ b/core/src/apps/monero/signing/offloading_keys.py @@ -3,10 +3,9 @@ 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 @@ 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 diff --git a/core/src/apps/monero/signing/state.py b/core/src/apps/monero/signing/state.py index 23f4aea3ab8..5700c041b25 100644 --- a/core/src/apps/monero/signing/state.py +++ b/core/src/apps/monero/signing/state.py @@ -7,8 +7,10 @@ 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 @@ def __init__(self, ctx): - 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 @@ def __init__(self, ctx): 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 @@ def __init__(self, ctx): 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 @@ def __init__(self, ctx): 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__, diff --git a/core/src/apps/monero/signing/step_01_init_transaction.py b/core/src/apps/monero/signing/step_01_init_transaction.py index 9cbc645a80f..9d07cff7f77 100644 --- a/core/src/apps/monero/signing/step_01_init_transaction.py +++ b/core/src/apps/monero/signing/step_01_init_transaction.py @@ -7,10 +7,9 @@ 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) diff --git a/core/src/apps/monero/signing/step_02_set_input.py b/core/src/apps/monero/signing/step_02_set_input.py index 5a1d5b05ec4..1f5c9a554e9 100644 --- a/core/src/apps/monero/signing/step_02_set_input.py +++ b/core/src/apps/monero/signing/step_02_set_input.py @@ -14,12 +14,11 @@ 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 diff --git a/core/src/apps/monero/signing/step_03_inputs_permutation.py b/core/src/apps/monero/signing/step_03_inputs_permutation.py deleted file mode 100644 index 5628386cbab..00000000000 --- a/core/src/apps/monero/signing/step_03_inputs_permutation.py +++ /dev/null @@ -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") diff --git a/core/src/apps/monero/signing/step_04_input_vini.py b/core/src/apps/monero/signing/step_04_input_vini.py index 71d759ae763..a9d9ce56695 100644 --- a/core/src/apps/monero/signing/step_04_input_vini.py +++ b/core/src/apps/monero/signing/step_04_input_vini.py @@ -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") diff --git a/core/src/apps/monero/signing/step_05_all_inputs_set.py b/core/src/apps/monero/signing/step_05_all_inputs_set.py index b35dae4469e..c465653eeb9 100644 --- a/core/src/apps/monero/signing/step_05_all_inputs_set.py +++ b/core/src/apps/monero/signing/step_05_all_inputs_set.py @@ -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 diff --git a/core/src/apps/monero/signing/step_06_set_output.py b/core/src/apps/monero/signing/step_06_set_output.py index c6d30758a18..0ecfc0bc017 100644 --- a/core/src/apps/monero/signing/step_06_set_output.py +++ b/core/src/apps/monero/signing/step_06_set_output.py @@ -9,12 +9,11 @@ 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 diff --git a/core/src/apps/monero/signing/step_07_all_outputs_set.py b/core/src/apps/monero/signing/step_07_all_outputs_set.py index 7f958ce9192..a311a8aa94a 100644 --- a/core/src/apps/monero/signing/step_07_all_outputs_set.py +++ b/core/src/apps/monero/signing/step_07_all_outputs_set.py @@ -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. """ diff --git a/core/src/apps/monero/signing/step_09_sign_input.py b/core/src/apps/monero/signing/step_09_sign_input.py index 4cec187e83d..40ba221b62e 100644 --- a/core/src/apps/monero/signing/step_09_sign_input.py +++ b/core/src/apps/monero/signing/step_09_sign_input.py @@ -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 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) ) diff --git a/core/src/apps/monero/signing/step_10_sign_final.py b/core/src/apps/monero/signing/step_10_sign_final.py index f211fd06c27..56a1d3a2f87 100644 --- a/core/src/apps/monero/signing/step_10_sign_final.py +++ b/core/src/apps/monero/signing/step_10_sign_final.py @@ -8,19 +8,14 @@ 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 diff --git a/core/src/apps/monero/xmr/__init__.py b/core/src/apps/monero/xmr/__init__.py new file mode 100644 index 00000000000..85de0c22a25 --- /dev/null +++ b/core/src/apps/monero/xmr/__init__.py @@ -0,0 +1 @@ +from trezor.crypto import monero as crypto # noqa: F401 diff --git a/core/src/apps/monero/xmr/addresses.py b/core/src/apps/monero/xmr/addresses.py index 4adfe6b4fcd..eaeb417cc5e 100644 --- a/core/src/apps/monero/xmr/addresses.py +++ b/core/src/apps/monero/xmr/addresses.py @@ -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 """ diff --git a/core/src/apps/monero/xmr/bulletproof.py b/core/src/apps/monero/xmr/bulletproof.py index d087da431a7..1dabb023686 100644 --- a/core/src/apps/monero/xmr/bulletproof.py +++ b/core/src/apps/monero/xmr/bulletproof.py @@ -1,14 +1,28 @@ import gc from micropython import const +from typing import TYPE_CHECKING from trezor import utils +from trezor.crypto import random from trezor.utils import memcpy as tmemcpy -from apps.monero.xmr import crypto +from apps.monero.xmr import crypto, crypto_helpers from apps.monero.xmr.serialize.int_serialize import dump_uvarint_b_into, uvarint_size -# Constants +if TYPE_CHECKING: + from typing import Iterator, TypeVar, Generic + + from .serialize_messages.tx_rsig_bulletproof import Bulletproof + + T = TypeVar("T") + ScalarDst = TypeVar("ScalarDst", bytearray, crypto.Scalar) + +else: + Generic = (object,) + T = 0 # type: ignore +# Constants +TBYTES = (bytes, bytearray, memoryview) _BP_LOG_N = const(6) _BP_N = const(64) # 1 << _BP_LOG_N _BP_M = const(16) # maximal number of bulletproofs @@ -17,7 +31,7 @@ _ONE = b"\x01" + b"\x00" * 31 _TWO = b"\x02" + b"\x00" * 31 _EIGHT = b"\x08" + b"\x00" * 31 -_INV_EIGHT = crypto.INV_EIGHT +_INV_EIGHT = crypto_helpers.INV_EIGHT _MINUS_ONE = b"\xec\xd3\xf5\x5c\x1a\x63\x12\x58\xd6\x9c\xf7\xa2\xde\xf9\xde\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10" # _MINUS_INV_EIGHT = b"\x74\xa4\x19\x7a\xf0\x7d\x0b\xf7\x05\xc2\xda\x25\x2b\x5c\x0b\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a" @@ -37,53 +51,50 @@ _tmp_bf_0 = bytearray(32) _tmp_bf_1 = bytearray(32) -_tmp_bf_2 = bytearray(32) _tmp_bf_exp = bytearray(11 + 32 + 4) -_tmp_pt_1 = crypto.new_point() -_tmp_pt_2 = crypto.new_point() -_tmp_pt_3 = crypto.new_point() -_tmp_pt_4 = crypto.new_point() +_tmp_pt_1 = crypto.Point() +_tmp_pt_2 = crypto.Point() +_tmp_pt_3 = crypto.Point() +_tmp_pt_4 = crypto.Point() -_tmp_sc_1 = crypto.new_scalar() -_tmp_sc_2 = crypto.new_scalar() -_tmp_sc_3 = crypto.new_scalar() -_tmp_sc_4 = crypto.new_scalar() +_tmp_sc_1 = crypto.Scalar() +_tmp_sc_2 = crypto.Scalar() +_tmp_sc_3 = crypto.Scalar() +_tmp_sc_4 = crypto.Scalar() -def _ensure_dst_key(dst=None): +def _ensure_dst_key(dst: bytearray | None = None) -> bytearray: if dst is None: dst = bytearray(32) return dst -def memcpy(dst, dst_off, src, src_off, len): +def memcpy( + dst: bytearray, dst_off: int, src: bytes, src_off: int, len: int +) -> bytearray: if dst is not None: tmemcpy(dst, dst_off, src, src_off, len) return dst -def _alloc_scalars(num=1): - return (crypto.new_scalar() for _ in range(num)) - - -def _copy_key(dst, src): +def _copy_key(dst: bytearray, src: bytes) -> bytearray: for i in range(32): dst[i] = src[i] return dst -def _init_key(val, dst=None): +def _init_key(val: bytes, dst: bytearray | None = None) -> bytearray: dst = _ensure_dst_key(dst) return _copy_key(dst, val) -def _gc_iter(i): +def _gc_iter(i: int) -> None: if i & 127 == 0: gc.collect() -def _invert(dst, x): +def _invert(dst: bytearray, x: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(_tmp_sc_1, x) crypto.sc_inv_into(_tmp_sc_2, _tmp_sc_1) @@ -91,17 +102,27 @@ def _invert(dst, x): return dst -def _scalarmult_key(dst, P, s, s_raw=None, tmp_pt=_tmp_pt_1): +def _scalarmult_key( + dst: bytearray, + P, + s: bytes, + s_raw: int | None = None, + tmp_pt: crypto.Point = _tmp_pt_1, +): + # TODO: two functions based on s/s_raw ? dst = _ensure_dst_key(dst) crypto.decodepoint_into(tmp_pt, P) if s: crypto.decodeint_into_noreduce(_tmp_sc_1, s) - crypto.scalarmult_into(tmp_pt, tmp_pt, _tmp_sc_1 if s else s_raw) + crypto.scalarmult_into(tmp_pt, tmp_pt, _tmp_sc_1) + else: + assert s_raw is not None + crypto.scalarmult_into(tmp_pt, tmp_pt, s_raw) crypto.encodepoint_into(dst, tmp_pt) return dst -def _scalarmultH(dst, x): +def _scalarmultH(dst: bytearray, x: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into(_tmp_sc_1, x) crypto.scalarmult_into(_tmp_pt_1, _XMR_HP, _tmp_sc_1) @@ -109,7 +130,7 @@ def _scalarmultH(dst, x): return dst -def _scalarmult_base(dst, x): +def _scalarmult_base(dst: bytearray, x: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(_tmp_sc_1, x) crypto.scalarmult_base_into(_tmp_pt_1, _tmp_sc_1) @@ -117,14 +138,14 @@ def _scalarmult_base(dst, x): return dst -def _sc_gen(dst=None): +def _sc_gen(dst: bytearray | None = None) -> bytearray: dst = _ensure_dst_key(dst) crypto.random_scalar(_tmp_sc_1) crypto.encodeint_into(dst, _tmp_sc_1) return dst -def _sc_add(dst, a, b): +def _sc_add(dst: bytearray, a: bytes, b: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(_tmp_sc_1, a) crypto.decodeint_into_noreduce(_tmp_sc_2, b) @@ -133,47 +154,60 @@ def _sc_add(dst, a, b): return dst -def _sc_sub(dst, a, b, a_raw=None, b_raw=None): +def _sc_sub( + dst: bytearray | None, + a: bytes | crypto.Scalar, + b: bytes | crypto.Scalar, +) -> bytearray: dst = _ensure_dst_key(dst) - if a: + if not isinstance(a, crypto.Scalar): crypto.decodeint_into_noreduce(_tmp_sc_1, a) - if b: + a = _tmp_sc_1 + if not isinstance(b, crypto.Scalar): crypto.decodeint_into_noreduce(_tmp_sc_2, b) - crypto.sc_sub_into(_tmp_sc_3, _tmp_sc_1 if a else a_raw, _tmp_sc_2 if b else b_raw) + b = _tmp_sc_2 + crypto.sc_sub_into(_tmp_sc_3, a, b) crypto.encodeint_into(dst, _tmp_sc_3) return dst -def _sc_mul(dst, a, b=None, b_raw=None): +def _sc_mul(dst: bytearray | None, a: bytes, b: bytes | crypto.Scalar) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(_tmp_sc_1, a) - if b: + if not isinstance(b, crypto.Scalar): crypto.decodeint_into_noreduce(_tmp_sc_2, b) - crypto.sc_mul_into(_tmp_sc_3, _tmp_sc_1, _tmp_sc_2 if b else b_raw) + b = _tmp_sc_2 + crypto.sc_mul_into(_tmp_sc_3, _tmp_sc_1, b) crypto.encodeint_into(dst, _tmp_sc_3) return dst -def _sc_muladd(dst, a, b, c, a_raw=None, b_raw=None, c_raw=None, raw=False): - dst = _ensure_dst_key(dst) if not raw else (dst if dst else crypto.new_scalar()) - if a: +def _sc_muladd( + dst: ScalarDst, + a: bytes | crypto.Scalar, + b: bytes | crypto.Scalar, + c: bytes | crypto.Scalar, +) -> ScalarDst: + if isinstance(dst, crypto.Scalar): + dst_sc = dst + else: + dst_sc = _tmp_sc_4 + if not isinstance(a, crypto.Scalar): crypto.decodeint_into_noreduce(_tmp_sc_1, a) - if b: + a = _tmp_sc_1 + if not isinstance(b, crypto.Scalar): crypto.decodeint_into_noreduce(_tmp_sc_2, b) - if c: + b = _tmp_sc_2 + if not isinstance(c, crypto.Scalar): crypto.decodeint_into_noreduce(_tmp_sc_3, c) - crypto.sc_muladd_into( - _tmp_sc_4 if not raw else dst, - _tmp_sc_1 if a else a_raw, - _tmp_sc_2 if b else b_raw, - _tmp_sc_3 if c else c_raw, - ) - if not raw: - crypto.encodeint_into(dst, _tmp_sc_4) + c = _tmp_sc_3 + crypto.sc_muladd_into(dst_sc, a, b, c) + if not isinstance(dst, crypto.Scalar): + crypto.encodeint_into(dst, dst_sc) return dst -def _sc_mulsub(dst, a, b, c): +def _sc_mulsub(dst: bytearray | None, a: bytes, b: bytes, c: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(_tmp_sc_1, a) crypto.decodeint_into_noreduce(_tmp_sc_2, b) @@ -183,7 +217,7 @@ def _sc_mulsub(dst, a, b, c): return dst -def _add_keys(dst, A, B): +def _add_keys(dst: bytearray | None, A: bytes, B: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodepoint_into(_tmp_pt_1, A) crypto.decodepoint_into(_tmp_pt_2, B) @@ -192,7 +226,7 @@ def _add_keys(dst, A, B): return dst -def _sub_keys(dst, A, B): +def _sub_keys(dst: bytearray | None, A: bytes, B: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodepoint_into(_tmp_pt_1, A) crypto.decodepoint_into(_tmp_pt_2, B) @@ -201,7 +235,7 @@ def _sub_keys(dst, A, B): return dst -def _add_keys2(dst, a, b, B): +def _add_keys2(dst: bytearray | None, a: bytes, b: bytes, B: bytes) -> bytearray: dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(_tmp_sc_1, a) crypto.decodeint_into_noreduce(_tmp_sc_2, b) @@ -211,17 +245,6 @@ def _add_keys2(dst, a, b, B): return dst -def _add_keys3(dst, a, A, b, B): - dst = _ensure_dst_key(dst) - crypto.decodeint_into_noreduce(_tmp_sc_1, a) - crypto.decodeint_into_noreduce(_tmp_sc_2, b) - crypto.decodepoint_into(_tmp_pt_1, A) - crypto.decodepoint_into(_tmp_pt_2, B) - crypto.add_keys3_into(_tmp_pt_3, _tmp_sc_1, _tmp_pt_1, _tmp_sc_2, _tmp_pt_2) - crypto.encodepoint_into(dst, _tmp_pt_3) - return dst - - def _hash_to_scalar(dst, data): dst = _ensure_dst_key(dst) crypto.hash_to_scalar_into(_tmp_sc_1, data) @@ -231,7 +254,7 @@ def _hash_to_scalar(dst, data): def _hash_vct_to_scalar(dst, data): dst = _ensure_dst_key(dst) - ctx = crypto.get_keccak() + ctx = crypto_helpers.get_keccak() for x in data: ctx.update(x) hsh = ctx.digest() @@ -249,7 +272,7 @@ def _get_exponent(dst, base, idx): memcpy(_tmp_bf_exp, 0, base, 0, 32) memcpy(_tmp_bf_exp, 32, salt, 0, lsalt) dump_uvarint_b_into(idx, _tmp_bf_exp, 32 + lsalt) - crypto.keccak_hash_into(_tmp_bf_1, _tmp_bf_exp, final_size) + crypto.fast_hash_into(_tmp_bf_1, _tmp_bf_exp, final_size) crypto.hash_to_point_into(_tmp_pt_4, _tmp_bf_1) crypto.encodepoint_into(dst, _tmp_pt_4) return dst @@ -260,57 +283,57 @@ def _get_exponent(dst, base, idx): # -class KeyVBase: +class KeyVBase(Generic[T]): """ Base KeyVector object """ __slots__ = ("current_idx", "size") - def __init__(self, elems=64): + def __init__(self, elems: int = 64) -> None: self.current_idx = 0 self.size = elems - def idxize(self, idx): + def idxize(self, idx: int) -> int: if idx < 0: idx = self.size + idx if idx >= self.size: raise IndexError("Index out of bounds") return idx - def __getitem__(self, item): - raise ValueError("Not supported") + def __getitem__(self, item: int) -> T: + raise NotImplementedError - def __setitem__(self, key, value): - raise ValueError("Not supported") + def __setitem__(self, key: int, value: T) -> None: + raise NotImplementedError - def __iter__(self): + def __iter__(self) -> Iterator[T]: self.current_idx = 0 return self - def __next__(self): + def __next__(self) -> T: if self.current_idx >= self.size: raise StopIteration else: self.current_idx += 1 return self[self.current_idx - 1] - def __len__(self): + def __len__(self) -> int: return self.size - def to(self, idx, buff=None, offset=0): + def to(self, idx: int, buff: bytearray | None = None, offset: int = 0) -> bytearray: buff = _ensure_dst_key(buff) return memcpy(buff, offset, self.to(self.idxize(idx)), 0, 32) - def read(self, idx, buff, offset=0): - raise ValueError + def read(self, idx: int, buff: bytes, offset: int = 0) -> bytes: + raise NotImplementedError - def slice(self, res, start, stop): + def slice(self, res, start: int, stop: int): for i in range(start, stop): res[i - start] = self[i] return res - def slice_view(self, start, stop): + def slice_view(self, start: int, stop: int) -> "KeyVSliced": return KeyVSliced(self, start, stop) @@ -318,7 +341,13 @@ def slice_view(self, start, stop): _CHSIZE = const(1 << _CHBITS) -class KeyV(KeyVBase): +if TYPE_CHECKING: + KeyVBaseType = KeyVBase +else: + KeyVBaseType = (KeyVBase,) + + +class KeyV(KeyVBaseType[T]): """ KeyVector abstraction Constant precomputed buffers = bytes, frozen. Same operation as normal. @@ -334,10 +363,16 @@ class KeyV(KeyVBase): __slots__ = ("current_idx", "size", "d", "mv", "const", "cur", "chunked") - def __init__(self, elems=64, buffer=None, const=False, no_init=False): + def __init__( + self, + elems: int = 64, + buffer: bytes | None = None, + const: bool = False, + no_init: bool = False, + ) -> None: super().__init__(elems) - self.d = None - self.mv = None + self.d: bytes | bytearray | list[bytearray] | None = None + self.mv: memoryview | None = None self.const = const self.cur = _ensure_dst_key() self.chunked = False @@ -352,7 +387,7 @@ def __init__(self, elems=64, buffer=None, const=False, no_init=False): if not no_init: self._set_mv() - def _set_d(self, elems): + def _set_d(self, elems: int) -> None: if elems > _CHSIZE and elems % _CHSIZE == 0: self.chunked = True gc.collect() @@ -363,8 +398,9 @@ def _set_d(self, elems): gc.collect() self.d = bytearray(32 * elems) - def _set_mv(self): + def _set_mv(self) -> None: if not self.chunked: + assert isinstance(self.d, TBYTES) self.mv = memoryview(self.d) def __getitem__(self, item): @@ -375,6 +411,7 @@ def __getitem__(self, item): if self.chunked: return self.to(item) item = self.idxize(item) + assert self.mv is not None return self.mv[item * 32 : (item + 1) * 32] def __setitem__(self, key, value): @@ -386,9 +423,10 @@ def __setitem__(self, key, value): for i in range(32): ck[i] = value[i] - def to(self, idx, buff=None, offset=0): + def to(self, idx, buff: bytearray | None = None, offset: int = 0): idx = self.idxize(idx) if self.chunked: + assert isinstance(self.d, list) memcpy( buff if buff else self.cur, offset, @@ -397,21 +435,25 @@ def to(self, idx, buff=None, offset=0): 32, ) else: + assert isinstance(self.d, (bytes, bytearray)) memcpy(buff if buff else self.cur, offset, self.d, idx << 5, 32) return buff if buff else self.cur - def read(self, idx, buff, offset=0): + def read(self, idx, buff, offset: int = 0): idx = self.idxize(idx) if self.chunked: + assert isinstance(self.d, list) memcpy(self.d[idx >> _CHBITS], (idx & (_CHSIZE - 1)) << 5, buff, offset, 32) else: + assert isinstance(self.d, bytearray) memcpy(self.d, idx << 5, buff, offset, 32) - def resize(self, nsize, chop=False, realloc=False): + def resize(self, nsize, chop: int = False, realloc: int = False): if self.size == nsize: return if self.chunked and nsize <= _CHSIZE: + assert isinstance(self.d, list) self.chunked = False # de-chunk if self.size > nsize and realloc: gc.collect() @@ -424,12 +466,14 @@ def resize(self, nsize, chop=False, realloc=False): self.d = bytearray(nsize << 5) elif self.chunked and self.size < nsize: + assert isinstance(self.d, list) if nsize % _CHSIZE != 0 or realloc or chop: raise ValueError("Unsupported") # not needed for i in range((nsize - self.size) // _CHSIZE): self.d.append(bytearray(32 * _CHSIZE)) elif self.chunked: + assert isinstance(self.d, list) if nsize % _CHSIZE != 0: raise ValueError("Unsupported") # not needed for i in range((self.size - nsize) // _CHSIZE): @@ -439,6 +483,7 @@ def resize(self, nsize, chop=False, realloc=False): self.d[i] = bytearray(self.d[i]) else: + assert isinstance(self.d, (bytes, bytearray)) if self.size > nsize and realloc: gc.collect() self.d = bytearray(self.d[: nsize << 5]) @@ -452,7 +497,7 @@ def resize(self, nsize, chop=False, realloc=False): self.size = nsize self._set_mv() - def realloc(self, nsize, collect=False): + def realloc(self, nsize, collect: int = False): self.d = None self.mv = None if collect: @@ -462,12 +507,14 @@ def realloc(self, nsize, collect=False): self.size = nsize self._set_mv() - def realloc_init_from(self, nsize, src, offset=0, collect=False): + def realloc_init_from(self, nsize, src, offset: int = 0, collect: int = False): if not isinstance(src, KeyV): raise ValueError("KeyV supported only") self.realloc(nsize, collect) if not self.chunked and not src.chunked: + assert isinstance(self.d, bytearray) + assert isinstance(src.d, (bytes, bytearray)) memcpy(self.d, 0, src.d, offset << 5, nsize << 5) elif self.chunked and not src.chunked or self.chunked and src.chunked: @@ -475,6 +522,8 @@ def realloc_init_from(self, nsize, src, offset=0, collect=False): self.read(i, src.to(i + offset)) elif not self.chunked and src.chunked: + assert isinstance(self.d, bytearray) + assert isinstance(src.d, list) for i in range(nsize >> _CHBITS): memcpy( self.d, @@ -492,7 +541,7 @@ class KeyVEval(KeyVBase): __slots__ = ("current_idx", "size", "fnc", "raw", "scalar", "buff") - def __init__(self, elems=64, src=None, raw=False, scalar=True): + def __init__(self, elems=64, src=None, raw: int = False, scalar=True): super().__init__(elems) self.fnc = src self.raw = raw @@ -500,13 +549,13 @@ def __init__(self, elems=64, src=None, raw=False, scalar=True): self.buff = ( _ensure_dst_key() if not raw - else (crypto.new_scalar() if scalar else crypto.new_point()) + else (crypto.Scalar() if scalar else crypto.Point()) ) def __getitem__(self, item): return self.fnc(self.idxize(item), self.buff) - def to(self, idx, buff=None, offset=0): + def to(self, idx, buff: bytearray | None = None, offset: int = 0): self.fnc(self.idxize(idx), self.buff) if self.raw: if offset != 0: @@ -551,7 +600,7 @@ def __init__(self, size, elem, copy=True): def __getitem__(self, item): return self.elem - def to(self, idx, buff=None, offset=0): + def to(self, idx: int, buff: bytearray, offset: int = 0): memcpy(buff, offset, self.elem, 0, 32) return buff if buff else self.elem @@ -565,7 +614,7 @@ class KeyVPrecomp(KeyVBase): __slots__ = ("current_idx", "size", "precomp_prefix", "aux_comp_fnc", "buff") - def __init__(self, size, precomp_prefix, aux_comp_fnc): + def __init__(self, size, precomp_prefix, aux_comp_fnc) -> None: super().__init__(size) self.precomp_prefix = precomp_prefix self.aux_comp_fnc = aux_comp_fnc @@ -577,7 +626,7 @@ def __getitem__(self, item): return self.precomp_prefix[item] return self.aux_comp_fnc(item, self.buff) - def to(self, idx, buff=None, offset=0): + def to(self, idx: int, buff: bytearray | None = None, offset: int = 0) -> bytearray: item = self.idxize(idx) if item < len(self.precomp_prefix): return self.precomp_prefix.to(item, buff if buff else self.buff, offset) @@ -601,16 +650,16 @@ def __init__(self, src, start, stop): def __getitem__(self, item): return self.wrapped[self.offset + self.idxize(item)] - def __setitem__(self, key, value): + def __setitem__(self, key, value) -> None: self.wrapped[self.offset + self.idxize(key)] = value - def resize(self, nsize, chop=False): + def resize(self, nsize: int, chop: bool = False) -> None: raise ValueError("Not supported") - def to(self, idx, buff=None, offset=0): + def to(self, idx, buff: bytearray | None = None, offset: int = 0): return self.wrapped.to(self.offset + self.idxize(idx), buff, offset) - def read(self, idx, buff, offset=0): + def read(self, idx, buff, offset: int = 0): return self.wrapped.read(self.offset + self.idxize(idx), buff, offset) @@ -621,11 +670,11 @@ class KeyVPowers(KeyVBase): __slots__ = ("current_idx", "size", "x", "raw", "cur", "last_idx") - def __init__(self, size, x, raw=False, **kwargs): + def __init__(self, size, x, raw: int = False, **kwargs): super().__init__(size) self.x = x if not raw else crypto.decodeint_into_noreduce(None, x) self.raw = raw - self.cur = bytearray(32) if not raw else crypto.new_scalar() + self.cur = bytearray(32) if not raw else crypto.Scalar() self.last_idx = 0 def __getitem__(self, item): @@ -656,7 +705,7 @@ def __getitem__(self, item): else: raise IndexError(f"Only linear scan allowed: {prev}, {item}") - def set_state(self, idx, val): + def set_state(self, idx: int, val): self.last_idx = idx if self.raw: return crypto.sc_copy(self.cur, val) @@ -689,23 +738,23 @@ class KeyR0(KeyVBase): "last_idx", ) - def __init__(self, size, N, aR, y, z, raw=False, **kwargs): + def __init__(self, size, N, aR, y, z, raw: int = False, **kwargs) -> None: super().__init__(size) self.N = N self.aR = aR self.raw = raw self.y = crypto.decodeint_into_noreduce(None, y) - self.yp = crypto.new_scalar() # y^{i} + self.yp = crypto.Scalar() # y^{i} self.z = crypto.decodeint_into_noreduce(None, z) - self.zt = crypto.new_scalar() # z^{2 + \floor{i/N}} - self.p2 = crypto.new_scalar() # 2^{i \% N} - self.res = crypto.new_scalar() # tmp_sc_1 + self.zt = crypto.Scalar() # z^{2 + \floor{i/N}} + self.p2 = crypto.Scalar() # 2^{i \% N} + self.res = crypto.Scalar() # tmp_sc_1 self.cur = bytearray(32) if not raw else None self.last_idx = 0 self.reset() - def reset(self): + def reset(self) -> None: crypto.decodeint_into_noreduce(self.yp, _ONE) crypto.decodeint_into_noreduce(self.p2, _ONE) crypto.sc_mul_into(self.zt, self.z, self.z) @@ -751,14 +800,14 @@ def __getitem__(self, item): crypto.encodeint_into(self.cur, self.res) return self.cur - def to(self, idx, buff=None, offset=0): + def to(self, idx, buff: bytearray | None = None, offset: int = 0): r = self[idx] if buff is None: return r return memcpy(buff, offset, r, 0, 32) -def _ensure_dst_keyvect(dst=None, size=None): +def _ensure_dst_keyvect(dst=None, size: int | None = None): if dst is None: dst = KeyV(elems=size) return dst @@ -767,7 +816,7 @@ def _ensure_dst_keyvect(dst=None, size=None): return dst -def _const_vector(val, elems=_BP_N, copy=True): +def _const_vector(val, elems=_BP_N, copy: bool = True) -> KeyVConst: return KeyVConst(elems, val, copy) @@ -798,7 +847,7 @@ def _vector_exponent_custom(A, B, a, b, dst=None, a_raw=None, b_raw=None): return dst -def _vector_powers(x, n, dst=None, dynamic=False, **kwargs): +def _vector_powers(x, n, dst=None, dynamic: int = False, **kwargs): """ r_i = x^i """ @@ -852,7 +901,7 @@ def _inner_product(a, b, dst=None): if len(a) != len(b): raise ValueError("Incompatible sizes of a and b") dst = _ensure_dst_key(dst) - crypto.sc_init_into(_tmp_sc_1, 0) + crypto.sc_copy(_tmp_sc_1, 0) for i in range(len(a)): crypto.decodeint_into_noreduce(_tmp_sc_2, a.to(i)) @@ -864,7 +913,7 @@ def _inner_product(a, b, dst=None): return dst -def _hadamard_fold(v, a, b, into=None, into_offset=0, vR=None, vRoff=0): +def _hadamard_fold(v, a, b, into=None, into_offset: int = 0, vR=None, vRoff=0): """ Folds a curvepoint array using a two way scaled Hadamard product @@ -887,7 +936,7 @@ def _hadamard_fold(v, a, b, into=None, into_offset=0, vR=None, vRoff=0): return into -def _hadamard_fold_linear(v, a, b, into=None, into_offset=0): +def _hadamard_fold_linear(v, a, b, into=None, into_offset: int = 0): """ Folds a curvepoint array using a two way scaled Hadamard product. Iterates v linearly to support linear-scan evaluated vectors (on the fly) @@ -919,7 +968,7 @@ def _hadamard_fold_linear(v, a, b, into=None, into_offset=0): return into -def _scalar_fold(v, a, b, into=None, into_offset=0): +def _scalar_fold(v, a, b, into=None, into_offset: int = 0): """ ln = len(v); h = ln // 2 v_i = a v_i + b v_{h + i} @@ -947,10 +996,10 @@ def _cross_inner_product(l0, r0, l1, r1): t1 = l0 . r1 + l1 . r0 t2 = l1 . r1 """ - sc_t1 = crypto.new_scalar() - sc_t2 = crypto.new_scalar() - tl = crypto.new_scalar() - tr = crypto.new_scalar() + sc_t1 = crypto.Scalar() + sc_t2 = crypto.Scalar() + tl = crypto.Scalar() + tr = crypto.Scalar() for i in range(len(l0)): crypto.decodeint_into_noreduce(tl, l0.to(i)) @@ -965,7 +1014,7 @@ def _cross_inner_product(l0, r0, l1, r1): _gc_iter(i) - return crypto.encodeint(sc_t1), crypto.encodeint(sc_t2) + return crypto_helpers.encodeint(sc_t1), crypto_helpers.encodeint(sc_t2) def _vector_gen(dst, size, op): @@ -988,7 +1037,7 @@ def _vector_dup(x, n, dst=None): def _hash_cache_mash(dst, hash_cache, *args): dst = _ensure_dst_key(dst) - ctx = crypto.get_keccak() + ctx = crypto_helpers.get_keccak() ctx.update(hash_cache) for x in args: @@ -1003,7 +1052,7 @@ def _hash_cache_mash(dst, hash_cache, *args): return dst -def _is_reduced(sc): +def _is_reduced(sc) -> bool: return crypto.encodeint_into(_tmp_bf_0, crypto.decodeint_into(_tmp_sc_1, sc)) == sc @@ -1020,7 +1069,9 @@ class MultiExpSequential: MultiExp holder with sequential evaluation """ - def __init__(self, size=None, points=None, point_fnc=None): + def __init__( + self, size: int | None = None, points: list | None = None, point_fnc=None + ) -> None: self.current_idx = 0 self.size = size if size else None self.points = points if points else [] @@ -1030,7 +1081,7 @@ def __init__(self, size=None, points=None, point_fnc=None): else: self.size = 0 - self.acc = crypto.identity() + self.acc = crypto.Point() self.tmp = _ensure_dst_key() def get_point(self, idx): @@ -1038,13 +1089,13 @@ def get_point(self, idx): self.point_fnc(idx, None) if idx >= len(self.points) else self.points[idx] ) - def add_pair(self, scalar, point): + def add_pair(self, scalar, point) -> None: self._acc(scalar, point) - def add_scalar(self, scalar): + def add_scalar(self, scalar) -> None: self._acc(scalar, self.get_point(self.current_idx)) - def _acc(self, scalar, point): + def _acc(self, scalar, point) -> None: crypto.decodeint_into_noreduce(_tmp_sc_1, scalar) crypto.decodepoint_into(_tmp_pt_2, point) crypto.scalarmult_into(_tmp_pt_3, _tmp_pt_2, _tmp_sc_1) @@ -1052,41 +1103,41 @@ def _acc(self, scalar, point): self.current_idx += 1 self.size += 1 - def eval(self, dst, GiHi=False): + def eval(self, dst): dst = _ensure_dst_key(dst) return crypto.encodepoint_into(dst, self.acc) -def _multiexp(dst=None, data=None, GiHi=False): - return data.eval(dst, GiHi) +def _multiexp(dst=None, data=None): + return data.eval(dst) class BulletProofBuilder: - def __init__(self): + def __init__(self) -> None: self.use_det_masks = True self.proof_sec = None # BP_GI_PRE = get_exponent(Gi[i], _XMR_H, i * 2 + 1) - self.Gprec = KeyV(buffer=crypto.tcry.BP_GI_PRE, const=True) + self.Gprec = KeyV(buffer=crypto.BP_GI_PRE, const=True) # BP_HI_PRE = get_exponent(Hi[i], _XMR_H, i * 2) - self.Hprec = KeyV(buffer=crypto.tcry.BP_HI_PRE, const=True) + self.Hprec = KeyV(buffer=crypto.BP_HI_PRE, const=True) # BP_TWO_N = vector_powers(_TWO, _BP_N); - self.twoN = KeyV(buffer=crypto.tcry.BP_TWO_N, const=True) + self.twoN = KeyV(buffer=crypto.BP_TWO_N, const=True) self.fnc_det_mask = None - self.tmp_sc_1 = crypto.new_scalar() + self.tmp_sc_1 = crypto.Scalar() self.tmp_det_buff = bytearray(64 + 1 + 4) self.gc_fnc = gc.collect self.gc_trace = None - def gc(self, *args): + def gc(self, *args) -> None: if self.gc_trace: self.gc_trace(*args) if self.gc_fnc: self.gc_fnc() - def aX_vcts(self, sv, MN): + def aX_vcts(self, sv, MN) -> tuple: num_inp = len(sv) def e_xL(idx, d=None, is_a=True): @@ -1106,10 +1157,10 @@ def e_xL(idx, d=None, is_a=True): aR = KeyVEval(MN, lambda i, d: e_xL(i, d, False)) return aL, aR - def _det_mask_init(self): + def _det_mask_init(self) -> None: memcpy(self.tmp_det_buff, 0, self.proof_sec, 0, len(self.proof_sec)) - def _det_mask(self, i, is_sL=True, dst=None): + def _det_mask(self, i, is_sL: bool = True, dst: bytearray | None = None): dst = _ensure_dst_key(dst) if self.fnc_det_mask: return self.fnc_det_mask(i, is_sL, dst) @@ -1120,21 +1171,21 @@ def _det_mask(self, i, is_sL=True, dst=None): crypto.encodeint_into(dst, self.tmp_sc_1) return dst - def _gprec_aux(self, size): + def _gprec_aux(self, size: int) -> KeyVPrecomp: return KeyVPrecomp( size, self.Gprec, lambda i, d: _get_exponent(d, _XMR_H, i * 2 + 1) ) - def _hprec_aux(self, size): + def _hprec_aux(self, size: int) -> KeyVPrecomp: return KeyVPrecomp( size, self.Hprec, lambda i, d: _get_exponent(d, _XMR_H, i * 2) ) - def _two_aux(self, size): + def _two_aux(self, size: int) -> KeyVPrecomp: # Simple recursive exponentiation from precomputed results lx = len(self.twoN) - def pow_two(i, d=None): + def pow_two(i: int, d=None): if i < lx: return self.twoN[i] @@ -1161,11 +1212,11 @@ def sR_vct(self, ln=_BP_N): else self.sX_gen(ln) ) - def sX_gen(self, ln=_BP_N): + def sX_gen(self, ln=_BP_N) -> KeyV: gc.collect() buff = bytearray(ln * 32) buff_mv = memoryview(buff) - sc = crypto.new_scalar() + sc = crypto.Scalar() for i in range(ln): crypto.random_scalar(sc) crypto.encodeint_into(buff_mv[i * 32 : (i + 1) * 32], sc) @@ -1178,15 +1229,15 @@ def vector_exponent(self, a, b, dst=None, a_raw=None, b_raw=None): def prove(self, sv, gamma): return self.prove_batch([sv], [gamma]) - def prove_setup(self, sv, gamma): + def prove_setup(self, sv, gamma) -> tuple: utils.ensure(len(sv) == len(gamma), "|sv| != |gamma|") utils.ensure(len(sv) > 0, "sv empty") - self.proof_sec = crypto.random_bytes(64) + self.proof_sec = random.bytes(64) self._det_mask_init() gc.collect() - sv = [crypto.encodeint(x) for x in sv] - gamma = [crypto.encodeint(x) for x in gamma] + sv = [crypto_helpers.encodeint(x) for x in sv] + gamma = [crypto_helpers.encodeint(x) for x in gamma] M, logM = 1, 0 while M <= _BP_M and M < len(sv): @@ -1215,7 +1266,9 @@ def prove_batch(self, sv, gamma): break return r[1] - def _prove_batch_main(self, V, gamma, aL, aR, hash_cache, logM, logN, M, N): + def _prove_batch_main( + self, V, gamma, aL, aR, hash_cache, logM, logN, M, N + ) -> tuple: logMN = logM + logN MN = M * N _hash_vct_to_scalar(hash_cache, V) @@ -1243,7 +1296,9 @@ def _prove_batch_main(self, V, gamma, aL, aR, hash_cache, logM, logN, M, N): ), ) - def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec): + def _prove_phase1( + self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec + ) -> tuple: MN = M * N # PAPER LINES 38-39, compute A = 8^{-1} ( \alpha G + \sum_{i=0}^{MN-1} a_{L,i} \Gi_i + a_{R,i} \Hi_i) @@ -1280,9 +1335,7 @@ def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec) # Polynomial construction by coefficients # l0 = aL - z r0 = ((aR + z) . ypow) + zt # l1 = sL r1 = sR . ypow - l0 = KeyVEval( - MN, lambda i, d: _sc_sub(d, aL.to(i), None, None, zc) # noqa: F821 - ) + l0 = KeyVEval(MN, lambda i, d: _sc_sub(d, aL.to(i), zc)) # noqa: F821 l1 = sL self.gc(13) @@ -1291,9 +1344,7 @@ def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec) # r1_i = s_{Ri} y^{i} r0 = KeyR0(MN, N, aR, y, z) ypow = KeyVPowers(MN, y, raw=True) - r1 = KeyVEval( - MN, lambda i, d: _sc_mul(d, sR.to(i), None, ypow[i]) # noqa: F821 - ) + r1 = KeyVEval(MN, lambda i, d: _sc_mul(d, sR.to(i), ypow[i])) # noqa: F821 del aR self.gc(14) @@ -1331,7 +1382,7 @@ def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec) # - $t = l . r$ l = _ensure_dst_keyvect(None, MN) r = _ensure_dst_keyvect(None, MN) - ts = crypto.new_scalar() + ts = crypto.Scalar() for i in range(MN): _sc_muladd(_tmp_bf_0, x, l1.to(i), l0.to(i)) l.read(i, _tmp_bf_0) @@ -1339,9 +1390,9 @@ def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec) _sc_muladd(_tmp_bf_1, x, r1.to(i), r0.to(i)) r.read(i, _tmp_bf_1) - _sc_muladd(ts, _tmp_bf_0, _tmp_bf_1, None, c_raw=ts, raw=True) + _sc_muladd(ts, _tmp_bf_0, _tmp_bf_1, ts) - t = crypto.encodeint(ts) + t = crypto_helpers.encodeint(ts) del (l0, l1, sL, sR, r0, r1, ypow, ts) self.gc(17) @@ -1354,7 +1405,7 @@ def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec) zpow = crypto.sc_mul_into(None, zc, zc) for j in range(1, len(V) + 1): - _sc_muladd(taux, None, gamma[j - 1], taux, a_raw=zpow) + _sc_muladd(taux, zpow, gamma[j - 1], taux) crypto.sc_mul_into(zpow, zpow, zc) del (zc, zpow) @@ -1371,14 +1422,14 @@ def _prove_phase1(self, N, M, logMN, V, gamma, aL, aR, hash_cache, Gprec, Hprec) return A, S, T1, T2, taux, mu, t, l, r, y, x_ip, hash_cache - def _prove_loop(self, MN, logMN, l, r, y, x_ip, hash_cache, Gprec, Hprec): + def _prove_loop(self, MN, logMN, l, r, y, x_ip, hash_cache, Gprec, Hprec) -> tuple: nprime = MN aprime = l bprime = r yinvpowL = KeyVPowers(MN, _invert(_tmp_bf_0, y), raw=True) yinvpowR = KeyVPowers(MN, _tmp_bf_0, raw=True) - tmp_pt = crypto.new_point() + tmp_pt = crypto.Point() Gprime = Gprec HprimeL = KeyVEval( @@ -1503,10 +1554,10 @@ def _prove_loop(self, MN, logMN, l, r, y, x_ip, hash_cache, Gprec, Hprec): return L, R, aprime.to(0), bprime.to(0) - def verify(self, proof): + def verify(self, proof) -> bool: return self.verify_batch([proof]) - def verify_batch(self, proofs, single_optim=True): + def verify_batch(self, proofs: list[Bulletproof], single_optim: bool = True): """ BP batch verification :param proofs: @@ -1554,8 +1605,8 @@ def verify_batch(self, proofs, single_optim=True): utils.ensure(len(proof.L) == 6 + logM, "Proof is not the expected size") MN = M * N - weight_y = crypto.encodeint(crypto.random_scalar()) - weight_z = crypto.encodeint(crypto.random_scalar()) + weight_y = crypto_helpers.encodeint(crypto.random_scalar()) + weight_z = crypto_helpers.encodeint(crypto.random_scalar()) # Reconstruct the challenges hash_cache = _hash_vct_to_scalar(None, proof.V) @@ -1587,7 +1638,7 @@ def verify_batch(self, proofs, single_optim=True): _sc_muladd(y1, tmp, weight_y, y1) weight_y8 = _init_key(weight_y) - weight_y8 = _sc_mul(None, weight_y, _EIGHT) + _sc_mul(weight_y8, weight_y, _EIGHT) muex = MultiExpSequential(points=[pt for pt in proof.V]) for j in range(len(proof.V)): @@ -1604,13 +1655,13 @@ def verify_batch(self, proofs, single_optim=True): muex.add_pair(_init_key(tmp), proof.T2) weight_z8 = _init_key(weight_z) - weight_z8 = _sc_mul(None, weight_z, _EIGHT) + _sc_mul(weight_z8, weight_z, _EIGHT) muex.add_pair(weight_z8, proof.A) _sc_mul(tmp, x, weight_z8) muex.add_pair(_init_key(tmp), proof.S) - _multiexp(tmp, muex, False) + _multiexp(tmp, muex) _add_keys(muex_acc, muex_acc, tmp) del muex @@ -1665,6 +1716,7 @@ def verify_batch(self, proofs, single_optim=True): _sc_mulsub(h_scalar, tmp, yinvpow, h_scalar) if not is_single: # ph4 + assert m_z4 is not None and m_z5 is not None m_z4.read(i, _sc_mulsub(_tmp_bf_0, g_scalar, weight_z, m_z4[i])) m_z5.read(i, _sc_mulsub(_tmp_bf_0, h_scalar, weight_z, m_z5[i])) else: @@ -1701,7 +1753,7 @@ def verify_batch(self, proofs, single_optim=True): _sc_mul(tmp, tmp, weight_z8) muex.add_scalar(tmp) - acc = _multiexp(None, muex, False) + acc = _multiexp(None, muex) _add_keys(muex_acc, muex_acc, acc) _sc_mulsub(tmp, proof.a, proof.b, proof.t) @@ -1711,14 +1763,18 @@ def verify_batch(self, proofs, single_optim=True): _sc_sub(tmp, m_y0, z1) z3p = _sc_sub(None, z3, y1) - check2 = crypto.encodepoint( - crypto.ge25519_double_scalarmult_base_vartime( - crypto.decodeint(z3p), crypto.xmr_H(), crypto.decodeint(tmp) + check2 = crypto_helpers.encodepoint( + crypto.ge25519_double_scalarmult_vartime_into( + None, + crypto.xmr_H(), + crypto_helpers.decodeint(z3p), + crypto_helpers.decodeint(tmp), ) ) _add_keys(muex_acc, muex_acc, check2) if not is_single: # ph4 + assert m_z4 is not None and m_z5 is not None muex = MultiExpSequential( point_fnc=lambda i, d: Gprec.to(i // 2) if i & 1 == 0 @@ -1727,7 +1783,7 @@ def verify_batch(self, proofs, single_optim=True): for i in range(maxMN): muex.add_scalar(m_z4[i]) muex.add_scalar(m_z5[i]) - _add_keys(muex_acc, muex_acc, _multiexp(None, muex, True)) + _add_keys(muex_acc, muex_acc, _multiexp(None, muex)) if muex_acc != _ONE: raise ValueError("Verification failure at step 2") diff --git a/core/src/apps/monero/xmr/crypto/chacha_poly.py b/core/src/apps/monero/xmr/chacha_poly.py similarity index 71% rename from core/src/apps/monero/xmr/crypto/chacha_poly.py rename to core/src/apps/monero/xmr/chacha_poly.py index 7c90995f04f..826de350dfd 100644 --- a/core/src/apps/monero/xmr/crypto/chacha_poly.py +++ b/core/src/apps/monero/xmr/chacha_poly.py @@ -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) diff --git a/core/src/apps/monero/xmr/clsag.py b/core/src/apps/monero/xmr/clsag.py new file mode 100644 index 00000000000..cabe732b969 --- /dev/null +++ b/core/src/apps/monero/xmr/clsag.py @@ -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 diff --git a/core/src/apps/monero/xmr/credentials.py b/core/src/apps/monero/xmr/credentials.py index e78fc982163..fc2039737c2 100644 --- a/core/src/apps/monero/xmr/credentials.py +++ b/core/src/apps/monero/xmr/credentials.py @@ -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, diff --git a/core/src/apps/monero/xmr/crypto/__init__.py b/core/src/apps/monero/xmr/crypto/__init__.py deleted file mode 100644 index a9d0a846ddf..00000000000 --- a/core/src/apps/monero/xmr/crypto/__init__.py +++ /dev/null @@ -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 diff --git a/core/src/apps/monero/xmr/crypto_helpers.py b/core/src/apps/monero/xmr/crypto_helpers.py new file mode 100644 index 00000000000..a66a9d10163 --- /dev/null +++ b/core/src/apps/monero/xmr/crypto_helpers.py @@ -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 diff --git a/core/src/apps/monero/xmr/keccak_hasher.py b/core/src/apps/monero/xmr/keccak_hasher.py index 0a3dc000182..dd1f64e2a4f 100644 --- a/core/src/apps/monero/xmr/keccak_hasher.py +++ b/core/src/apps/monero/xmr/keccak_hasher.py @@ -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) diff --git a/core/src/apps/monero/xmr/key_image.py b/core/src/apps/monero/xmr/key_image.py index 26c4cf092e5..305b6c7150e 100644 --- a/core/src/apps/monero/xmr/key_image.py +++ b/core/src/apps/monero/xmr/key_image.py @@ -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 diff --git a/core/src/apps/monero/xmr/mlsag.py b/core/src/apps/monero/xmr/mlsag.py deleted file mode 100644 index 6b5ec1c5f50..00000000000 --- a/core/src/apps/monero/xmr/mlsag.py +++ /dev/null @@ -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) diff --git a/core/src/apps/monero/xmr/mlsag_hasher.py b/core/src/apps/monero/xmr/mlsag_hasher.py index 77cd94c2bce..649e7b1e19d 100644 --- a/core/src/apps/monero/xmr/mlsag_hasher.py +++ b/core/src/apps/monero/xmr/mlsag_hasher.py @@ -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 @@ def rsig_val(self, p: bytes | list[bytes] | Bulletproof, raw: bool = False): 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 @@ def get_digest(self) -> bytes: 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() diff --git a/core/src/apps/monero/xmr/monero.py b/core/src/apps/monero/xmr/monero.py index a3d88fb8f28..e8d214eab99 100644 --- a/core/src/apps/monero/xmr/monero.py +++ b/core/src/apps/monero/xmr/monero.py @@ -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) # , 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) diff --git a/core/src/apps/monero/xmr/networks.py b/core/src/apps/monero/xmr/networks.py index e9ad42c8744..79f9fadb039 100644 --- a/core/src/apps/monero/xmr/networks.py +++ b/core/src/apps/monero/xmr/networks.py @@ -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}") diff --git a/core/src/apps/monero/xmr/range_signatures.py b/core/src/apps/monero/xmr/range_signatures.py index adb280716fa..dceb3f87038 100644 --- a/core/src/apps/monero/xmr/range_signatures.py +++ b/core/src/apps/monero/xmr/range_signatures.py @@ -11,35 +11,36 @@ 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) diff --git a/core/src/apps/monero/xmr/serialize/__init__.py b/core/src/apps/monero/xmr/serialize/__init__.py index 93814207d6a..c410ea0c6fc 100644 --- a/core/src/apps/monero/xmr/serialize/__init__.py +++ b/core/src/apps/monero/xmr/serialize/__init__.py @@ -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() diff --git a/core/src/apps/monero/xmr/serialize/base_types.py b/core/src/apps/monero/xmr/serialize/base_types.py index b76c10b72c3..04557a525cf 100644 --- a/core/src/apps/monero/xmr/serialize/base_types.py +++ b/core/src/apps/monero/xmr/serialize/base_types.py @@ -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 @@ 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) diff --git a/core/src/apps/monero/xmr/serialize/int_serialize.py b/core/src/apps/monero/xmr/serialize/int_serialize.py index 97b17c945c6..871faf0d0cb 100644 --- a/core/src/apps/monero/xmr/serialize/int_serialize.py +++ b/core/src/apps/monero/xmr/serialize/int_serialize.py @@ -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 diff --git a/core/src/apps/monero/xmr/serialize/message_types.py b/core/src/apps/monero/xmr/serialize/message_types.py index 21957471fbd..f3f6588d651 100644 --- a/core/src/apps/monero/xmr/serialize/message_types.py +++ b/core/src/apps/monero/xmr/serialize/message_types.py @@ -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 @@ def dump(cls, writer, elem: bytes): 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 @@ def load(cls, reader) -> bytearray: 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 @@ def dump(cls, writer, elems, elem_type=None): 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 @@ def load(cls, reader, elem_type=None): 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 @@ def __init__(self, **kwargs): __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 @@ def dump(cls, writer, msg): 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 @@ def load(cls, reader): return msg @classmethod - def f_specs(cls): + def f_specs(cls) -> XmrFspec: return () diff --git a/core/src/apps/monero/xmr/serialize/readwriter.py b/core/src/apps/monero/xmr/serialize/readwriter.py index 67eec249648..5df774f515c 100644 --- a/core/src/apps/monero/xmr/serialize/readwriter.py +++ b/core/src/apps/monero/xmr/serialize/readwriter.py @@ -4,14 +4,12 @@ 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 @@ def __init__( 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 @@ def readinto(self, buf): 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 @@ def write(self, buf): 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] diff --git a/core/src/apps/monero/xmr/serialize_messages/tx_ct_key.py b/core/src/apps/monero/xmr/serialize_messages/tx_ct_key.py index 98791976955..d1e2eb32351 100644 --- a/core/src/apps/monero/xmr/serialize_messages/tx_ct_key.py +++ b/core/src/apps/monero/xmr/serialize_messages/tx_ct_key.py @@ -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 diff --git a/core/src/apps/monero/xmr/serialize_messages/tx_prefix.py b/core/src/apps/monero/xmr/serialize_messages/tx_prefix.py index da89e77c0d6..bdf5c83330e 100644 --- a/core/src/apps/monero/xmr/serialize_messages/tx_prefix.py +++ b/core/src/apps/monero/xmr/serialize_messages/tx_prefix.py @@ -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), diff --git a/core/src/apps/monero/xmr/serialize_messages/tx_rsig_bulletproof.py b/core/src/apps/monero/xmr/serialize_messages/tx_rsig_bulletproof.py index 40604149fd7..a772a178b12 100644 --- a/core/src/apps/monero/xmr/serialize_messages/tx_rsig_bulletproof.py +++ b/core/src/apps/monero/xmr/serialize_messages/tx_rsig_bulletproof.py @@ -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), diff --git a/core/src/apps/monero/xmr/types.py b/core/src/apps/monero/xmr/types.py deleted file mode 100644 index 60cd6b5e605..00000000000 --- a/core/src/apps/monero/xmr/types.py +++ /dev/null @@ -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 diff --git a/core/src/trezor/enums/MoneroNetworkType.py b/core/src/trezor/enums/MoneroNetworkType.py new file mode 100644 index 00000000000..7f057159b2c --- /dev/null +++ b/core/src/trezor/enums/MoneroNetworkType.py @@ -0,0 +1,8 @@ +# Automatically generated by pb2py +# fmt: off +# isort:skip_file + +MAINNET = 0 +TESTNET = 1 +STAGENET = 2 +FAKECHAIN = 3 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 5b4b7478000..9e87c004393 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -456,6 +456,12 @@ class EthereumDataType(IntEnum): ARRAY = 7 STRUCT = 8 + class MoneroNetworkType(IntEnum): + MAINNET = 0 + TESTNET = 1 + STAGENET = 2 + FAKECHAIN = 3 + class NEMMosaicLevy(IntEnum): MosaicLevy_Absolute = 1 MosaicLevy_Percentile = 2 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index f45f9409cf6..e07e55a87a1 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -39,6 +39,7 @@ def __getattr__(name: str) -> Any: 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroTransactionRs 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 @@ def __init__( *, 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroAddress"]: 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroWatchKey"]: 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 @@ def __init__( *, 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroTransactionFi 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroKeyImageSyncF 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroGetTxKeyAck"] 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroLiveRefreshSt 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroMultisigKLRki 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroRingCtSig"]: 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 @@ def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["MoneroSubAddressInd 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: diff --git a/core/tests/test_apps.monero.bulletproof.py b/core/tests/test_apps.monero.bulletproof.py index 8dcec9b00b2..8535570560a 100644 --- a/core/tests/test_apps.monero.bulletproof.py +++ b/core/tests/test_apps.monero.bulletproof.py @@ -12,8 +12,8 @@ def test_1(self): 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 @@ def test_verify(self): 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 @@ def test_verify_batch_1(self): 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 @@ def test_prove_random_masks(self): 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 @@ def ctest_multiexp(self): 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]) diff --git a/core/tests/test_apps.monero.clsag.py b/core/tests/test_apps.monero.clsag.py index 148f09d1032..13446ac5592 100644 --- a/core/tests/test_apps.monero.clsag.py +++ b/core/tests/test_apps.monero.clsag.py @@ -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 @@ def __init__(self, d, c): 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 @@ def hsh_PC(x): 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 @@ def hsh_PC(x): 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 @@ def hsh_PC(x): 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 @@ def gen_clsag_test(self, ring_size=11, index=None): 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 @@ def test_monero_generated_clsag_01(self): "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 @@ def test_monero_generated_clsag_02(self): "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 @@ def test_clsag_invalid_sI(self): 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 @@ 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].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 @@ def test_clsag_invalid_Cp(self): 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): diff --git a/core/tests/test_apps.monero.crypto.py b/core/tests/test_apps.monero.crypto.py index 83aee80024c..3a4adff1bd1 100644 --- a/core/tests/test_apps.monero.crypto.py +++ b/core/tests/test_apps.monero.crypto.py @@ -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 @@ def test_scalarmult_base(self): 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 @@ def test_scalarmult_base(self): 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 @@ def test_scalarmult(self): 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 @@ def test_hash_to_scalar(self): 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 @@ def test_hash_to_point(self): 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 @@ def test_derivation_to_scalar(self): 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 @@ def test_generate_key_derivation(self): 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 @@ def test_sc_inversion(self): crypto.sc_inv_into(res, inp) self.assertEqual( - hexlify(crypto.encodeint(res)), + hexlify(crypto_helpers.encodeint(res)), b"bcf365a551e6358f3f281a6241d4a25eded60230b60a1d48c67b51a85e33d70e", ) @@ -166,39 +166,39 @@ def test_wallet_addr(self): 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 @@ def test_derive_subaddress_public_key(self): ) 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 @@ def test_public_spend(self): 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__": diff --git a/core/tests/test_apps.monero.proto.py b/core/tests/test_apps.monero.proto.py index 460cb73ff94..2d396d9599b 100644 --- a/core/tests/test_apps.monero.proto.py +++ b/core/tests/test_apps.monero.proto.py @@ -11,33 +11,53 @@ @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] diff --git a/core/tests/test_apps.monero.serializer.py b/core/tests/test_apps.monero.serializer.py index dc44b275534..4ef4828c183 100644 --- a/core/tests/test_apps.monero.serializer.py +++ b/core/tests/test_apps.monero.serializer.py @@ -10,9 +10,7 @@ ) 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 @@ def test_txin_to_key(self): 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__": diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index b292db97d88..babd689539d 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -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 @@ def __init__( *, 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 @@ def __init__( *, 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 @@ def __init__( 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 @@ def __init__( 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 @@ def __init__( 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 @@ def __init__( 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: