mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-28 17:18:29 +00:00
refactor(core/monero): Monero code cleanup
* remove support for HF12 and below * remove MLSAG support * clean up monero cryptography naming * get rid of "optional first argument" pattern, in favor of mandatory argument that is allowed to be None (and fix several bugs related to this feature) Co-authored-by: grdddj <jiri.musil06@seznam.cz> Co-authored-by: Martin Milata <martin@martinmilata.cz> Co-authored-by: matejcik <ja@matejcik.cz>
This commit is contained in:
parent
99c817bad4
commit
33c174491f
@ -5,6 +5,13 @@ package hw.trezor.messages.monero;
|
||||
option java_package = "com.satoshilabs.trezor.lib.protobuf";
|
||||
option java_outer_classname = "TrezorMessageMonero";
|
||||
|
||||
enum MoneroNetworkType {
|
||||
MAINNET = 0;
|
||||
TESTNET = 1;
|
||||
STAGENET = 2;
|
||||
FAKECHAIN = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Structure representing Monero transaction source entry, UTXO
|
||||
* @embed
|
||||
@ -24,8 +31,8 @@ message MoneroTransactionSourceEntry {
|
||||
optional uint64 idx = 1;
|
||||
optional MoneroRctKeyPublic key = 2;
|
||||
message MoneroRctKeyPublic {
|
||||
optional bytes dest = 1;
|
||||
optional bytes commitment = 2;
|
||||
required bytes dest = 1;
|
||||
required bytes commitment = 2;
|
||||
}
|
||||
}
|
||||
message MoneroMultisigKLRki {
|
||||
@ -79,7 +86,7 @@ message MoneroTransactionRsigData {
|
||||
message MoneroGetAddress {
|
||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||
optional bool show_display = 2; // Optionally show on display before sending the result
|
||||
optional uint32 network_type = 3; // Main-net / testnet / stagenet
|
||||
optional MoneroNetworkType network_type = 3 [default=MAINNET]; // Network type
|
||||
optional uint32 account = 4; // Major subaddr index
|
||||
optional uint32 minor = 5; // Minor subaddr index
|
||||
optional bytes payment_id = 6; // Payment ID for integrated address
|
||||
@ -101,7 +108,7 @@ message MoneroAddress {
|
||||
*/
|
||||
message MoneroGetWatchKey {
|
||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||
optional uint32 network_type = 2; // Main-net / testnet / stagenet
|
||||
optional MoneroNetworkType network_type = 2 [default=MAINNET]; // Network type
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,7 +128,7 @@ message MoneroWatchKey {
|
||||
message MoneroTransactionInitRequest {
|
||||
optional uint32 version = 1;
|
||||
repeated uint32 address_n = 2;
|
||||
optional uint32 network_type = 3; // Main-net / testnet / stagenet
|
||||
optional MoneroNetworkType network_type = 3 [default=MAINNET]; // Network type
|
||||
optional MoneroTransactionData tsx_data = 4;
|
||||
/**
|
||||
* Structure representing Monero initial transaction information
|
||||
@ -329,16 +336,16 @@ message MoneroTransactionFinalAck {
|
||||
* @next MoneroKeyImageExportInitAck
|
||||
*/
|
||||
message MoneroKeyImageExportInitRequest {
|
||||
optional uint64 num = 1;
|
||||
optional bytes hash = 2;
|
||||
required uint64 num = 1;
|
||||
required bytes hash = 2;
|
||||
repeated uint32 address_n = 3; // BIP-32 path to derive the key from master node
|
||||
optional uint32 network_type = 4; // Main-net / testnet / stagenet
|
||||
optional MoneroNetworkType network_type = 4 [default=MAINNET]; // network type
|
||||
repeated MoneroSubAddressIndicesList subs = 5;
|
||||
/**
|
||||
* Structure representing Monero list of sub-addresses
|
||||
*/
|
||||
message MoneroSubAddressIndicesList {
|
||||
optional uint32 account = 1;
|
||||
required uint32 account = 1;
|
||||
repeated uint32 minor_indices = 2;
|
||||
}
|
||||
}
|
||||
@ -360,10 +367,10 @@ message MoneroKeyImageSyncStepRequest {
|
||||
* Structure representing Monero UTXO for key image sync
|
||||
*/
|
||||
message MoneroTransferDetails {
|
||||
optional bytes out_key = 1;
|
||||
optional bytes tx_pub_key = 2;
|
||||
required bytes out_key = 1;
|
||||
required bytes tx_pub_key = 2;
|
||||
repeated bytes additional_tx_pub_keys = 3;
|
||||
optional uint64 internal_output_index = 4;
|
||||
required uint64 internal_output_index = 4;
|
||||
optional uint32 sub_addr_major = 5;
|
||||
optional uint32 sub_addr_minor = 6;
|
||||
}
|
||||
@ -406,12 +413,12 @@ message MoneroKeyImageSyncFinalAck {
|
||||
*/
|
||||
message MoneroGetTxKeyRequest {
|
||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||
optional uint32 network_type = 2; // Main-net / testnet / stagenet
|
||||
optional MoneroNetworkType network_type = 2 [default=MAINNET]; // network type
|
||||
|
||||
optional bytes salt1 = 3;
|
||||
optional bytes salt2 = 4;
|
||||
optional bytes tx_enc_keys = 5;
|
||||
optional bytes tx_prefix_hash = 6;
|
||||
required bytes salt1 = 3;
|
||||
required bytes salt2 = 4;
|
||||
required bytes tx_enc_keys = 5;
|
||||
required bytes tx_prefix_hash = 6;
|
||||
optional uint32 reason = 7; // reason to display for user. e.g., tx_proof
|
||||
optional bytes view_public_key = 8; // addr for derivation
|
||||
}
|
||||
@ -432,7 +439,7 @@ message MoneroGetTxKeyAck {
|
||||
*/
|
||||
message MoneroLiveRefreshStartRequest {
|
||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||
optional uint32 network_type = 2; // Main-net / testnet / stagenet
|
||||
optional MoneroNetworkType network_type = 2 [default=MAINNET]; // network type
|
||||
}
|
||||
|
||||
/**
|
||||
@ -449,11 +456,11 @@ message MoneroLiveRefreshStartAck {
|
||||
* @next MoneroLiveRefreshStepAck
|
||||
*/
|
||||
message MoneroLiveRefreshStepRequest {
|
||||
optional bytes out_key = 1;
|
||||
optional bytes recv_deriv = 2;
|
||||
optional uint64 real_out_idx = 3;
|
||||
optional uint32 sub_addr_major = 4;
|
||||
optional uint32 sub_addr_minor = 5;
|
||||
required bytes out_key = 1;
|
||||
required bytes recv_deriv = 2;
|
||||
required uint64 real_out_idx = 3;
|
||||
required uint32 sub_addr_major = 4;
|
||||
required uint32 sub_addr_minor = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
|
1
core/.changelog.d/642.changed
Normal file
1
core/.changelog.d/642.changed
Normal file
@ -0,0 +1 @@
|
||||
Refactor and cleanup of Monero code.
|
1
core/.changelog.d/642.removed
Normal file
1
core/.changelog.d/642.removed
Normal file
@ -0,0 +1 @@
|
||||
Removed support for obsolete Monero hardfork 12 and below
|
File diff suppressed because it is too large
Load Diff
@ -2,22 +2,22 @@ from typing import *
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
class Ge25519:
|
||||
class Point:
|
||||
"""
|
||||
EC point on ED25519
|
||||
"""
|
||||
def __init__(self, x: Ge25519 | bytes | None = None):
|
||||
def __init__(self, x: Point | bytes | None = None):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
class Sc25519:
|
||||
class Scalar:
|
||||
"""
|
||||
EC scalar on SC25519
|
||||
"""
|
||||
def __init__(self, x: Sc25519 | bytes | int | None = None):
|
||||
def __init__(self, x: Scalar | bytes | int | None = None):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
@ -47,92 +47,84 @@ class Hasher:
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def init256_modm(
|
||||
dst: Sc25519 | None, val: int | bytes | Sc25519
|
||||
) -> Sc25519:
|
||||
def sc_copy(
|
||||
dst: Scalar | None, val: int | bytes | Scalar
|
||||
) -> Scalar:
|
||||
"""
|
||||
Initializes Sc25519 scalar
|
||||
Initializes a scalar
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def check256_modm(val: Sc25519) -> None:
|
||||
def sc_check(val: Scalar) -> None:
|
||||
"""
|
||||
Throws exception if scalar is invalid
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def iszero256_modm(val: Sc25519) -> bool:
|
||||
def sc_iszero(val: Scalar) -> bool:
|
||||
"""
|
||||
Returns False if the scalar is zero
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def eq256_modm(a: Sc25519, b: Sc25519) -> int:
|
||||
def sc_eq(a: Scalar, b: Scalar) -> int:
|
||||
"""
|
||||
Compares scalars, returns 1 on the same value
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def get256_modm(a: Sc25519) -> int:
|
||||
"""
|
||||
Extracts 64bit integer from the scalar. Raises exception if scalar is
|
||||
bigger than 2^64
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def add256_modm(r: Sc25519 | None, a: Sc25519, b: Sc25519) -> Sc25519:
|
||||
def sc_add_into(r: Scalar | None, a: Scalar, b: Scalar) -> Scalar:
|
||||
"""
|
||||
Scalar addition
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def sub256_modm(r: Sc25519 | None, a: Sc25519, b: Sc25519) -> Sc25519:
|
||||
def sc_sub_into(r: Scalar | None, a: Scalar, b: Scalar) -> Scalar:
|
||||
"""
|
||||
Scalar subtraction
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def mul256_modm(r: Sc25519 | None, a: Sc25519, b: Sc25519) -> Sc25519:
|
||||
def sc_mul_into(r: Scalar | None, a: Scalar, b: Scalar) -> Scalar:
|
||||
"""
|
||||
Scalar multiplication
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def mulsub256_modm(
|
||||
r: Sc25519 | None, a: Sc25519, b: Sc25519, c: Sc25519
|
||||
) -> Sc25519:
|
||||
def sc_mulsub_into(
|
||||
r: Scalar | None, a: Scalar, b: Scalar, c: Scalar
|
||||
) -> Scalar:
|
||||
"""
|
||||
c - a*b
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def muladd256_modm(
|
||||
r: Sc25519 | None, a: Sc25519, b: Sc25519, c: Sc25519
|
||||
) -> Sc25519:
|
||||
def sc_muladd_into(
|
||||
r: Scalar | None, a: Scalar, b: Scalar, c: Scalar
|
||||
) -> Scalar:
|
||||
"""
|
||||
c + a*b
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def inv256_modm(r: Sc25519 | None, a: Sc25519) -> Sc25519:
|
||||
def sc_inv_into(r: Scalar | None, a: Scalar) -> Scalar:
|
||||
"""
|
||||
Scalar modular inversion
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def pack256_modm(
|
||||
r: bytes | None, a: Sc25519, offset: int | None = 0
|
||||
def encodeint_into(
|
||||
r: bytes | None, a: Scalar, offset: int | None = 0
|
||||
) -> bytes:
|
||||
"""
|
||||
Scalar compression
|
||||
@ -140,144 +132,124 @@ def pack256_modm(
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def unpack256_modm(
|
||||
r: Sc25519 | None, a: bytes, offset: int = 0
|
||||
) -> Sc25519:
|
||||
def decodeint_into(
|
||||
r: Scalar | None, a: bytes, offset: int = 0
|
||||
) -> Scalar:
|
||||
"""
|
||||
Scalar decompression
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def unpack256_modm_noreduce(
|
||||
r: Sc25519 | None, a: bytes, offset: int = 0
|
||||
) -> Sc25519:
|
||||
def decodeint_into_noreduce(
|
||||
r: Scalar | None, a: bytes, offset: int = 0
|
||||
) -> Scalar:
|
||||
"""
|
||||
Scalar decompression, raw, without modular reduction
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_set_neutral(r: Ge25519 | None) -> Ge25519:
|
||||
def identity_into(r: Point | None = None) -> Point:
|
||||
"""
|
||||
Sets neutral point
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_set_xmr_h(r: Ge25519 | None) -> Ge25519:
|
||||
def xmr_H(r: Point | None = None) -> Point:
|
||||
"""
|
||||
Sets H point
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_check(r: Ge25519) -> None:
|
||||
def ge25519_check(r: Point) -> None:
|
||||
"""
|
||||
Checks point, throws if not on curve
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_eq(a: Ge25519, b: Ge25519) -> bool:
|
||||
def point_eq(a: Point, b: Point) -> bool:
|
||||
"""
|
||||
Compares EC points
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_add(r: Ge25519 | None, a: Ge25519, b: Ge25519) -> Ge25519:
|
||||
def point_add_into(r: Point | None, a: Point, b: Point) -> Point:
|
||||
"""
|
||||
Adds EC points
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_sub(r: Ge25519 | None, a: Ge25519, b: Ge25519) -> Ge25519:
|
||||
def point_sub_into(r: Point | None, a: Point, b: Point) -> Point:
|
||||
"""
|
||||
Subtracts EC points
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_double(r: Ge25519 | None, p: Ge25519) -> Ge25519:
|
||||
"""
|
||||
EC point doubling
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_mul8(r: Ge25519 | None, p: Ge25519) -> Ge25519:
|
||||
def ge25519_mul8(r: Point | None, p: Point) -> Point:
|
||||
"""
|
||||
EC point * 8
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_double_scalarmult_vartime(
|
||||
r: Ge25519 | None, p1: Ge25519, s1: Sc25519, s2: Sc25519
|
||||
) -> Ge25519:
|
||||
def ge25519_double_scalarmult_vartime_into(
|
||||
r: Point | None, p1: Point, s1: Scalar, s2: Scalar
|
||||
) -> Point:
|
||||
"""
|
||||
s1 * G + s2 * p1
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_double_scalarmult_vartime2(
|
||||
r: Ge25519 | None,
|
||||
p1: Ge25519,
|
||||
s1: Sc25519,
|
||||
p2: Ge25519,
|
||||
s2: Sc25519,
|
||||
) -> Ge25519:
|
||||
"""
|
||||
s1 * p1 + s2 * p2
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_scalarmult_base(
|
||||
r: Ge25519 | None, s: Sc25519 | int
|
||||
) -> Ge25519:
|
||||
def scalarmult_base_into(
|
||||
r: Point | None, s: Scalar | int
|
||||
) -> Point:
|
||||
"""
|
||||
s * G
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_scalarmult(
|
||||
r: Ge25519 | None, p: Ge25519, s: Sc25519 | int
|
||||
) -> Ge25519:
|
||||
def scalarmult_into(
|
||||
r: Point | None, p: Point, s: Scalar | int
|
||||
) -> Point:
|
||||
"""
|
||||
s * p
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_pack(r: bytes, p: Ge25519, offset: int = 0) -> bytes:
|
||||
def encodepoint_into(r: bytes | None, p: Point, offset: int = 0) -> bytes:
|
||||
"""
|
||||
Point compression
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def ge25519_unpack_vartime(
|
||||
r: Ge25519 | None, buff: bytes, offset: int = 0
|
||||
) -> Ge25519:
|
||||
def decodepoint_into(
|
||||
r: Point | None, buff: bytes, offset: int = 0
|
||||
) -> Point:
|
||||
"""
|
||||
Point decompression
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def base58_addr_encode_check(tag: int, buff: bytes) -> bytes:
|
||||
def xmr_base58_addr_encode_check(tag: int, buff: bytes) -> str:
|
||||
"""
|
||||
Monero block base 58 encoding
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def base58_addr_decode_check(buff: bytes) -> tuple[bytes, int]:
|
||||
def xmr_base58_addr_decode_check(buff: bytes) -> tuple[bytes, int]:
|
||||
"""
|
||||
Monero block base 58 decoding, returning (decoded, tag) or raising on
|
||||
error.
|
||||
@ -285,30 +257,43 @@ def base58_addr_decode_check(buff: bytes) -> tuple[bytes, int]:
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_random_scalar(r: Sc25519 | None = None) -> Sc25519:
|
||||
def random_scalar(r: Scalar | None = None) -> Scalar:
|
||||
"""
|
||||
Generates a random scalar
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_fast_hash(r: bytes | None, buff: bytes, length: int, offset: int) -> bytes:
|
||||
def fast_hash_into(
|
||||
r: bytes | None,
|
||||
buff: bytes,
|
||||
length: int | None = None,
|
||||
offset: int = 0,
|
||||
) -> bytes:
|
||||
"""
|
||||
XMR fast hash
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_hash_to_ec(r: Ge25519 | None, buff: bytes, length: int, offset:
|
||||
int) -> Ge25519:
|
||||
def hash_to_point_into(
|
||||
r: Point | None,
|
||||
buff: bytes,
|
||||
length: int | None = None,
|
||||
offset: int = 0,
|
||||
) -> Point:
|
||||
"""
|
||||
XMR hashing to EC point
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_hash_to_scalar(r: Sc25519 | None, buff: bytes, length: int,
|
||||
offset: int) -> Sc25519:
|
||||
def hash_to_scalar_into(
|
||||
r: Scalar | None,
|
||||
buff: bytes,
|
||||
length: int | None = None,
|
||||
offset: int = 0,
|
||||
) -> Scalar:
|
||||
"""
|
||||
XMR hashing to EC scalar
|
||||
"""
|
||||
@ -316,8 +301,8 @@ offset: int) -> Sc25519:
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_derivation_to_scalar(
|
||||
r: Sc25519 | None, p: Ge25519, output_index: int
|
||||
) -> Sc25519:
|
||||
r: Scalar | None, p: Point, output_index: int
|
||||
) -> Scalar:
|
||||
"""
|
||||
H_s(derivation || varint(output_index))
|
||||
"""
|
||||
@ -325,8 +310,8 @@ def xmr_derivation_to_scalar(
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_generate_key_derivation(
|
||||
r: Ge25519 | None, A: Ge25519, b: Sc25519
|
||||
) -> Ge25519:
|
||||
r: Point | None, A: Point, b: Scalar
|
||||
) -> Point:
|
||||
"""
|
||||
8*(key2*key1)
|
||||
"""
|
||||
@ -334,8 +319,8 @@ def xmr_generate_key_derivation(
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_derive_private_key(
|
||||
r: Sc25519 | None, deriv: Ge25519, idx: int, base: Sc25519
|
||||
) -> Sc25519:
|
||||
r: Scalar | None, deriv: Point, idx: int, base: Scalar
|
||||
) -> Scalar:
|
||||
"""
|
||||
base + H_s(derivation || varint(output_index))
|
||||
"""
|
||||
@ -343,44 +328,26 @@ def xmr_derive_private_key(
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_derive_public_key(
|
||||
r: Ge25519 | None, deriv: Ge25519, idx: int, base: Ge25519
|
||||
) -> Ge25519:
|
||||
r: Point | None, deriv: Point, idx: int, base: Point
|
||||
) -> Point:
|
||||
"""
|
||||
H_s(derivation || varint(output_index))G + base
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_add_keys2(
|
||||
r: Ge25519 | None, a: Sc25519, b: Sc25519, B: Ge25519
|
||||
) -> Ge25519:
|
||||
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_keys2_vartime(
|
||||
r: Ge25519 | None, a: Sc25519, b: Sc25519, B: Ge25519
|
||||
) -> Ge25519:
|
||||
"""
|
||||
aG + bB, G is basepoint
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_add_keys3(
|
||||
r: Ge25519 | None, a: Sc25519, A: Ge25519, b: Sc25519, B: Ge25519
|
||||
) -> Ge25519:
|
||||
"""
|
||||
aA + bB
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_add_keys3_vartime(
|
||||
r: Ge25519 | None, a: Sc25519, A: Ge25519, b: Sc25519, B: Ge25519
|
||||
) -> Ge25519:
|
||||
def add_keys3_into(
|
||||
r: Point | None, a: Scalar, A: Point, b: Scalar, B: Point
|
||||
) -> Point:
|
||||
"""
|
||||
aA + bB
|
||||
"""
|
||||
@ -388,15 +355,15 @@ def xmr_add_keys3_vartime(
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_get_subaddress_secret_key(
|
||||
r: Sc25519 | None, major: int, minor: int, m: Sc25519
|
||||
) -> Sc25519:
|
||||
r: Scalar | None, major: int, minor: int, m: Scalar
|
||||
) -> Scalar:
|
||||
"""
|
||||
Hs(SubAddr || a || index_major || index_minor)
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-monero.h
|
||||
def xmr_gen_c(r: Ge25519 | None, a: Sc25519, amount: int) -> Ge25519:
|
||||
def gen_commitment_into(r: Point | None, a: Scalar, amount: int) -> Point:
|
||||
"""
|
||||
aG + amount * H
|
||||
"""
|
||||
@ -407,3 +374,6 @@ def ct_equals(a: bytes, b: bytes) -> bool:
|
||||
"""
|
||||
Constant time buffer comparison
|
||||
"""
|
||||
BP_GI_PRE: bytes
|
||||
BP_HI_PRE: bytes
|
||||
BP_TWO_N: bytes
|
||||
|
@ -3,7 +3,8 @@ from typing import TypeVar
|
||||
C = TypeVar("C", bound=int)
|
||||
|
||||
def const(c: C) -> C: ...
|
||||
def mem_info(verbose: bool | None = None) -> None: ...
|
||||
def mem_info(verbose: bool | int | None = None) -> None: ...
|
||||
def mem_current() -> int: ...
|
||||
def mem_total() -> int: ...
|
||||
def mem_peak() -> int: ...
|
||||
def stack_use() -> int: ...
|
||||
|
@ -434,6 +434,8 @@ if not utils.BITCOIN_ONLY:
|
||||
import trezor.enums.CardanoTxWitnessType
|
||||
trezor.enums.EthereumDataType
|
||||
import trezor.enums.EthereumDataType
|
||||
trezor.enums.MoneroNetworkType
|
||||
import trezor.enums.MoneroNetworkType
|
||||
trezor.enums.NEMImportanceTransferMode
|
||||
import trezor.enums.NEMImportanceTransferMode
|
||||
trezor.enums.NEMModificationType
|
||||
@ -592,8 +594,6 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.monero.signing.step_01_init_transaction
|
||||
apps.monero.signing.step_02_set_input
|
||||
import apps.monero.signing.step_02_set_input
|
||||
apps.monero.signing.step_03_inputs_permutation
|
||||
import apps.monero.signing.step_03_inputs_permutation
|
||||
apps.monero.signing.step_04_input_vini
|
||||
import apps.monero.signing.step_04_input_vini
|
||||
apps.monero.signing.step_05_all_inputs_set
|
||||
@ -606,22 +606,24 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.monero.signing.step_09_sign_input
|
||||
apps.monero.signing.step_10_sign_final
|
||||
import apps.monero.signing.step_10_sign_final
|
||||
apps.monero.xmr
|
||||
import apps.monero.xmr
|
||||
apps.monero.xmr.addresses
|
||||
import apps.monero.xmr.addresses
|
||||
apps.monero.xmr.bulletproof
|
||||
import apps.monero.xmr.bulletproof
|
||||
apps.monero.xmr.chacha_poly
|
||||
import apps.monero.xmr.chacha_poly
|
||||
apps.monero.xmr.clsag
|
||||
import apps.monero.xmr.clsag
|
||||
apps.monero.xmr.credentials
|
||||
import apps.monero.xmr.credentials
|
||||
apps.monero.xmr.crypto
|
||||
import apps.monero.xmr.crypto
|
||||
apps.monero.xmr.crypto.chacha_poly
|
||||
import apps.monero.xmr.crypto.chacha_poly
|
||||
apps.monero.xmr.crypto_helpers
|
||||
import apps.monero.xmr.crypto_helpers
|
||||
apps.monero.xmr.keccak_hasher
|
||||
import apps.monero.xmr.keccak_hasher
|
||||
apps.monero.xmr.key_image
|
||||
import apps.monero.xmr.key_image
|
||||
apps.monero.xmr.mlsag
|
||||
import apps.monero.xmr.mlsag
|
||||
apps.monero.xmr.mlsag_hasher
|
||||
import apps.monero.xmr.mlsag_hasher
|
||||
apps.monero.xmr.monero
|
||||
@ -650,8 +652,6 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.monero.xmr.serialize_messages.tx_prefix
|
||||
apps.monero.xmr.serialize_messages.tx_rsig_bulletproof
|
||||
import apps.monero.xmr.serialize_messages.tx_rsig_bulletproof
|
||||
apps.monero.xmr.types
|
||||
import apps.monero.xmr.types
|
||||
apps.nem
|
||||
import apps.nem
|
||||
apps.nem.get_address
|
||||
|
@ -1,3 +1,9 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import Failure
|
||||
|
||||
|
||||
if __debug__:
|
||||
import gc
|
||||
import micropython
|
||||
@ -8,7 +14,7 @@ if __debug__:
|
||||
PREV_MEM = gc.mem_free()
|
||||
CUR_MES = 0
|
||||
|
||||
def log_trace(x=None):
|
||||
def log_trace(x=None) -> None:
|
||||
log.debug(
|
||||
__name__,
|
||||
"Log trace %s, ... F: %s A: %s, S: %s",
|
||||
@ -18,7 +24,7 @@ if __debug__:
|
||||
micropython.stack_use(),
|
||||
)
|
||||
|
||||
def check_mem(x=""):
|
||||
def check_mem(x: str | int = "") -> None:
|
||||
global PREV_MEM, CUR_MES
|
||||
|
||||
gc.collect()
|
||||
@ -33,12 +39,12 @@ if __debug__:
|
||||
CUR_MES += 1
|
||||
PREV_MEM = free
|
||||
|
||||
def retit(**kwargs):
|
||||
def retit(**kwargs) -> Failure:
|
||||
from trezor.messages import Failure
|
||||
|
||||
return Failure(**kwargs)
|
||||
|
||||
async def diag(ctx, msg, **kwargs):
|
||||
async def diag(ctx, msg, **kwargs) -> Failure:
|
||||
log.debug(__name__, "----diagnostics")
|
||||
gc.collect()
|
||||
|
||||
@ -85,7 +91,7 @@ if __debug__:
|
||||
bpi.gc_fnc = gc.collect
|
||||
bpi.gc_trace = log_trace
|
||||
|
||||
vals = [crypto.sc_init((1 << 30) - 1 + 16), crypto.sc_init(22222)]
|
||||
vals = [crypto.Scalar((1 << 30) - 1 + 16), crypto.Scalar(22222)]
|
||||
masks = [crypto.random_scalar(), crypto.random_scalar()]
|
||||
check_mem("BP pre input")
|
||||
|
||||
|
@ -1,33 +1,53 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import wire
|
||||
from trezor.messages import MoneroAddress
|
||||
from trezor.ui.layouts import show_address
|
||||
|
||||
from apps.common import paths
|
||||
from apps.common.keychain import auto_keychain
|
||||
from apps.monero import misc
|
||||
from apps.monero.xmr import addresses, crypto, monero
|
||||
from apps.monero.xmr import addresses, crypto_helpers, monero
|
||||
from apps.monero.xmr.networks import net_version
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import MoneroGetAddress
|
||||
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
|
||||
@auto_keychain(__name__)
|
||||
async def get_address(ctx, msg, keychain):
|
||||
async def get_address(
|
||||
ctx: wire.Context, msg: MoneroGetAddress, keychain: Keychain
|
||||
) -> MoneroAddress:
|
||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||
|
||||
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
|
||||
addr = creds.address
|
||||
|
||||
if msg.payment_id:
|
||||
have_subaddress = msg.account is not None and msg.minor is not None
|
||||
have_payment_id = msg.payment_id is not None
|
||||
|
||||
if (msg.account is None) != (msg.minor is None):
|
||||
raise wire.ProcessError("Invalid subaddress indexes")
|
||||
|
||||
if have_payment_id and have_subaddress:
|
||||
raise wire.DataError("Subaddress cannot be integrated")
|
||||
|
||||
if have_payment_id:
|
||||
assert msg.payment_id is not None
|
||||
if len(msg.payment_id) != 8:
|
||||
raise ValueError("Invalid payment ID length")
|
||||
addr = addresses.encode_addr(
|
||||
net_version(msg.network_type, False, True),
|
||||
crypto.encodepoint(creds.spend_key_public),
|
||||
crypto.encodepoint(creds.view_key_public),
|
||||
crypto_helpers.encodepoint(creds.spend_key_public),
|
||||
crypto_helpers.encodepoint(creds.view_key_public),
|
||||
msg.payment_id,
|
||||
)
|
||||
|
||||
if msg.account or msg.minor:
|
||||
if msg.payment_id:
|
||||
raise ValueError("Subaddress cannot be integrated")
|
||||
if have_subaddress:
|
||||
assert msg.account is not None
|
||||
assert msg.minor is not None
|
||||
|
||||
pub_spend, pub_view = monero.generate_sub_address_keys(
|
||||
creds.view_key_private, creds.spend_key_public, msg.account, msg.minor
|
||||
@ -35,17 +55,17 @@ async def get_address(ctx, msg, keychain):
|
||||
|
||||
addr = addresses.encode_addr(
|
||||
net_version(msg.network_type, True, False),
|
||||
crypto.encodepoint(pub_spend),
|
||||
crypto.encodepoint(pub_view),
|
||||
crypto_helpers.encodepoint(pub_spend),
|
||||
crypto_helpers.encodepoint(pub_view),
|
||||
)
|
||||
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address=addr.decode(),
|
||||
address_qr="monero:" + addr.decode(),
|
||||
address=addr,
|
||||
address_qr="monero:" + addr,
|
||||
title=title,
|
||||
)
|
||||
|
||||
return MoneroAddress(address=addr)
|
||||
return MoneroAddress(address=addr.encode())
|
||||
|
@ -14,22 +14,26 @@ encrypted using the private spend key. Here the host sends it back
|
||||
in `MoneroGetTxKeyRequest.tx_enc_keys` to be decrypted and yet again encrypted
|
||||
using the view key, which the host possess.
|
||||
"""
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import utils
|
||||
from trezor import utils, wire
|
||||
from trezor.messages import MoneroGetTxKeyAck, MoneroGetTxKeyRequest
|
||||
|
||||
from apps.common import paths
|
||||
from apps.common.keychain import auto_keychain
|
||||
from apps.monero import layout, misc
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr.crypto import chacha_poly
|
||||
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers
|
||||
|
||||
_GET_TX_KEY_REASON_TX_KEY = 0
|
||||
_GET_TX_KEY_REASON_TX_DERIVATION = 1
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
|
||||
@auto_keychain(__name__)
|
||||
async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
|
||||
async def get_tx_keys(
|
||||
ctx: wire.Context, msg: MoneroGetTxKeyRequest, keychain: Keychain
|
||||
) -> MoneroGetTxKeyAck:
|
||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||
|
||||
do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION
|
||||
@ -41,7 +45,7 @@ async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
|
||||
creds.spend_key_private,
|
||||
msg.tx_prefix_hash,
|
||||
msg.salt1,
|
||||
crypto.decodeint(msg.salt2),
|
||||
crypto_helpers.decodeint(msg.salt2),
|
||||
)
|
||||
|
||||
# the plain_buff first stores the tx_priv_keys as decrypted here
|
||||
@ -52,10 +56,13 @@ async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
|
||||
|
||||
# If return only derivations do tx_priv * view_pub
|
||||
if do_deriv:
|
||||
if msg.view_public_key is None:
|
||||
raise wire.DataError("Missing view public key")
|
||||
|
||||
plain_buff = bytearray(plain_buff)
|
||||
view_pub = crypto.decodepoint(msg.view_public_key)
|
||||
tx_priv = crypto.new_scalar()
|
||||
derivation = crypto.new_point()
|
||||
view_pub = crypto_helpers.decodepoint(msg.view_public_key)
|
||||
tx_priv = crypto.Scalar()
|
||||
derivation = crypto.Point()
|
||||
n_keys = len(plain_buff) // 32
|
||||
for c in range(n_keys):
|
||||
crypto.decodeint_into(tx_priv, plain_buff, 32 * c)
|
||||
|
@ -1,19 +1,29 @@
|
||||
from trezor.messages import MoneroGetWatchKey, MoneroWatchKey
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.messages import MoneroWatchKey
|
||||
|
||||
from apps.common import paths
|
||||
from apps.common.keychain import auto_keychain
|
||||
from apps.monero import layout, misc
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto_helpers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.wire import Context
|
||||
from trezor.messages import MoneroGetWatchKey
|
||||
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
|
||||
@auto_keychain(__name__)
|
||||
async def get_watch_only(ctx, msg: MoneroGetWatchKey, keychain):
|
||||
async def get_watch_only(
|
||||
ctx: Context, msg: MoneroGetWatchKey, keychain: Keychain
|
||||
) -> MoneroWatchKey:
|
||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||
|
||||
await layout.require_confirm_watchkey(ctx)
|
||||
|
||||
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
|
||||
address = creds.address
|
||||
watch_key = crypto.encodeint(creds.view_key_private)
|
||||
watch_key = crypto_helpers.encodeint(creds.view_key_private)
|
||||
|
||||
return MoneroWatchKey(watch_key=watch_key, address=address)
|
||||
return MoneroWatchKey(watch_key=watch_key, address=address.encode())
|
||||
|
@ -1,6 +1,8 @@
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import log, wire
|
||||
from trezor.crypto import random
|
||||
from trezor.messages import (
|
||||
MoneroExportedKeyImage,
|
||||
MoneroKeyImageExportInitAck,
|
||||
@ -13,20 +15,28 @@ from trezor.messages import (
|
||||
from apps.common import paths
|
||||
from apps.common.keychain import auto_keychain
|
||||
from apps.monero import layout, misc
|
||||
from apps.monero.xmr import crypto, key_image, monero
|
||||
from apps.monero.xmr.crypto import chacha_poly
|
||||
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers, key_image, monero
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import MoneroKeyImageExportInitRequest
|
||||
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
from .xmr.credentials import AccountCreds
|
||||
|
||||
|
||||
@auto_keychain(__name__)
|
||||
async def key_image_sync(ctx, msg, keychain):
|
||||
async def key_image_sync(
|
||||
ctx: wire.Context, msg: MoneroKeyImageExportInitRequest, keychain: Keychain
|
||||
) -> MoneroKeyImageSyncFinalAck:
|
||||
state = KeyImageSync()
|
||||
|
||||
res = await _init_step(state, ctx, msg, keychain)
|
||||
while state.current_output + 1 < state.num_outputs:
|
||||
msg = await ctx.call(res, MoneroKeyImageSyncStepRequest)
|
||||
res = await _sync_step(state, ctx, msg)
|
||||
step = await ctx.call(res, MoneroKeyImageSyncStepRequest)
|
||||
res = await _sync_step(state, ctx, step)
|
||||
gc.collect()
|
||||
msg = await ctx.call(res, MoneroKeyImageSyncFinalRequest)
|
||||
await ctx.call(res, MoneroKeyImageSyncFinalRequest)
|
||||
res = await _final_step(state, ctx)
|
||||
|
||||
return res
|
||||
@ -36,14 +46,19 @@ class KeyImageSync:
|
||||
def __init__(self):
|
||||
self.current_output = -1
|
||||
self.num_outputs = 0
|
||||
self.expected_hash = None
|
||||
self.enc_key = None
|
||||
self.creds = None
|
||||
self.expected_hash = b""
|
||||
self.enc_key = b""
|
||||
self.creds: AccountCreds | None = None
|
||||
self.subaddresses = {}
|
||||
self.hasher = crypto.get_keccak()
|
||||
self.hasher = crypto_helpers.get_keccak()
|
||||
|
||||
|
||||
async def _init_step(s, ctx, msg, keychain):
|
||||
async def _init_step(
|
||||
s: KeyImageSync,
|
||||
ctx: wire.Context,
|
||||
msg: MoneroKeyImageExportInitRequest,
|
||||
keychain: Keychain,
|
||||
) -> MoneroKeyImageExportInitAck:
|
||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||
|
||||
s.creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
|
||||
@ -52,7 +67,7 @@ async def _init_step(s, ctx, msg, keychain):
|
||||
|
||||
s.num_outputs = msg.num
|
||||
s.expected_hash = msg.hash
|
||||
s.enc_key = crypto.random_bytes(32)
|
||||
s.enc_key = random.bytes(32)
|
||||
|
||||
for sub in msg.subs:
|
||||
monero.compute_subaddresses(
|
||||
@ -62,7 +77,11 @@ async def _init_step(s, ctx, msg, keychain):
|
||||
return MoneroKeyImageExportInitAck()
|
||||
|
||||
|
||||
async def _sync_step(s, ctx, tds):
|
||||
async def _sync_step(
|
||||
s: KeyImageSync, ctx: wire.Context, tds: MoneroKeyImageSyncStepRequest
|
||||
) -> MoneroKeyImageSyncStepAck:
|
||||
assert s.creds is not None
|
||||
|
||||
if not tds.tdis:
|
||||
raise wire.DataError("Empty")
|
||||
|
||||
@ -99,7 +118,7 @@ async def _sync_step(s, ctx, tds):
|
||||
return MoneroKeyImageSyncStepAck(kis=kis)
|
||||
|
||||
|
||||
async def _final_step(s, ctx):
|
||||
async def _final_step(s, ctx: wire.Context) -> MoneroKeyImageSyncFinalAck:
|
||||
if s.current_output + 1 != s.num_outputs:
|
||||
raise wire.DataError("Invalid number of outputs")
|
||||
|
||||
|
@ -14,18 +14,21 @@ DUMMY_PAYMENT_ID = b"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.signing.state import State
|
||||
from trezor.enums import MoneroNetworkType
|
||||
from trezor.messages import (
|
||||
MoneroTransactionData,
|
||||
MoneroTransactionDestinationEntry,
|
||||
)
|
||||
from trezor.wire import Context
|
||||
|
||||
from .signing.state import State
|
||||
|
||||
|
||||
def _format_amount(value):
|
||||
def _format_amount(value: int) -> str:
|
||||
return f"{strings.format_amount(value, 12)} XMR"
|
||||
|
||||
|
||||
async def require_confirm_watchkey(ctx):
|
||||
async def require_confirm_watchkey(ctx: Context) -> None:
|
||||
await confirm_action(
|
||||
ctx,
|
||||
"get_watchkey",
|
||||
@ -37,7 +40,7 @@ async def require_confirm_watchkey(ctx):
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_keyimage_sync(ctx):
|
||||
async def require_confirm_keyimage_sync(ctx: Context) -> None:
|
||||
await confirm_action(
|
||||
ctx,
|
||||
"key_image_sync",
|
||||
@ -49,7 +52,7 @@ async def require_confirm_keyimage_sync(ctx):
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_live_refresh(ctx):
|
||||
async def require_confirm_live_refresh(ctx: Context) -> None:
|
||||
await confirm_action(
|
||||
ctx,
|
||||
"live_refresh",
|
||||
@ -61,7 +64,7 @@ async def require_confirm_live_refresh(ctx):
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_tx_key(ctx, export_key=False):
|
||||
async def require_confirm_tx_key(ctx: Context, export_key: bool = False) -> None:
|
||||
if export_key:
|
||||
description = "Do you really want to export tx_key?"
|
||||
else:
|
||||
@ -78,8 +81,11 @@ async def require_confirm_tx_key(ctx, export_key=False):
|
||||
|
||||
|
||||
async def require_confirm_transaction(
|
||||
ctx, state: State, tsx_data: MoneroTransactionData, network_type: int
|
||||
):
|
||||
ctx: Context,
|
||||
state: State,
|
||||
tsx_data: MoneroTransactionData,
|
||||
network_type: MoneroNetworkType,
|
||||
) -> None:
|
||||
"""
|
||||
Ask for confirmation from user.
|
||||
"""
|
||||
@ -87,8 +93,6 @@ async def require_confirm_transaction(
|
||||
|
||||
outputs = tsx_data.outputs
|
||||
change_idx = get_change_addr_idx(outputs, tsx_data.change_dts)
|
||||
has_integrated = bool(tsx_data.integrated_indices)
|
||||
has_payment = bool(tsx_data.payment_id)
|
||||
|
||||
if tsx_data.unlock_time != 0:
|
||||
await _require_confirm_unlock_time(ctx, tsx_data.unlock_time)
|
||||
@ -100,13 +104,17 @@ async def require_confirm_transaction(
|
||||
is_dummy = change_idx is None and dst.amount == 0 and len(outputs) == 2
|
||||
if is_dummy:
|
||||
continue # Dummy output does not need confirmation
|
||||
if has_integrated and idx in tsx_data.integrated_indices:
|
||||
if tsx_data.integrated_indices and idx in tsx_data.integrated_indices:
|
||||
cur_payment = tsx_data.payment_id
|
||||
else:
|
||||
cur_payment = None
|
||||
await _require_confirm_output(ctx, dst, network_type, cur_payment)
|
||||
|
||||
if has_payment and not has_integrated and tsx_data.payment_id != DUMMY_PAYMENT_ID:
|
||||
if (
|
||||
tsx_data.payment_id
|
||||
and not tsx_data.integrated_indices
|
||||
and tsx_data.payment_id != DUMMY_PAYMENT_ID
|
||||
):
|
||||
await _require_confirm_payment_id(ctx, tsx_data.payment_id)
|
||||
|
||||
await _require_confirm_fee(ctx, tsx_data.fee)
|
||||
@ -114,8 +122,11 @@ async def require_confirm_transaction(
|
||||
|
||||
|
||||
async def _require_confirm_output(
|
||||
ctx, dst: MoneroTransactionDestinationEntry, network_type: int, payment_id: bytes
|
||||
):
|
||||
ctx: Context,
|
||||
dst: MoneroTransactionDestinationEntry,
|
||||
network_type: MoneroNetworkType,
|
||||
payment_id: bytes | None,
|
||||
) -> None:
|
||||
"""
|
||||
Single transaction destination confirmation
|
||||
"""
|
||||
@ -129,14 +140,14 @@ async def _require_confirm_output(
|
||||
|
||||
await confirm_output(
|
||||
ctx,
|
||||
address=addr.decode(),
|
||||
address=addr,
|
||||
amount=_format_amount(dst.amount),
|
||||
font_amount=ui.BOLD,
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
|
||||
|
||||
async def _require_confirm_payment_id(ctx, payment_id: bytes):
|
||||
async def _require_confirm_payment_id(ctx: Context, payment_id: bytes) -> None:
|
||||
await confirm_blob(
|
||||
ctx,
|
||||
"confirm_payment_id",
|
||||
@ -146,7 +157,7 @@ async def _require_confirm_payment_id(ctx, payment_id: bytes):
|
||||
)
|
||||
|
||||
|
||||
async def _require_confirm_fee(ctx, fee):
|
||||
async def _require_confirm_fee(ctx: Context, fee: int) -> None:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"confirm_final",
|
||||
@ -158,7 +169,7 @@ async def _require_confirm_fee(ctx, fee):
|
||||
)
|
||||
|
||||
|
||||
async def _require_confirm_unlock_time(ctx, unlock_time):
|
||||
async def _require_confirm_unlock_time(ctx: Context, unlock_time: int) -> None:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"confirm_locktime",
|
||||
@ -170,12 +181,12 @@ async def _require_confirm_unlock_time(ctx, unlock_time):
|
||||
|
||||
|
||||
class TransactionStep(ui.Component):
|
||||
def __init__(self, state, info):
|
||||
def __init__(self, state: State, info: list[str]) -> None:
|
||||
super().__init__()
|
||||
self.state = state
|
||||
self.info = info
|
||||
|
||||
def on_render(self):
|
||||
def on_render(self) -> None:
|
||||
state = self.state
|
||||
info = self.info
|
||||
ui.header("Signing transaction", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE)
|
||||
@ -187,12 +198,12 @@ class TransactionStep(ui.Component):
|
||||
|
||||
|
||||
class KeyImageSyncStep(ui.Component):
|
||||
def __init__(self, current, total_num):
|
||||
def __init__(self, current: int, total_num: int) -> None:
|
||||
super().__init__()
|
||||
self.current = current
|
||||
self.total_num = total_num
|
||||
|
||||
def on_render(self):
|
||||
def on_render(self) -> None:
|
||||
current = self.current
|
||||
total_num = self.total_num
|
||||
ui.header("Syncing", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE)
|
||||
@ -201,11 +212,11 @@ class KeyImageSyncStep(ui.Component):
|
||||
|
||||
|
||||
class LiveRefreshStep(ui.Component):
|
||||
def __init__(self, current):
|
||||
def __init__(self, current: int) -> None:
|
||||
super().__init__()
|
||||
self.current = current
|
||||
|
||||
def on_render(self):
|
||||
def on_render(self) -> None:
|
||||
current = self.current
|
||||
ui.header("Refreshing", ui.ICON_SEND, ui.TITLE_GREY, ui.BG, ui.BLUE)
|
||||
p = (1000 * current // 8) % 1000
|
||||
@ -215,13 +226,11 @@ class LiveRefreshStep(ui.Component):
|
||||
)
|
||||
|
||||
|
||||
async def transaction_step(state: State, step: int, sub_step: int | None = None):
|
||||
async def transaction_step(state: State, step: int, sub_step: int = 0) -> None:
|
||||
if step == 0:
|
||||
info = ["Signing..."]
|
||||
elif step == state.STEP_INP:
|
||||
info = ["Processing inputs", f"{sub_step + 1}/{state.input_count}"]
|
||||
elif step == state.STEP_PERM:
|
||||
info = ["Sorting..."]
|
||||
elif step == state.STEP_VINI:
|
||||
info = ["Hashing inputs", f"{sub_step + 1}/{state.input_count}"]
|
||||
elif step == state.STEP_ALL_IN:
|
||||
@ -239,13 +248,13 @@ async def transaction_step(state: State, step: int, sub_step: int | None = None)
|
||||
await Popup(TransactionStep(state, info))
|
||||
|
||||
|
||||
async def keyimage_sync_step(ctx, current, total_num):
|
||||
async def keyimage_sync_step(ctx: Context, current: int | None, total_num: int) -> None:
|
||||
if current is None:
|
||||
return
|
||||
await Popup(KeyImageSyncStep(current, total_num))
|
||||
|
||||
|
||||
async def live_refresh_step(ctx, current):
|
||||
async def live_refresh_step(ctx: Context, current: int | None) -> None:
|
||||
if current is None:
|
||||
return
|
||||
await Popup(LiveRefreshStep(current))
|
||||
|
@ -1,4 +1,5 @@
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import storage.cache
|
||||
from trezor import log
|
||||
@ -6,7 +7,6 @@ from trezor.enums import MessageType
|
||||
from trezor.messages import (
|
||||
MoneroLiveRefreshFinalAck,
|
||||
MoneroLiveRefreshStartAck,
|
||||
MoneroLiveRefreshStartRequest,
|
||||
MoneroLiveRefreshStepAck,
|
||||
MoneroLiveRefreshStepRequest,
|
||||
)
|
||||
@ -14,40 +14,49 @@ from trezor.messages import (
|
||||
from apps.common import paths
|
||||
from apps.common.keychain import auto_keychain
|
||||
from apps.monero import layout, misc
|
||||
from apps.monero.xmr import crypto, key_image, monero
|
||||
from apps.monero.xmr.crypto import chacha_poly
|
||||
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers, key_image, monero
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import MoneroLiveRefreshStartRequest
|
||||
from trezor.wire import Context
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
from .xmr.credentials import AccountCreds
|
||||
|
||||
|
||||
@auto_keychain(__name__)
|
||||
async def live_refresh(ctx, msg: MoneroLiveRefreshStartRequest, keychain):
|
||||
async def live_refresh(
|
||||
ctx: Context, msg: MoneroLiveRefreshStartRequest, keychain: Keychain
|
||||
) -> MoneroLiveRefreshFinalAck:
|
||||
state = LiveRefreshState()
|
||||
|
||||
res = await _init_step(state, ctx, msg, keychain)
|
||||
while True:
|
||||
msg = await ctx.call_any(
|
||||
step = await ctx.call_any(
|
||||
res,
|
||||
MessageType.MoneroLiveRefreshStepRequest,
|
||||
MessageType.MoneroLiveRefreshFinalRequest,
|
||||
)
|
||||
del res
|
||||
if msg.MESSAGE_WIRE_TYPE == MessageType.MoneroLiveRefreshStepRequest:
|
||||
res = await _refresh_step(state, ctx, msg)
|
||||
if MoneroLiveRefreshStepRequest.is_type_of(step):
|
||||
res = await _refresh_step(state, ctx, step)
|
||||
else:
|
||||
return MoneroLiveRefreshFinalAck()
|
||||
gc.collect()
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class LiveRefreshState:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.current_output = 0
|
||||
self.creds = None
|
||||
self.creds: AccountCreds | None = None
|
||||
|
||||
|
||||
async def _init_step(
|
||||
s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStartRequest, keychain
|
||||
):
|
||||
s: LiveRefreshState,
|
||||
ctx: Context,
|
||||
msg: MoneroLiveRefreshStartRequest,
|
||||
keychain: Keychain,
|
||||
) -> MoneroLiveRefreshStartAck:
|
||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||
|
||||
if not storage.cache.get(storage.cache.APP_MONERO_LIVE_REFRESH):
|
||||
@ -59,7 +68,11 @@ async def _init_step(
|
||||
return MoneroLiveRefreshStartAck()
|
||||
|
||||
|
||||
async def _refresh_step(s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStepRequest):
|
||||
async def _refresh_step(
|
||||
s: LiveRefreshState, ctx: Context, msg: MoneroLiveRefreshStepRequest
|
||||
) -> MoneroLiveRefreshStepAck:
|
||||
assert s.creds is not None
|
||||
|
||||
buff = bytearray(32 * 3)
|
||||
buff_mv = memoryview(buff)
|
||||
|
||||
@ -74,14 +87,14 @@ async def _refresh_step(s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStepRequ
|
||||
# If subaddr:
|
||||
# spend_priv += Hs("SubAddr" || view_key_private || major || minor)
|
||||
# out_key = spend_priv * G, KI: spend_priv * Hp(out_key)
|
||||
out_key = crypto.decodepoint(msg.out_key)
|
||||
recv_deriv = crypto.decodepoint(msg.recv_deriv)
|
||||
out_key = crypto_helpers.decodepoint(msg.out_key)
|
||||
recv_deriv = crypto_helpers.decodepoint(msg.recv_deriv)
|
||||
received_index = msg.sub_addr_major, msg.sub_addr_minor
|
||||
spend_priv, ki = monero.generate_tx_spend_and_key_image(
|
||||
s.creds, out_key, recv_deriv, msg.real_out_idx, received_index
|
||||
)
|
||||
|
||||
ki_enc = crypto.encodepoint(ki)
|
||||
ki_enc = crypto_helpers.encodepoint(ki)
|
||||
sig = key_image.generate_ring_signature(ki_enc, ki, [out_key], spend_priv, 0, False)
|
||||
del spend_priv # spend_priv never leaves the device
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519
|
||||
from apps.common.keychain import Keychain
|
||||
from apps.common.paths import Bip32Path
|
||||
|
||||
from trezor.enums import MoneroNetworkType
|
||||
|
||||
from .xmr.crypto import Scalar
|
||||
from .xmr.credentials import AccountCreds
|
||||
|
||||
|
||||
def get_creds(keychain, address_n=None, network_type=None):
|
||||
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
|
||||
from trezor.crypto import random
|
||||
from apps.monero.xmr import crypto_helpers
|
||||
|
||||
salt = crypto.random_bytes(32)
|
||||
passwd = crypto.keccak_2hash(crypto.encodeint(view_key_private) + tx_prefix_hash)
|
||||
tx_key = crypto.compute_hmac(salt, passwd)
|
||||
salt = random.bytes(32)
|
||||
passwd = crypto_helpers.keccak_2hash(
|
||||
crypto_helpers.encodeint(view_key_private) + tx_prefix_hash
|
||||
)
|
||||
tx_key = crypto_helpers.compute_hmac(salt, passwd)
|
||||
return tx_key, salt
|
||||
|
@ -1,4 +1,5 @@
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import log, utils, wire
|
||||
from trezor.enums import MessageType
|
||||
@ -6,9 +7,15 @@ from trezor.enums import MessageType
|
||||
from apps.common.keychain import auto_keychain
|
||||
from apps.monero.signing.state import State
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import MoneroTransactionFinalAck
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
|
||||
@auto_keychain(__name__)
|
||||
async def sign_tx(ctx, received_msg, keychain):
|
||||
async def sign_tx(
|
||||
ctx: wire.Context, received_msg, keychain: Keychain
|
||||
) -> MoneroTransactionFinalAck:
|
||||
state = State(ctx)
|
||||
mods = utils.unimport_begin()
|
||||
|
||||
@ -34,7 +41,7 @@ async def sign_tx(ctx, received_msg, keychain):
|
||||
return result_msg
|
||||
|
||||
|
||||
async def sign_tx_dispatch(state, msg, keychain):
|
||||
async def sign_tx_dispatch(state: State, msg, keychain: Keychain) -> tuple:
|
||||
if msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInitRequest:
|
||||
from apps.monero.signing import step_01_init_transaction
|
||||
|
||||
@ -57,14 +64,6 @@ async def sign_tx_dispatch(state, msg, keychain):
|
||||
),
|
||||
)
|
||||
|
||||
elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInputsPermutationRequest:
|
||||
from apps.monero.signing import step_03_inputs_permutation
|
||||
|
||||
return (
|
||||
await step_03_inputs_permutation.tsx_inputs_permutation(state, msg.perm),
|
||||
(MessageType.MoneroTransactionInputViniRequest,),
|
||||
)
|
||||
|
||||
elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInputViniRequest:
|
||||
from apps.monero.signing import step_04_input_vini
|
||||
|
||||
|
@ -16,8 +16,7 @@ class NotEnoughOutputsError(wire.DataError):
|
||||
class RctType:
|
||||
"""
|
||||
There are several types of monero Ring Confidential Transactions
|
||||
like RCTTypeFull and RCTTypeSimple but currently we use only Bulletproof2
|
||||
like RCTTypeFull and RCTTypeSimple but currently we use only CLSAG
|
||||
"""
|
||||
|
||||
Bulletproof2 = 4
|
||||
CLSAG = 5
|
||||
|
@ -3,10 +3,9 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import utils
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto, crypto_helpers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519
|
||||
from trezor.messages import (
|
||||
MoneroTransactionDestinationEntry,
|
||||
MoneroTransactionSourceEntry,
|
||||
@ -20,7 +19,10 @@ _BUILD_KEY_BUFFER = bytearray(_SECRET_LENGTH + _DISCRIMINATOR_LENGTH + _INDEX_LE
|
||||
|
||||
|
||||
def _build_key(
|
||||
secret, discriminator=None, index: int = None, out: bytes = None
|
||||
secret: bytes,
|
||||
discriminator: bytes,
|
||||
index: int | None = None,
|
||||
out: bytes | None = None,
|
||||
) -> bytes:
|
||||
"""
|
||||
Creates an unique-purpose key
|
||||
@ -48,81 +50,81 @@ def _build_key(
|
||||
offset += 1
|
||||
index = shifted
|
||||
|
||||
return crypto.keccak_2hash(key_buff, out)
|
||||
return crypto_helpers.keccak_2hash(key_buff, out)
|
||||
|
||||
|
||||
def hmac_key_txin(key_hmac, idx: int) -> bytes:
|
||||
def hmac_key_txin(key_hmac: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
(TxSourceEntry[i] || tx.vin[i]) hmac key
|
||||
"""
|
||||
return _build_key(key_hmac, b"txin", idx)
|
||||
|
||||
|
||||
def hmac_key_txin_comm(key_hmac, idx: int) -> bytes:
|
||||
def hmac_key_txin_comm(key_hmac: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
pseudo_outputs[i] hmac key. Pedersen commitment for inputs.
|
||||
"""
|
||||
return _build_key(key_hmac, b"txin-comm", idx)
|
||||
|
||||
|
||||
def hmac_key_txdst(key_hmac, idx: int) -> bytes:
|
||||
def hmac_key_txdst(key_hmac: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
TxDestinationEntry[i] hmac key
|
||||
"""
|
||||
return _build_key(key_hmac, b"txdest", idx)
|
||||
|
||||
|
||||
def hmac_key_txout(key_hmac, idx: int) -> bytes:
|
||||
def hmac_key_txout(key_hmac: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
(TxDestinationEntry[i] || tx.vout[i]) hmac key
|
||||
"""
|
||||
return _build_key(key_hmac, b"txout", idx)
|
||||
|
||||
|
||||
def hmac_key_txout_asig(key_hmac, idx: int) -> bytes:
|
||||
def hmac_key_txout_asig(key_hmac: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
rsig[i] hmac key. Range signature HMAC
|
||||
"""
|
||||
return _build_key(key_hmac, b"txout-asig", idx)
|
||||
|
||||
|
||||
def enc_key_txin_alpha(key_enc, idx: int) -> bytes:
|
||||
def enc_key_txin_alpha(key_enc: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
Chacha20Poly1305 encryption key for alpha[i] used in Pedersen commitment in pseudo_outs[i]
|
||||
"""
|
||||
return _build_key(key_enc, b"txin-alpha", idx)
|
||||
|
||||
|
||||
def enc_key_spend(key_enc, idx: int) -> bytes:
|
||||
def enc_key_spend(key_enc: bytes, idx: int) -> bytes:
|
||||
"""
|
||||
Chacha20Poly1305 encryption key for alpha[i] used in Pedersen commitment in pseudo_outs[i]
|
||||
"""
|
||||
return _build_key(key_enc, b"txin-spend", idx)
|
||||
|
||||
|
||||
def enc_key_cout(key_enc, idx: int = None) -> bytes:
|
||||
def enc_key_cout(key_enc: bytes, idx: int | None = None) -> bytes:
|
||||
"""
|
||||
Chacha20Poly1305 encryption key for multisig C values from MLASG.
|
||||
"""
|
||||
return _build_key(key_enc, b"cout", idx)
|
||||
|
||||
|
||||
def key_signature(master, idx: int, is_iv=False) -> bytes:
|
||||
def key_signature(master: bytes, idx: int, is_iv: bool = False) -> bytes:
|
||||
"""
|
||||
Generates signature offloading related offloading keys
|
||||
"""
|
||||
return _build_key(master, b"sig-iv" if is_iv else b"sig-key", idx)
|
||||
|
||||
|
||||
def det_comm_masks(key_enc, idx: int) -> Sc25519:
|
||||
def det_comm_masks(key_enc: bytes, idx: int) -> crypto.Scalar:
|
||||
"""
|
||||
Deterministic output commitment masks
|
||||
"""
|
||||
return crypto.decodeint(_build_key(key_enc, b"out-mask", idx))
|
||||
return crypto_helpers.decodeint(_build_key(key_enc, b"out-mask", idx))
|
||||
|
||||
|
||||
def gen_hmac_vini(
|
||||
key, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, idx: int
|
||||
key: bytes, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, idx: int
|
||||
) -> bytes:
|
||||
"""
|
||||
Computes hmac (TxSourceEntry[i] || tx.vin[i])
|
||||
@ -151,12 +153,12 @@ def gen_hmac_vini(
|
||||
kwriter.write(vini_bin)
|
||||
|
||||
hmac_key_vini = hmac_key_txin(key, idx)
|
||||
hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest())
|
||||
hmac_vini = crypto_helpers.compute_hmac(hmac_key_vini, kwriter.get_digest())
|
||||
return hmac_vini
|
||||
|
||||
|
||||
def gen_hmac_vouti(
|
||||
key, dst_entr: MoneroTransactionDestinationEntry, tx_out_bin: bytes, idx: int
|
||||
key: bytes, dst_entr: MoneroTransactionDestinationEntry, tx_out_bin: bytes, idx: int
|
||||
) -> bytes:
|
||||
"""
|
||||
Generates HMAC for (TxDestinationEntry[i] || tx.vout[i])
|
||||
@ -169,12 +171,12 @@ def gen_hmac_vouti(
|
||||
kwriter.write(tx_out_bin)
|
||||
|
||||
hmac_key_vouti = hmac_key_txout(key, idx)
|
||||
hmac_vouti = crypto.compute_hmac(hmac_key_vouti, kwriter.get_digest())
|
||||
hmac_vouti = crypto_helpers.compute_hmac(hmac_key_vouti, kwriter.get_digest())
|
||||
return hmac_vouti
|
||||
|
||||
|
||||
def gen_hmac_tsxdest(
|
||||
key, dst_entr: MoneroTransactionDestinationEntry, idx: int
|
||||
key: bytes, dst_entr: MoneroTransactionDestinationEntry, idx: int
|
||||
) -> bytes:
|
||||
"""
|
||||
Generates HMAC for TxDestinationEntry[i]
|
||||
@ -186,7 +188,7 @@ def gen_hmac_tsxdest(
|
||||
kwriter.write(protobuf.dump_message_buffer(dst_entr))
|
||||
|
||||
hmac_key = hmac_key_txdst(key, idx)
|
||||
hmac_tsxdest = crypto.compute_hmac(hmac_key, kwriter.get_digest())
|
||||
hmac_tsxdest = crypto_helpers.compute_hmac(hmac_key, kwriter.get_digest())
|
||||
return hmac_tsxdest
|
||||
|
||||
|
||||
|
@ -7,8 +7,10 @@ from trezor import log
|
||||
from apps.monero.xmr import crypto
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Ge25519, Sc25519
|
||||
from trezor.wire import Context
|
||||
from apps.monero.xmr.crypto import Point, Scalar
|
||||
from apps.monero.xmr.credentials import AccountCreds
|
||||
from trezor.messages import MoneroTransactionDestinationEntry
|
||||
|
||||
Subaddresses = dict[bytes, tuple[int, int]]
|
||||
|
||||
@ -17,14 +19,13 @@ class State:
|
||||
|
||||
STEP_INIT = const(0)
|
||||
STEP_INP = const(100)
|
||||
STEP_PERM = const(200)
|
||||
STEP_VINI = const(300)
|
||||
STEP_ALL_IN = const(350)
|
||||
STEP_OUT = const(400)
|
||||
STEP_ALL_OUT = const(500)
|
||||
STEP_SIGN = const(600)
|
||||
|
||||
def __init__(self, ctx):
|
||||
def __init__(self, ctx: Context) -> None:
|
||||
from apps.monero.xmr.keccak_hasher import KeccakXmrArchive
|
||||
from apps.monero.xmr.mlsag_hasher import PreMlsagHasher
|
||||
|
||||
@ -51,8 +52,8 @@ class State:
|
||||
- for subaddresses the `r` is commonly denoted as `s`, however it is still just a random number
|
||||
- the keys are used to derive the one time address and its keys (P = H(A*r)*G + B)
|
||||
"""
|
||||
self.tx_priv: Sc25519 = None
|
||||
self.tx_pub: Ge25519 = None
|
||||
self.tx_priv: Scalar | None = None
|
||||
self.tx_pub: Point | None = None
|
||||
|
||||
"""
|
||||
In some cases when subaddresses are used we need more tx_keys
|
||||
@ -64,58 +65,54 @@ class State:
|
||||
self.client_version = 0
|
||||
self.hard_fork = 12
|
||||
|
||||
self.input_count = 0
|
||||
self.input_count: int | None = 0
|
||||
self.output_count = 0
|
||||
self.progress_total = 0
|
||||
self.progress_cur = 0
|
||||
|
||||
self.output_change = None
|
||||
self.fee = 0
|
||||
self.output_change: "MoneroTransactionDestinationEntry" | None = None
|
||||
self.fee: int | None = 0
|
||||
self.tx_type = 0
|
||||
|
||||
# wallet sub-address major index
|
||||
self.account_idx = 0
|
||||
self.account_idx: int | None = 0
|
||||
|
||||
# contains additional tx keys if need_additional_tx_keys is True
|
||||
self.additional_tx_private_keys: list[Sc25519] = []
|
||||
self.additional_tx_public_keys: list[bytes] = []
|
||||
self.additional_tx_private_keys: list[Scalar] = []
|
||||
self.additional_tx_public_keys: list[bytes] | None = []
|
||||
|
||||
# currently processed input/output index
|
||||
self.current_input_index = -1
|
||||
self.current_output_index = -1
|
||||
self.current_output_index: int | None = -1
|
||||
self.is_processing_offloaded = False
|
||||
|
||||
# for pseudo_out recomputation from new mask
|
||||
self.input_last_amount = 0
|
||||
self.input_last_amount: int | None = 0
|
||||
|
||||
self.summary_inputs_money = 0
|
||||
self.summary_outs_money = 0
|
||||
self.summary_inputs_money: int | None = 0
|
||||
self.summary_outs_money: int | None = 0
|
||||
|
||||
# output commitments
|
||||
self.output_pk_commitments: list[bytes] = []
|
||||
self.output_pk_commitments: list[bytes] | None = []
|
||||
|
||||
self.output_amounts: list[int] = []
|
||||
self.output_amounts: list[int] | None = []
|
||||
# output *range proof* masks. HP10+ makes them deterministic.
|
||||
self.output_masks: list[Sc25519] = []
|
||||
self.output_masks: list[Scalar] | None = []
|
||||
|
||||
# the range proofs are calculated in batches, this denotes the grouping
|
||||
self.rsig_grouping: list[int] = []
|
||||
self.rsig_grouping: list[int] | None = []
|
||||
# is range proof computing offloaded or not
|
||||
self.rsig_offload = False
|
||||
self.rsig_offload: bool | None = False
|
||||
|
||||
# sum of all inputs' pseudo out masks
|
||||
self.sumpouts_alphas: Sc25519 = crypto.sc_0()
|
||||
self.sumpouts_alphas: Scalar = crypto.Scalar(0)
|
||||
# sum of all output' pseudo out masks
|
||||
self.sumout: Sc25519 = crypto.sc_0()
|
||||
self.sumout: Scalar = crypto.Scalar(0)
|
||||
|
||||
self.subaddresses: Subaddresses = {}
|
||||
self.subaddresses: Subaddresses | None = {}
|
||||
|
||||
# TX_EXTRA_NONCE extra field for tx.extra, due to sort_tx_extra()
|
||||
self.extra_nonce = None
|
||||
|
||||
# contains an array where each item denotes the input's position
|
||||
# (inputs are sorted by key images)
|
||||
self.source_permutation: list[int] = []
|
||||
self.extra_nonce: bytes | None = None
|
||||
|
||||
# Last key image seen. Used for input permutation correctness check
|
||||
self.last_ki: bytes | None = None
|
||||
@ -124,14 +121,14 @@ class State:
|
||||
self.opening_key: bytes | None = None
|
||||
|
||||
# Step transition automaton
|
||||
self.last_step = self.STEP_INIT
|
||||
self.last_step: int | None = self.STEP_INIT
|
||||
|
||||
"""
|
||||
Tx prefix hasher/hash. We use the hasher to incrementally hash and then
|
||||
store the final hash in tx_prefix_hash.
|
||||
See Monero-Trezor documentation section 3.3 for more details.
|
||||
"""
|
||||
self.tx_prefix_hasher = KeccakXmrArchive()
|
||||
self.tx_prefix_hasher: KeccakXmrArchive | None = KeccakXmrArchive()
|
||||
self.tx_prefix_hash: bytes | None = None
|
||||
|
||||
"""
|
||||
@ -139,10 +136,10 @@ class State:
|
||||
Contains tx_prefix_hash.
|
||||
See Monero-Trezor documentation section 3.3 for more details.
|
||||
"""
|
||||
self.full_message_hasher = PreMlsagHasher()
|
||||
self.full_message_hasher: PreMlsagHasher | None = PreMlsagHasher()
|
||||
self.full_message: bytes | None = None
|
||||
|
||||
def mem_trace(self, x=None, collect=False):
|
||||
def mem_trace(self, x=None, collect: bool = False) -> None:
|
||||
if __debug__:
|
||||
log.debug(
|
||||
__name__,
|
||||
|
@ -7,10 +7,9 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero import layout, misc, signing
|
||||
from apps.monero.signing.state import State
|
||||
from apps.monero.xmr import crypto, monero
|
||||
from apps.monero.xmr import crypto, crypto_helpers, monero
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519, Ge25519
|
||||
from trezor.messages import (
|
||||
MoneroAccountPublicAddress,
|
||||
MoneroTransactionData,
|
||||
@ -34,16 +33,17 @@ async def init_transaction(
|
||||
|
||||
state.creds = misc.get_creds(keychain, address_n, network_type)
|
||||
state.client_version = tsx_data.client_version or 0
|
||||
if state.client_version == 0:
|
||||
if state.client_version < 3:
|
||||
raise ValueError("Client version not supported")
|
||||
|
||||
state.fee = state.fee if state.fee > 0 else 0
|
||||
state.tx_priv = crypto.random_scalar()
|
||||
state.tx_pub = crypto.scalarmult_base(state.tx_priv)
|
||||
state.tx_pub = crypto.scalarmult_base_into(None, state.tx_priv)
|
||||
state.mem_trace(1)
|
||||
|
||||
state.input_count = tsx_data.num_inputs
|
||||
state.output_count = len(tsx_data.outputs)
|
||||
assert state.input_count is not None
|
||||
state.progress_total = 4 + 3 * state.input_count + state.output_count
|
||||
state.progress_cur = 0
|
||||
|
||||
@ -58,16 +58,14 @@ async def init_transaction(
|
||||
|
||||
# Basic transaction parameters
|
||||
state.output_change = tsx_data.change_dts
|
||||
state.mixin = tsx_data.mixin
|
||||
state.fee = tsx_data.fee
|
||||
state.account_idx = tsx_data.account
|
||||
state.last_step = state.STEP_INIT
|
||||
state.tx_type = signing.RctType.CLSAG
|
||||
if tsx_data.hard_fork:
|
||||
state.hard_fork = tsx_data.hard_fork
|
||||
|
||||
state.tx_type = (
|
||||
signing.RctType.CLSAG if state.hard_fork >= 13 else signing.RctType.Bulletproof2
|
||||
)
|
||||
if state.hard_fork < 13:
|
||||
raise ValueError("Unsupported hard-fork version")
|
||||
|
||||
# Ensure change is correct
|
||||
_check_change(state, tsx_data.outputs)
|
||||
@ -123,7 +121,9 @@ async def init_transaction(
|
||||
return MoneroTransactionInitAck(hmacs=hmacs, rsig_data=rsig_data)
|
||||
|
||||
|
||||
def _check_subaddresses(state: State, outputs: list[MoneroTransactionDestinationEntry]):
|
||||
def _check_subaddresses(
|
||||
state: State, outputs: list[MoneroTransactionDestinationEntry]
|
||||
) -> None:
|
||||
"""
|
||||
Using subaddresses leads to a few poorly documented exceptions.
|
||||
|
||||
@ -156,8 +156,10 @@ def _check_subaddresses(state: State, outputs: list[MoneroTransactionDestination
|
||||
# we set (override) the tx pubkey to R=r*D and no additional
|
||||
# tx keys are needed
|
||||
if num_stdaddresses == 0 and num_subaddresses == 1:
|
||||
state.tx_pub = crypto.scalarmult(
|
||||
crypto.decodepoint(single_dest_subaddress.spend_public_key), state.tx_priv
|
||||
state.tx_pub = crypto.scalarmult_into(
|
||||
None,
|
||||
crypto_helpers.decodepoint(single_dest_subaddress.spend_public_key),
|
||||
state.tx_priv,
|
||||
)
|
||||
|
||||
# if a subaddress is used and either standard address is as well
|
||||
@ -178,11 +180,12 @@ def _get_primary_change_address(state: State) -> MoneroAccountPublicAddress:
|
||||
state.creds.view_key_private, state.creds.spend_key_public, state.account_idx, 0
|
||||
)
|
||||
return MoneroAccountPublicAddress(
|
||||
view_public_key=crypto.encodepoint(C), spend_public_key=crypto.encodepoint(D)
|
||||
view_public_key=crypto_helpers.encodepoint(C),
|
||||
spend_public_key=crypto_helpers.encodepoint(D),
|
||||
)
|
||||
|
||||
|
||||
def _check_rsig_data(state: State, rsig_data: MoneroTransactionRsigData):
|
||||
def _check_rsig_data(state: State, rsig_data: MoneroTransactionRsigData) -> None:
|
||||
"""
|
||||
There are two types of monero ring confidential transactions:
|
||||
1. RCTTypeFull = 1 (used if num_inputs == 1 && Borromean)
|
||||
@ -211,7 +214,7 @@ def _check_rsig_data(state: State, rsig_data: MoneroTransactionRsigData):
|
||||
_check_grouping(state)
|
||||
|
||||
|
||||
def _check_grouping(state: State):
|
||||
def _check_grouping(state: State) -> None:
|
||||
acc = 0
|
||||
for x in state.rsig_grouping:
|
||||
if x is None or x <= 0:
|
||||
@ -222,7 +225,9 @@ def _check_grouping(state: State):
|
||||
raise ValueError("Invalid grouping")
|
||||
|
||||
|
||||
def _check_change(state: State, outputs: list[MoneroTransactionDestinationEntry]):
|
||||
def _check_change(
|
||||
state: State, outputs: list[MoneroTransactionDestinationEntry]
|
||||
) -> None:
|
||||
"""
|
||||
Check if the change address in state.output_change (from `tsx_data.outputs`) is
|
||||
a) among tx outputs
|
||||
@ -269,7 +274,7 @@ def _check_change(state: State, outputs: list[MoneroTransactionDestinationEntry]
|
||||
raise signing.ChangeAddressError("Change address differs from ours")
|
||||
|
||||
|
||||
def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData):
|
||||
def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData) -> None:
|
||||
"""
|
||||
Generate master key H( H(TsxData || tx_priv) || rand )
|
||||
"""
|
||||
@ -278,16 +283,16 @@ def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData):
|
||||
|
||||
writer = get_keccak_writer()
|
||||
writer.write(protobuf.dump_message_buffer(tsx_data))
|
||||
writer.write(crypto.encodeint(state.tx_priv))
|
||||
writer.write(crypto_helpers.encodeint(state.tx_priv))
|
||||
|
||||
master_key = crypto.keccak_2hash(
|
||||
writer.get_digest() + crypto.encodeint(crypto.random_scalar())
|
||||
master_key = crypto_helpers.keccak_2hash(
|
||||
writer.get_digest() + crypto_helpers.encodeint(crypto.random_scalar())
|
||||
)
|
||||
state.key_hmac = crypto.keccak_2hash(b"hmac" + master_key)
|
||||
state.key_enc = crypto.keccak_2hash(b"enc" + master_key)
|
||||
state.key_hmac = crypto_helpers.keccak_2hash(b"hmac" + master_key)
|
||||
state.key_enc = crypto_helpers.keccak_2hash(b"enc" + master_key)
|
||||
|
||||
|
||||
def _precompute_subaddr(state: State, account: int, indices: list[int]):
|
||||
def _precompute_subaddr(state: State, account: int, indices: list[int]) -> None:
|
||||
"""
|
||||
Precomputes subaddresses for account (major) and list of indices (minors)
|
||||
Subaddresses have to be stored in encoded form - unique representation.
|
||||
@ -296,7 +301,7 @@ def _precompute_subaddr(state: State, account: int, indices: list[int]):
|
||||
monero.compute_subaddresses(state.creds, account, indices, state.subaddresses)
|
||||
|
||||
|
||||
def _process_payment_id(state: State, tsx_data: MoneroTransactionData):
|
||||
def _process_payment_id(state: State, tsx_data: MoneroTransactionData) -> None:
|
||||
"""
|
||||
Writes payment id to the `extra` field under the TX_EXTRA_NONCE = 0x02 tag.
|
||||
|
||||
@ -316,14 +321,14 @@ def _process_payment_id(state: State, tsx_data: MoneroTransactionData):
|
||||
|
||||
if not tsx_data.payment_id or len(tsx_data.payment_id) == 8:
|
||||
view_key_pub_enc = _get_key_for_payment_id_encryption(
|
||||
tsx_data, state.change_address(), state.client_version > 0
|
||||
tsx_data, state.change_address(), True
|
||||
)
|
||||
|
||||
if not tsx_data.payment_id:
|
||||
return
|
||||
|
||||
elif len(tsx_data.payment_id) == 8:
|
||||
view_key_pub = crypto.decodepoint(view_key_pub_enc)
|
||||
view_key_pub = crypto_helpers.decodepoint(view_key_pub_enc)
|
||||
payment_id_encr = _encrypt_payment_id(
|
||||
tsx_data.payment_id, view_key_pub, state.tx_priv
|
||||
)
|
||||
@ -354,7 +359,7 @@ def _process_payment_id(state: State, tsx_data: MoneroTransactionData):
|
||||
|
||||
def _get_key_for_payment_id_encryption(
|
||||
tsx_data: MoneroTransactionData,
|
||||
change_addr=None,
|
||||
change_addr: MoneroAccountPublicAddress | None = None,
|
||||
add_dummy_payment_id: bool = False,
|
||||
) -> bytes:
|
||||
"""
|
||||
@ -366,7 +371,8 @@ def _get_key_for_payment_id_encryption(
|
||||
from trezor.messages import MoneroAccountPublicAddress
|
||||
|
||||
addr = MoneroAccountPublicAddress(
|
||||
spend_public_key=crypto.NULL_KEY_ENC, view_public_key=crypto.NULL_KEY_ENC
|
||||
spend_public_key=crypto_helpers.NULL_KEY_ENC,
|
||||
view_public_key=crypto_helpers.NULL_KEY_ENC,
|
||||
)
|
||||
count = 0
|
||||
for dest in tsx_data.outputs:
|
||||
@ -390,23 +396,23 @@ def _get_key_for_payment_id_encryption(
|
||||
if count == 0 and change_addr:
|
||||
return change_addr.view_public_key
|
||||
|
||||
if addr.view_public_key == crypto.NULL_KEY_ENC:
|
||||
if addr.view_public_key == crypto_helpers.NULL_KEY_ENC:
|
||||
raise ValueError("Invalid key")
|
||||
|
||||
return addr.view_public_key
|
||||
|
||||
|
||||
def _encrypt_payment_id(
|
||||
payment_id: bytes, public_key: Ge25519, secret_key: Sc25519
|
||||
payment_id: bytes, public_key: crypto.Point, secret_key: crypto.Scalar
|
||||
) -> bytes:
|
||||
"""
|
||||
Encrypts payment_id hex.
|
||||
Used in the transaction extra. Only recipient is able to decrypt.
|
||||
"""
|
||||
derivation_p = crypto.generate_key_derivation(public_key, secret_key)
|
||||
derivation_p = crypto_helpers.generate_key_derivation(public_key, secret_key)
|
||||
derivation = bytearray(33)
|
||||
derivation = crypto.encodepoint_into(derivation, derivation_p)
|
||||
derivation[32] = 0x8D # ENCRYPTED_PAYMENT_ID_TAIL
|
||||
hash = crypto.cn_fast_hash(derivation)
|
||||
hash = crypto.fast_hash_into(None, derivation)
|
||||
pm_copy = bytearray(payment_id)
|
||||
return crypto.xor8(pm_copy, hash)
|
||||
return crypto_helpers.xor8(pm_copy, hash)
|
||||
|
@ -14,12 +14,11 @@ key derived for exactly this purpose.
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero import layout
|
||||
from apps.monero.xmr import crypto, monero, serialize
|
||||
from apps.monero.xmr import crypto, crypto_helpers, monero, serialize
|
||||
|
||||
from .state import State
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519, Ge25519
|
||||
from trezor.messages import MoneroTransactionSourceEntry
|
||||
from trezor.messages import MoneroTransactionSetInputAck
|
||||
|
||||
@ -28,7 +27,7 @@ async def set_input(
|
||||
state: State, src_entr: MoneroTransactionSourceEntry
|
||||
) -> MoneroTransactionSetInputAck:
|
||||
from trezor.messages import MoneroTransactionSetInputAck
|
||||
from apps.monero.xmr.crypto import chacha_poly
|
||||
from apps.monero.xmr import chacha_poly
|
||||
from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey
|
||||
from apps.monero.signing import offloading_keys
|
||||
|
||||
@ -49,9 +48,11 @@ async def set_input(
|
||||
|
||||
# Secrets derivation
|
||||
# the UTXO's one-time address P
|
||||
out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output].key.dest)
|
||||
out_key = crypto_helpers.decodepoint(
|
||||
src_entr.outputs[src_entr.real_output].key.dest
|
||||
)
|
||||
# the tx_pub of our UTXO stored inside its transaction
|
||||
tx_key = crypto.decodepoint(src_entr.real_out_tx_key)
|
||||
tx_key = crypto_helpers.decodepoint(src_entr.real_out_tx_key)
|
||||
additional_tx_pub_key = _get_additional_public_key(src_entr)
|
||||
|
||||
# Calculates `derivation = Ra`, private spend key `x = H(Ra||i) + b` to be able
|
||||
@ -70,7 +71,7 @@ async def set_input(
|
||||
|
||||
# Construct tx.vin
|
||||
# If multisig is used then ki in vini should be src_entr.multisig_kLRki.ki
|
||||
vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki))
|
||||
vini = TxinToKey(amount=src_entr.amount, k_image=crypto_helpers.encodepoint(ki))
|
||||
vini.key_offsets = _absolute_output_offsets_to_relative(
|
||||
[x.idx for x in src_entr.outputs]
|
||||
)
|
||||
@ -92,22 +93,22 @@ async def set_input(
|
||||
|
||||
# PseudoOuts commitment, alphas stored to state
|
||||
alpha, pseudo_out = _gen_commitment(state, src_entr.amount)
|
||||
pseudo_out = crypto.encodepoint(pseudo_out)
|
||||
pseudo_out = crypto_helpers.encodepoint(pseudo_out)
|
||||
|
||||
# The alpha is encrypted and passed back for storage
|
||||
pseudo_out_hmac = crypto.compute_hmac(
|
||||
pseudo_out_hmac = crypto_helpers.compute_hmac(
|
||||
offloading_keys.hmac_key_txin_comm(state.key_hmac, state.current_input_index),
|
||||
pseudo_out,
|
||||
)
|
||||
|
||||
alpha_enc = chacha_poly.encrypt_pack(
|
||||
offloading_keys.enc_key_txin_alpha(state.key_enc, state.current_input_index),
|
||||
crypto.encodeint(alpha),
|
||||
crypto_helpers.encodeint(alpha),
|
||||
)
|
||||
|
||||
spend_enc = chacha_poly.encrypt_pack(
|
||||
offloading_keys.enc_key_spend(state.key_enc, state.current_input_index),
|
||||
crypto.encodeint(xi),
|
||||
crypto_helpers.encodeint(xi),
|
||||
)
|
||||
|
||||
state.last_step = state.STEP_INP
|
||||
@ -127,7 +128,7 @@ async def set_input(
|
||||
)
|
||||
|
||||
|
||||
def _gen_commitment(state: State, in_amount: int) -> tuple[Sc25519, Ge25519]:
|
||||
def _gen_commitment(state: State, in_amount: int) -> tuple[crypto.Scalar, crypto.Point]:
|
||||
"""
|
||||
Computes Pedersen commitment - pseudo outs
|
||||
Here is slight deviation from the original protocol.
|
||||
@ -139,8 +140,8 @@ def _gen_commitment(state: State, in_amount: int) -> tuple[Sc25519, Ge25519]:
|
||||
Returns pseudo_out
|
||||
"""
|
||||
alpha = crypto.random_scalar()
|
||||
state.sumpouts_alphas = crypto.sc_add(state.sumpouts_alphas, alpha)
|
||||
return alpha, crypto.gen_commitment(alpha, in_amount)
|
||||
state.sumpouts_alphas = crypto.sc_add_into(None, state.sumpouts_alphas, alpha)
|
||||
return alpha, crypto.gen_commitment_into(None, alpha, in_amount)
|
||||
|
||||
|
||||
def _absolute_output_offsets_to_relative(off: list[int]) -> list[int]:
|
||||
@ -161,10 +162,10 @@ def _absolute_output_offsets_to_relative(off: list[int]) -> list[int]:
|
||||
|
||||
def _get_additional_public_key(
|
||||
src_entr: MoneroTransactionSourceEntry,
|
||||
) -> Ge25519 | None:
|
||||
) -> crypto.Point | None:
|
||||
additional_tx_pub_key = None
|
||||
if len(src_entr.real_out_additional_tx_keys) == 1: # compression
|
||||
additional_tx_pub_key = crypto.decodepoint(
|
||||
additional_tx_pub_key = crypto_helpers.decodepoint(
|
||||
src_entr.real_out_additional_tx_keys[0]
|
||||
)
|
||||
elif src_entr.real_out_additional_tx_keys:
|
||||
@ -172,7 +173,7 @@ def _get_additional_public_key(
|
||||
src_entr.real_out_additional_tx_keys
|
||||
):
|
||||
raise ValueError("Wrong number of additional derivations")
|
||||
additional_tx_pub_key = crypto.decodepoint(
|
||||
additional_tx_pub_key = crypto_helpers.decodepoint(
|
||||
src_entr.real_out_additional_tx_keys[src_entr.real_output_in_tx_index]
|
||||
)
|
||||
return additional_tx_pub_key
|
||||
|
@ -1,57 +0,0 @@
|
||||
"""
|
||||
Inputs in transaction need to be sorted by their key image, otherwise the
|
||||
transaction is rejected. The sorting is done on host and then sent here in
|
||||
the MoneroTransactionInputsPermutationRequest message.
|
||||
|
||||
The message contains just a simple array where each item stands for the
|
||||
input's position in the transaction.
|
||||
|
||||
We do not do the actual sorting here (we do not store the complete input
|
||||
data anyway, so we can't) we just save the array to the state and use
|
||||
it later when needed.
|
||||
|
||||
New protocol version (CL3) does not store the permutation. The permutation
|
||||
correctness is checked by checking the number of elements,
|
||||
HMAC correctness (host sends original sort idx) and ordering check
|
||||
on the key images. This step is skipped.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.layout import transaction_step
|
||||
|
||||
from .state import State
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import MoneroTransactionInputsPermutationAck
|
||||
|
||||
|
||||
async def tsx_inputs_permutation(
|
||||
state: State, permutation: list[int]
|
||||
) -> MoneroTransactionInputsPermutationAck:
|
||||
"""
|
||||
Set permutation on the inputs - sorted by key image on host.
|
||||
"""
|
||||
from trezor.messages import MoneroTransactionInputsPermutationAck
|
||||
|
||||
await transaction_step(state, state.STEP_PERM)
|
||||
|
||||
if state.last_step != state.STEP_INP:
|
||||
raise ValueError("Invalid state transition")
|
||||
if len(permutation) != state.input_count:
|
||||
raise ValueError("Invalid permutation size")
|
||||
if state.current_input_index != state.input_count - 1:
|
||||
raise ValueError("Invalid input count")
|
||||
_check_permutation(permutation)
|
||||
|
||||
state.source_permutation = permutation
|
||||
state.current_input_index = -1
|
||||
state.last_step = state.STEP_PERM
|
||||
|
||||
return MoneroTransactionInputsPermutationAck()
|
||||
|
||||
|
||||
def _check_permutation(permutation: list[int]):
|
||||
for n in range(len(permutation)):
|
||||
if n not in permutation:
|
||||
raise ValueError("Invalid permutation")
|
@ -28,12 +28,12 @@ async def input_vini(
|
||||
from trezor.messages import MoneroTransactionInputViniAck
|
||||
|
||||
await layout.transaction_step(state, state.STEP_VINI, state.current_input_index + 1)
|
||||
if state.last_step not in (state.STEP_INP, state.STEP_PERM, state.STEP_VINI):
|
||||
if state.last_step not in (state.STEP_INP, state.STEP_VINI):
|
||||
raise ValueError("Invalid state transition")
|
||||
if state.current_input_index >= state.input_count:
|
||||
raise ValueError("Too many inputs")
|
||||
|
||||
if state.client_version >= 2 and state.last_step < state.STEP_VINI:
|
||||
if state.last_step < state.STEP_VINI:
|
||||
state.current_input_index = -1
|
||||
state.last_ki = None
|
||||
|
||||
@ -44,9 +44,7 @@ async def input_vini(
|
||||
state.key_hmac,
|
||||
src_entr,
|
||||
vini_bin,
|
||||
state.source_permutation[state.current_input_index]
|
||||
if state.client_version <= 1
|
||||
else orig_idx,
|
||||
orig_idx,
|
||||
)
|
||||
if not crypto.ct_equals(hmac_vini_comp, vini_hmac):
|
||||
raise ValueError("HMAC is not correct")
|
||||
|
@ -27,7 +27,7 @@ async def all_inputs_set(state: State) -> MoneroTransactionAllInputsSetAck:
|
||||
raise ValueError("Invalid input count")
|
||||
|
||||
# The sum of the masks must match the input masks sum.
|
||||
state.sumout = crypto.sc_init(0)
|
||||
state.sumout = crypto.Scalar()
|
||||
state.last_step = state.STEP_ALL_IN
|
||||
resp = MoneroTransactionAllInputsSetAck()
|
||||
return resp
|
||||
|
@ -9,12 +9,11 @@ from trezor import utils
|
||||
|
||||
from apps.monero import layout, signing
|
||||
from apps.monero.signing import offloading_keys
|
||||
from apps.monero.xmr import crypto, serialize
|
||||
from apps.monero.xmr import crypto, crypto_helpers, serialize
|
||||
|
||||
from .state import State
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519, Ge25519
|
||||
from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple
|
||||
from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof
|
||||
from trezor.messages import (
|
||||
@ -170,7 +169,7 @@ def _validate(
|
||||
|
||||
def _compute_tx_keys(
|
||||
state: State, dst_entr: MoneroTransactionDestinationEntry
|
||||
) -> tuple[Ge25519, Sc25519]:
|
||||
) -> tuple[crypto.Point, crypto.Scalar]:
|
||||
"""Computes tx_out_key, amount_key"""
|
||||
|
||||
if state.is_processing_offloaded:
|
||||
@ -181,24 +180,26 @@ def _compute_tx_keys(
|
||||
# derivation = a*R or r*A or s*C
|
||||
derivation = _set_out_derivation(state, dst_entr, additional_txkey_priv)
|
||||
# amount key = H_s(derivation || i)
|
||||
amount_key = crypto.derivation_to_scalar(derivation, state.current_output_index)
|
||||
amount_key = crypto_helpers.derivation_to_scalar(
|
||||
derivation, state.current_output_index
|
||||
)
|
||||
# one-time destination address P = H_s(derivation || i)*G + B
|
||||
tx_out_key = crypto.derive_public_key(
|
||||
tx_out_key = crypto_helpers.derive_public_key(
|
||||
derivation,
|
||||
state.current_output_index,
|
||||
crypto.decodepoint(dst_entr.addr.spend_public_key),
|
||||
crypto_helpers.decodepoint(dst_entr.addr.spend_public_key),
|
||||
)
|
||||
del (derivation, additional_txkey_priv)
|
||||
|
||||
from apps.monero.xmr import monero
|
||||
|
||||
mask = monero.commitment_mask(crypto.encodeint(amount_key))
|
||||
mask = monero.commitment_mask(crypto_helpers.encodeint(amount_key))
|
||||
state.output_masks.append(mask)
|
||||
return tx_out_key, amount_key
|
||||
|
||||
|
||||
def _set_out_tx_out(
|
||||
state: State, dst_entr: MoneroTransactionDestinationEntry, tx_out_key: Ge25519
|
||||
state: State, dst_entr: MoneroTransactionDestinationEntry, tx_out_key: crypto.Point
|
||||
) -> tuple[bytes, bytes]:
|
||||
"""
|
||||
Manually serializes TxOut(0, TxoutToKey(key)) and calculates hmac.
|
||||
@ -223,7 +224,7 @@ def _set_out_tx_out(
|
||||
|
||||
def _range_proof(
|
||||
state: State, rsig_data: MoneroTransactionRsigData
|
||||
) -> tuple[MoneroTransactionRsigData, Sc25519]:
|
||||
) -> tuple[MoneroTransactionRsigData, crypto.Scalar]:
|
||||
"""
|
||||
Computes rangeproof and handles range proof offloading logic.
|
||||
|
||||
@ -257,7 +258,9 @@ def _range_proof(
|
||||
# If not last, do not proceed to the BP processing.
|
||||
if not last_in_batch:
|
||||
rsig_data_new = (
|
||||
_return_rsig_data(mask=crypto.encodeint(mask)) if offload_mask else None
|
||||
_return_rsig_data(mask=crypto_helpers.encodeint(mask))
|
||||
if offload_mask
|
||||
else None
|
||||
)
|
||||
return rsig_data_new, mask
|
||||
|
||||
@ -282,7 +285,7 @@ def _range_proof(
|
||||
|
||||
# Construct new rsig data to send back to the host.
|
||||
rsig_data_new = _return_rsig_data(
|
||||
rsig, crypto.encodeint(mask) if offload_mask else None
|
||||
rsig, crypto_helpers.encodeint(mask) if offload_mask else None
|
||||
)
|
||||
|
||||
if state.current_output_index + 1 == state.output_count and (
|
||||
@ -386,7 +389,7 @@ def _dump_rsig_bp(rsig: Bulletproof) -> bytes:
|
||||
|
||||
|
||||
def _return_rsig_data(
|
||||
rsig: bytes = None, mask: bytes = None
|
||||
rsig: bytes | None = None, mask: bytes | None = None
|
||||
) -> MoneroTransactionRsigData:
|
||||
if rsig is None and mask is None:
|
||||
return None
|
||||
@ -405,17 +408,23 @@ def _return_rsig_data(
|
||||
|
||||
|
||||
def _get_ecdh_info_and_out_pk(
|
||||
state: State, tx_out_key: Ge25519, amount: int, mask: Sc25519, amount_key: Sc25519
|
||||
state: State,
|
||||
tx_out_key: crypto.Point,
|
||||
amount: int,
|
||||
mask: crypto.Scalar,
|
||||
amount_key: crypto.Scalar,
|
||||
) -> tuple[bytes, bytes, bytes]:
|
||||
"""
|
||||
Calculates the Pedersen commitment C = aG + bH and returns it as CtKey.
|
||||
Also encodes the two items - `mask` and `amount` - into ecdh info,
|
||||
so the recipient is able to reconstruct the commitment.
|
||||
"""
|
||||
out_pk_dest = crypto.encodepoint(tx_out_key)
|
||||
out_pk_commitment = crypto.encodepoint(crypto.gen_commitment(mask, amount))
|
||||
out_pk_dest = crypto_helpers.encodepoint(tx_out_key)
|
||||
out_pk_commitment = crypto_helpers.encodepoint(
|
||||
crypto.gen_commitment_into(None, mask, amount)
|
||||
)
|
||||
crypto.sc_add_into(state.sumout, state.sumout, mask)
|
||||
ecdh_info = _ecdh_encode(amount, crypto.encodeint(amount_key))
|
||||
ecdh_info = _ecdh_encode(amount, crypto_helpers.encodeint(amount_key))
|
||||
|
||||
# Manual ECDH info serialization
|
||||
ecdh_info_bin = _serialize_ecdh(ecdh_info)
|
||||
@ -442,7 +451,7 @@ def _ecdh_hash(shared_sec: bytes) -> bytes:
|
||||
data = bytearray(38)
|
||||
data[0:6] = b"amount"
|
||||
data[6:] = shared_sec
|
||||
return crypto.cn_fast_hash(data)
|
||||
return crypto.fast_hash_into(None, data)
|
||||
|
||||
|
||||
def _ecdh_encode(amount: int, amount_key: bytes) -> EcdhTuple:
|
||||
@ -451,16 +460,16 @@ def _ecdh_encode(amount: int, amount_key: bytes) -> EcdhTuple:
|
||||
"""
|
||||
from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple
|
||||
|
||||
ecdh_info = EcdhTuple(mask=crypto.NULL_KEY_ENC, amount=bytearray(32))
|
||||
amnt = crypto.sc_init(amount)
|
||||
ecdh_info = EcdhTuple(mask=crypto_helpers.NULL_KEY_ENC, amount=bytearray(32))
|
||||
amnt = crypto.Scalar(amount)
|
||||
crypto.encodeint_into(ecdh_info.amount, amnt)
|
||||
crypto.xor8(ecdh_info.amount, _ecdh_hash(amount_key))
|
||||
crypto_helpers.xor8(ecdh_info.amount, _ecdh_hash(amount_key))
|
||||
return ecdh_info
|
||||
|
||||
|
||||
def _set_out_additional_keys(
|
||||
state: State, dst_entr: MoneroTransactionDestinationEntry
|
||||
) -> Sc25519:
|
||||
) -> crypto.Scalar:
|
||||
"""
|
||||
If needed (decided in step 1), additional tx keys are calculated
|
||||
for this particular output.
|
||||
@ -472,15 +481,15 @@ def _set_out_additional_keys(
|
||||
|
||||
if dst_entr.is_subaddress:
|
||||
# R=r*D
|
||||
additional_txkey = crypto.decodepoint(dst_entr.addr.spend_public_key)
|
||||
additional_txkey = crypto_helpers.decodepoint(dst_entr.addr.spend_public_key)
|
||||
crypto.scalarmult_into(
|
||||
additional_txkey, additional_txkey, additional_txkey_priv
|
||||
)
|
||||
else:
|
||||
# R=r*G
|
||||
additional_txkey = crypto.scalarmult_base(additional_txkey_priv)
|
||||
additional_txkey = crypto.scalarmult_base_into(None, additional_txkey_priv)
|
||||
|
||||
state.additional_tx_public_keys.append(crypto.encodepoint(additional_txkey))
|
||||
state.additional_tx_public_keys.append(crypto_helpers.encodepoint(additional_txkey))
|
||||
state.additional_tx_private_keys.append(additional_txkey_priv)
|
||||
return additional_txkey_priv
|
||||
|
||||
@ -488,8 +497,8 @@ def _set_out_additional_keys(
|
||||
def _set_out_derivation(
|
||||
state: State,
|
||||
dst_entr: MoneroTransactionDestinationEntry,
|
||||
additional_txkey_priv: Sc25519,
|
||||
) -> Ge25519:
|
||||
additional_txkey_priv: crypto.Scalar,
|
||||
) -> crypto.Point:
|
||||
"""
|
||||
Calculates derivation which is then used in the one-time address as
|
||||
`P = H(derivation)*G + B`.
|
||||
@ -504,7 +513,7 @@ def _set_out_derivation(
|
||||
change_addr = state.change_address()
|
||||
if change_addr and addr_eq(dst_entr.addr, change_addr):
|
||||
# sending change to yourself; derivation = a*R
|
||||
derivation = crypto.generate_key_derivation(
|
||||
derivation = crypto_helpers.generate_key_derivation(
|
||||
state.tx_pub, state.creds.view_key_private
|
||||
)
|
||||
|
||||
@ -514,8 +523,8 @@ def _set_out_derivation(
|
||||
deriv_priv = additional_txkey_priv
|
||||
else:
|
||||
deriv_priv = state.tx_priv
|
||||
derivation = crypto.generate_key_derivation(
|
||||
crypto.decodepoint(dst_entr.addr.view_public_key), deriv_priv
|
||||
derivation = crypto_helpers.generate_key_derivation(
|
||||
crypto_helpers.decodepoint(dst_entr.addr.view_public_key), deriv_priv
|
||||
)
|
||||
return derivation
|
||||
|
||||
|
@ -62,6 +62,7 @@ async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck:
|
||||
state.current_output_index = None
|
||||
state.current_input_index = -1
|
||||
|
||||
assert state.full_message_hasher is not None
|
||||
state.full_message = state.full_message_hasher.get_digest()
|
||||
state.full_message_hasher = None
|
||||
state.output_pk_commitments = None
|
||||
@ -79,7 +80,7 @@ async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck:
|
||||
)
|
||||
|
||||
|
||||
def _validate(state: State):
|
||||
def _validate(state: State) -> None:
|
||||
if state.last_step != state.STEP_OUT:
|
||||
raise ValueError("Invalid state transition")
|
||||
if state.current_output_index + 1 != state.output_count:
|
||||
@ -143,7 +144,7 @@ def _set_tx_extra(state: State) -> bytes:
|
||||
return extra
|
||||
|
||||
|
||||
def _set_tx_prefix(state: State, extra: bytes):
|
||||
def _set_tx_prefix(state: State, extra: bytes) -> None:
|
||||
"""
|
||||
Adds `extra` to the tx_prefix_hash, which is the last needed item,
|
||||
so the tx_prefix_hash is now complete and can be incorporated
|
||||
@ -160,7 +161,7 @@ def _set_tx_prefix(state: State, extra: bytes):
|
||||
state.full_message_hasher.set_message(state.tx_prefix_hash)
|
||||
|
||||
|
||||
def _out_pk(state: State):
|
||||
def _out_pk(state: State) -> None:
|
||||
"""
|
||||
Hashes out_pk into the full message.
|
||||
"""
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Generates a MLSAG signature for one input.
|
||||
Generates a clsag signature for one input.
|
||||
|
||||
Mask Balancing.
|
||||
Sum of input masks has to be equal to the sum of output masks.
|
||||
@ -16,7 +16,7 @@ from typing import TYPE_CHECKING
|
||||
from trezor import utils
|
||||
|
||||
from apps.monero import layout
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto, crypto_helpers
|
||||
|
||||
from .state import State
|
||||
|
||||
@ -61,11 +61,7 @@ async def sign_input(
|
||||
if pseudo_out_alpha_enc is None:
|
||||
raise ValueError("SimpleRCT requires pseudo_out's mask but none provided")
|
||||
|
||||
input_position = (
|
||||
state.source_permutation[state.current_input_index]
|
||||
if state.client_version <= 1
|
||||
else orig_idx
|
||||
)
|
||||
input_position = orig_idx
|
||||
mods = utils.unimport_begin()
|
||||
|
||||
# Check input's HMAC
|
||||
@ -88,9 +84,9 @@ async def sign_input(
|
||||
gc.collect()
|
||||
state.mem_trace(1, True)
|
||||
|
||||
from apps.monero.xmr.crypto import chacha_poly
|
||||
from apps.monero.xmr import chacha_poly
|
||||
|
||||
pseudo_out_alpha = crypto.decodeint(
|
||||
pseudo_out_alpha = crypto_helpers.decodeint(
|
||||
chacha_poly.decrypt_pack(
|
||||
offloading_keys.enc_key_txin_alpha(state.key_enc, input_position),
|
||||
bytes(pseudo_out_alpha_enc),
|
||||
@ -101,31 +97,33 @@ async def sign_input(
|
||||
if input_position + 1 == state.input_count:
|
||||
# Recompute the lash alpha so the sum holds
|
||||
state.mem_trace("Correcting alpha")
|
||||
alpha_diff = crypto.sc_sub(state.sumout, state.sumpouts_alphas)
|
||||
alpha_diff = crypto.sc_sub_into(None, state.sumout, state.sumpouts_alphas)
|
||||
crypto.sc_add_into(pseudo_out_alpha, pseudo_out_alpha, alpha_diff)
|
||||
pseudo_out_c = crypto.gen_commitment(pseudo_out_alpha, state.input_last_amount)
|
||||
pseudo_out_c = crypto.gen_commitment_into(
|
||||
None, pseudo_out_alpha, state.input_last_amount
|
||||
)
|
||||
|
||||
else:
|
||||
if input_position + 1 == state.input_count:
|
||||
utils.ensure(
|
||||
crypto.sc_eq(state.sumpouts_alphas, state.sumout), "Sum eq error"
|
||||
crypto.sc_eq(state.sumpouts_alphas, state.sumout) != 0, "Sum eq error"
|
||||
)
|
||||
|
||||
# both pseudo_out and its mask were offloaded so we need to
|
||||
# validate pseudo_out's HMAC and decrypt the alpha
|
||||
pseudo_out_hmac_comp = crypto.compute_hmac(
|
||||
pseudo_out_hmac_comp = crypto_helpers.compute_hmac(
|
||||
offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position),
|
||||
pseudo_out,
|
||||
)
|
||||
if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac):
|
||||
raise ValueError("HMAC is not correct")
|
||||
|
||||
pseudo_out_c = crypto.decodepoint(pseudo_out)
|
||||
pseudo_out_c = crypto_helpers.decodepoint(pseudo_out)
|
||||
|
||||
state.mem_trace(2, True)
|
||||
|
||||
# Spending secret
|
||||
spend_key = crypto.decodeint(
|
||||
spend_key = crypto_helpers.decodeint(
|
||||
chacha_poly.decrypt_pack(
|
||||
offloading_keys.enc_key_spend(state.key_enc, input_position),
|
||||
bytes(spend_enc),
|
||||
@ -147,28 +145,29 @@ async def sign_input(
|
||||
from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey
|
||||
|
||||
index = src_entr.real_output
|
||||
input_secret_key = CtKey(spend_key, crypto.decodeint(src_entr.mask))
|
||||
input_secret_key = CtKey(spend_key, crypto_helpers.decodeint(src_entr.mask))
|
||||
|
||||
# Private key correctness test
|
||||
utils.ensure(
|
||||
crypto.point_eq(
|
||||
crypto.decodepoint(src_entr.outputs[src_entr.real_output].key.dest),
|
||||
crypto.scalarmult_base(input_secret_key.dest),
|
||||
crypto_helpers.decodepoint(src_entr.outputs[src_entr.real_output].key.dest),
|
||||
crypto.scalarmult_base_into(None, input_secret_key.dest),
|
||||
),
|
||||
"Real source entry's destination does not equal spend key's",
|
||||
)
|
||||
utils.ensure(
|
||||
crypto.point_eq(
|
||||
crypto.decodepoint(src_entr.outputs[src_entr.real_output].key.commitment),
|
||||
crypto.gen_commitment(input_secret_key.mask, src_entr.amount),
|
||||
crypto_helpers.decodepoint(
|
||||
src_entr.outputs[src_entr.real_output].key.commitment
|
||||
),
|
||||
crypto.gen_commitment_into(None, input_secret_key.mask, src_entr.amount),
|
||||
),
|
||||
"Real source entry's mask does not equal spend key's",
|
||||
)
|
||||
|
||||
state.mem_trace(4, True)
|
||||
|
||||
from apps.monero.xmr import mlsag
|
||||
from apps.monero import signing
|
||||
from apps.monero.xmr import clsag
|
||||
|
||||
mg_buffer = []
|
||||
ring_pubkeys = [x.key for x in src_entr.outputs if x]
|
||||
@ -177,19 +176,9 @@ async def sign_input(
|
||||
|
||||
state.mem_trace(5, True)
|
||||
|
||||
if state.tx_type == signing.RctType.CLSAG:
|
||||
assert state.full_message is not None
|
||||
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(
|
||||
clsag.generate_clsag_simple(
|
||||
state.full_message,
|
||||
ring_pubkeys,
|
||||
input_secret_key,
|
||||
@ -199,13 +188,12 @@ async def sign_input(
|
||||
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)
|
||||
@ -213,7 +201,7 @@ async def sign_input(
|
||||
state.mem_trace(8, True)
|
||||
state.last_step = state.STEP_SIGN
|
||||
return MoneroTransactionSignInputAck(
|
||||
signature=mg_buffer, pseudo_out=crypto.encodepoint(pseudo_out_c)
|
||||
signature=mg_buffer, pseudo_out=crypto_helpers.encodepoint(pseudo_out_c)
|
||||
)
|
||||
|
||||
|
||||
|
@ -8,19 +8,14 @@ so we can recover it just from the transaction and the spend key.
|
||||
The private tx keys are used in other numerous Monero features.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.crypto import random
|
||||
from trezor.messages import MoneroTransactionFinalAck
|
||||
|
||||
from apps.monero import misc
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr.crypto import chacha_poly
|
||||
from apps.monero.xmr import chacha_poly, crypto, crypto_helpers
|
||||
|
||||
from .state import State
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519
|
||||
|
||||
|
||||
def final_msg(state: State) -> MoneroTransactionFinalAck:
|
||||
if state.last_step != state.STEP_SIGN:
|
||||
@ -32,8 +27,8 @@ def final_msg(state: State) -> MoneroTransactionFinalAck:
|
||||
state.creds.spend_key_private, state.tx_prefix_hash
|
||||
)
|
||||
|
||||
key_buff = crypto.encodeint(state.tx_priv) + b"".join(
|
||||
[crypto.encodeint(x) for x in state.additional_tx_private_keys]
|
||||
key_buff = crypto_helpers.encodeint(state.tx_priv) + b"".join(
|
||||
[crypto_helpers.encodeint(x) for x in state.additional_tx_private_keys]
|
||||
)
|
||||
tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff)
|
||||
state.last_step = None
|
||||
@ -48,12 +43,12 @@ def final_msg(state: State) -> MoneroTransactionFinalAck:
|
||||
|
||||
|
||||
def _compute_tx_key(
|
||||
spend_key_private: Sc25519, tx_prefix_hash: bytes
|
||||
spend_key_private: crypto.Scalar, tx_prefix_hash: bytes
|
||||
) -> tuple[bytes, bytes, bytes]:
|
||||
salt = crypto.random_bytes(32)
|
||||
salt = random.bytes(32)
|
||||
|
||||
rand_mult_num = crypto.random_scalar()
|
||||
rand_mult = crypto.encodeint(rand_mult_num)
|
||||
rand_mult = crypto_helpers.encodeint(rand_mult_num)
|
||||
|
||||
tx_key = misc.compute_tx_key(spend_key_private, tx_prefix_hash, salt, rand_mult_num)
|
||||
return tx_key, salt, rand_mult
|
||||
|
1
core/src/apps/monero/xmr/__init__.py
Normal file
1
core/src/apps/monero/xmr/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from trezor.crypto import monero as crypto # noqa: F401
|
@ -1,11 +1,11 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.crypto import monero as tcry
|
||||
from trezor.enums import MoneroNetworkType
|
||||
|
||||
from apps.monero.xmr.networks import NetworkTypes, net_version
|
||||
from apps.monero.xmr.networks import net_version
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Ge25519
|
||||
from trezor.messages import MoneroAccountPublicAddress
|
||||
from trezor.messages import MoneroTransactionDestinationEntry
|
||||
|
||||
@ -18,7 +18,7 @@ def addr_to_hash(addr: MoneroAccountPublicAddress) -> bytes:
|
||||
|
||||
|
||||
def encode_addr(
|
||||
version, spend_pub: Ge25519, view_pub: Ge25519, payment_id: bytes | None = None
|
||||
version, spend_pub: bytes, view_pub: bytes, payment_id: bytes | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Builds Monero address from public keys
|
||||
@ -40,8 +40,10 @@ def decode_addr(addr: bytes) -> tuple[int, bytes, bytes]:
|
||||
|
||||
|
||||
def public_addr_encode(
|
||||
pub_addr: MoneroAccountPublicAddress, is_sub=False, net=NetworkTypes.MAINNET
|
||||
):
|
||||
pub_addr: MoneroAccountPublicAddress,
|
||||
is_sub: bool = False,
|
||||
net: MoneroNetworkType = MoneroNetworkType.MAINNET,
|
||||
) -> str:
|
||||
"""
|
||||
Encodes public address to Monero address
|
||||
"""
|
||||
@ -52,13 +54,13 @@ def public_addr_encode(
|
||||
def classify_subaddresses(
|
||||
tx_dests: list[MoneroTransactionDestinationEntry],
|
||||
change_addr: MoneroAccountPublicAddress,
|
||||
) -> tuple[int, int, int]:
|
||||
) -> tuple[int, int, MoneroAccountPublicAddress | None]:
|
||||
"""
|
||||
Classify destination subaddresses
|
||||
"""
|
||||
num_stdaddresses = 0
|
||||
num_subaddresses = 0
|
||||
single_dest_subaddress = None
|
||||
single_dest_subaddress: MoneroAccountPublicAddress | None = None
|
||||
addr_set = set()
|
||||
for tx in tx_dests:
|
||||
if change_addr and addr_eq(change_addr, tx.addr):
|
||||
@ -75,7 +77,7 @@ def classify_subaddresses(
|
||||
return num_stdaddresses, num_subaddresses, single_dest_subaddress
|
||||
|
||||
|
||||
def addr_eq(a: MoneroAccountPublicAddress, b: MoneroAccountPublicAddress):
|
||||
def addr_eq(a: MoneroAccountPublicAddress, b: MoneroAccountPublicAddress) -> bool:
|
||||
return (
|
||||
a.spend_public_key == b.spend_public_key
|
||||
and a.view_public_key == b.view_public_key
|
||||
@ -84,8 +86,8 @@ def addr_eq(a: MoneroAccountPublicAddress, b: MoneroAccountPublicAddress):
|
||||
|
||||
def get_change_addr_idx(
|
||||
outputs: list[MoneroTransactionDestinationEntry],
|
||||
change_dts: MoneroTransactionDestinationEntry,
|
||||
) -> int:
|
||||
change_dts: MoneroTransactionDestinationEntry | None,
|
||||
) -> int | None:
|
||||
"""
|
||||
Returns ID of the change output from the change_dts and outputs
|
||||
"""
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
from trezor.crypto import chacha20poly1305 as ChaCha20Poly1305, monero, random
|
||||
|
||||
|
||||
def encrypt(key, plaintext, associated_data=None):
|
||||
def encrypt(key: bytes, plaintext: bytes, associated_data: bytes | None = None):
|
||||
"""
|
||||
Uses ChaCha20Poly1305 for encryption
|
||||
"""
|
||||
@ -14,7 +14,13 @@ def encrypt(key, plaintext, associated_data=None):
|
||||
return nonce, ciphertext + tag, b""
|
||||
|
||||
|
||||
def decrypt(key, iv, ciphertext, tag=None, associated_data=None):
|
||||
def decrypt(
|
||||
key: bytes,
|
||||
iv: bytes,
|
||||
ciphertext: bytes,
|
||||
tag: bytes | None = None,
|
||||
associated_data: bytes | None = None,
|
||||
):
|
||||
"""
|
||||
ChaCha20Poly1305 decryption
|
||||
"""
|
||||
@ -30,11 +36,11 @@ def decrypt(key, iv, ciphertext, tag=None, associated_data=None):
|
||||
return plaintext
|
||||
|
||||
|
||||
def encrypt_pack(key, plaintext, associated_data=None):
|
||||
def encrypt_pack(key: bytes, plaintext: bytes, associated_data: bytes | None = None):
|
||||
b = encrypt(key, plaintext, associated_data)
|
||||
return b[0] + b[1]
|
||||
|
||||
|
||||
def decrypt_pack(key, ciphertext):
|
||||
def decrypt_pack(key: bytes, ciphertext: bytes):
|
||||
cp = memoryview(ciphertext)
|
||||
return decrypt(key, cp[:12], cp[12:], None)
|
243
core/src/apps/monero/xmr/clsag.py
Normal file
243
core/src/apps/monero/xmr/clsag.py
Normal file
@ -0,0 +1,243 @@
|
||||
"""
|
||||
Multilayer Linkable Spontaneous Anonymous Group (MLSAG)
|
||||
Optimized versions with incremental hashing.
|
||||
|
||||
See https://eprint.iacr.org/2015/1098.pdf for details.
|
||||
Also explained in From Zero to Monero section 3.3 and 5.
|
||||
|
||||
----------
|
||||
|
||||
Please note, that the MLSAG code is written in a generic manner,
|
||||
where it is designed for multiple public keys (aka inputs). In another
|
||||
words, MLSAG should be used to sign multiple inputs, but that is currently
|
||||
not the case of Monero, where the inputs are signed one by one.
|
||||
So the public keys matrix has always two rows (one for public keys,
|
||||
one for commitments), although the algorithm is designed for `n` rows.
|
||||
|
||||
This has one unfortunate effect where `rows` is always equal to 2 and
|
||||
dsRows always to 1, but the algorithm is still written as the numbers
|
||||
are arbitrary. That's why there are loops such as `for i in range(dsRows)`
|
||||
where it is run only once currently.
|
||||
|
||||
----------
|
||||
|
||||
Also note, that the matrix of public keys is indexed by columns first.
|
||||
This is because the code was ported from the official Monero client,
|
||||
which is written in C++ and where it does have some memory advantages.
|
||||
|
||||
For ring size = 3 and one input the matrix M will look like this:
|
||||
|------------------------|------------------------|------------------------|
|
||||
| public key 0 | public key 1 | public key 2 |
|
||||
| cmt 0 - pseudo_out cmt | cmt 1 - pseudo_out cmt | cmt 2 - pseudo_out cmt |
|
||||
|
||||
and `sk` is equal to:
|
||||
|--------------|-----------------------------------------------------|
|
||||
| private key* | input secret key's mask - pseudo_out's mask (alpha) |
|
||||
|
||||
* corresponding to one of the public keys (`index` denotes which one)
|
||||
|
||||
----------
|
||||
|
||||
Mostly ported from official Monero client, but also inspired by Mininero.
|
||||
Author: Dusan Klinec, ph4r05, 2018
|
||||
"""
|
||||
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr import crypto, crypto_helpers
|
||||
from apps.monero.xmr.serialize import int_serialize
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, TypeGuard, TypeVar
|
||||
|
||||
from .serialize_messages.tx_ct_key import CtKey
|
||||
from trezor.messages import MoneroRctKeyPublic
|
||||
|
||||
KeyM = list[list[bytes]]
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
def list_of_type(lst: list[Any], typ: type[T]) -> TypeGuard[list[T]]:
|
||||
...
|
||||
|
||||
|
||||
_HASH_KEY_CLSAG_ROUND = b"CLSAG_round\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
_HASH_KEY_CLSAG_AGG_0 = b"CLSAG_agg_0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
_HASH_KEY_CLSAG_AGG_1 = b"CLSAG_agg_1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
|
||||
def generate_clsag_simple(
|
||||
message: bytes,
|
||||
pubs: list[MoneroRctKeyPublic],
|
||||
in_sk: CtKey,
|
||||
a: crypto.Scalar,
|
||||
cout: crypto.Point,
|
||||
index: int,
|
||||
mg_buff: list[bytearray],
|
||||
) -> list[bytes]:
|
||||
"""
|
||||
CLSAG for RctType.Simple
|
||||
https://eprint.iacr.org/2019/654.pdf
|
||||
|
||||
Corresponds to proveRctCLSAGSimple in rctSigs.cpp
|
||||
|
||||
:param message: the full message to be signed (actually its hash)
|
||||
:param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
|
||||
:param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
|
||||
:param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
|
||||
:param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
|
||||
:param index: specifies corresponding public key to the `in_sk` in the pubs array
|
||||
:param mg_buff: buffer to store the signature to
|
||||
"""
|
||||
cols = len(pubs)
|
||||
if cols == 0:
|
||||
raise ValueError("Empty pubs")
|
||||
|
||||
P = _key_vector(cols)
|
||||
C_nonzero = _key_vector(cols)
|
||||
p = in_sk.dest
|
||||
z = crypto.sc_sub_into(None, in_sk.mask, a)
|
||||
|
||||
for i in range(cols):
|
||||
P[i] = pubs[i].dest
|
||||
C_nonzero[i] = pubs[i].commitment
|
||||
pubs[i] = None # type: ignore
|
||||
|
||||
del pubs
|
||||
gc.collect()
|
||||
|
||||
return _generate_clsag(message, P, p, C_nonzero, z, cout, index, mg_buff)
|
||||
|
||||
|
||||
def _generate_clsag(
|
||||
message: bytes,
|
||||
P: list[bytes],
|
||||
p: crypto.Scalar,
|
||||
C_nonzero: list[bytes],
|
||||
z: crypto.Scalar,
|
||||
Cout: crypto.Point,
|
||||
index: int,
|
||||
mg_buff: list[bytearray],
|
||||
) -> list[bytes]:
|
||||
sI = crypto.Point() # sig.I
|
||||
sD = crypto.Point() # sig.D
|
||||
sc1 = crypto.Scalar() # sig.c1
|
||||
a = crypto.random_scalar()
|
||||
H = crypto.Point()
|
||||
D = crypto.Point()
|
||||
Cout_bf = crypto_helpers.encodepoint(Cout)
|
||||
|
||||
tmp_sc = crypto.Scalar()
|
||||
tmp = crypto.Point()
|
||||
tmp_bf = bytearray(32)
|
||||
|
||||
crypto.hash_to_point_into(H, P[index])
|
||||
crypto.scalarmult_into(sI, H, p) # I = p*H
|
||||
crypto.scalarmult_into(D, H, z) # D = z*H
|
||||
crypto.sc_mul_into(tmp_sc, z, crypto_helpers.INV_EIGHT_SC) # 1/8*z
|
||||
crypto.scalarmult_into(sD, H, tmp_sc) # sig.D = 1/8*z*H
|
||||
sD = crypto_helpers.encodepoint(sD)
|
||||
|
||||
hsh_P = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_C = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_P.update(_HASH_KEY_CLSAG_AGG_0)
|
||||
hsh_C.update(_HASH_KEY_CLSAG_AGG_1)
|
||||
|
||||
def hsh_PC(x):
|
||||
nonlocal hsh_P, hsh_C
|
||||
hsh_P.update(x)
|
||||
hsh_C.update(x)
|
||||
|
||||
for x in P:
|
||||
hsh_PC(x)
|
||||
|
||||
for x in C_nonzero:
|
||||
hsh_PC(x)
|
||||
|
||||
hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
|
||||
hsh_PC(sD)
|
||||
hsh_PC(Cout_bf)
|
||||
mu_P = crypto_helpers.decodeint(hsh_P.digest())
|
||||
mu_C = crypto_helpers.decodeint(hsh_C.digest())
|
||||
|
||||
del (hsh_PC, hsh_P, hsh_C)
|
||||
c_to_hash = crypto_helpers.get_keccak() # domain, P, C, C_offset, message, aG, aH
|
||||
c_to_hash.update(_HASH_KEY_CLSAG_ROUND)
|
||||
for i in range(len(P)):
|
||||
c_to_hash.update(P[i])
|
||||
for i in range(len(P)):
|
||||
c_to_hash.update(C_nonzero[i])
|
||||
c_to_hash.update(Cout_bf)
|
||||
c_to_hash.update(message)
|
||||
|
||||
chasher = c_to_hash.copy()
|
||||
crypto.scalarmult_base_into(tmp, a)
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aG
|
||||
crypto.scalarmult_into(tmp, H, a)
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aH
|
||||
c = crypto_helpers.decodeint(chasher.digest())
|
||||
del (chasher, H)
|
||||
|
||||
L = crypto.Point()
|
||||
R = crypto.Point()
|
||||
c_p = crypto.Scalar()
|
||||
c_c = crypto.Scalar()
|
||||
i = (index + 1) % len(P)
|
||||
if i == 0:
|
||||
crypto.sc_copy(sc1, c)
|
||||
|
||||
mg_buff.append(int_serialize.dump_uvarint_b(len(P)))
|
||||
for _ in range(len(P)):
|
||||
mg_buff.append(bytearray(32))
|
||||
|
||||
while i != index:
|
||||
crypto.random_scalar(tmp_sc)
|
||||
crypto.encodeint_into(mg_buff[i + 1], tmp_sc)
|
||||
|
||||
crypto.sc_mul_into(c_p, mu_P, c)
|
||||
crypto.sc_mul_into(c_c, mu_C, c)
|
||||
|
||||
# L = tmp_sc * G + c_P * P[i] + c_c * C[i]
|
||||
crypto.add_keys2_into(L, tmp_sc, c_p, crypto.decodepoint_into(tmp, P[i]))
|
||||
crypto.decodepoint_into(tmp, C_nonzero[i]) # C = C_nonzero - Cout
|
||||
crypto.point_sub_into(tmp, tmp, Cout)
|
||||
crypto.scalarmult_into(tmp, tmp, c_c)
|
||||
crypto.point_add_into(L, L, tmp)
|
||||
|
||||
# R = tmp_sc * HP + c_p * I + c_c * D
|
||||
crypto.hash_to_point_into(tmp, P[i])
|
||||
crypto.add_keys3_into(R, tmp_sc, tmp, c_p, sI)
|
||||
crypto.point_add_into(R, R, crypto.scalarmult_into(tmp, D, c_c))
|
||||
|
||||
chasher = c_to_hash.copy()
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, L))
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, R))
|
||||
crypto.decodeint_into(c, chasher.digest())
|
||||
|
||||
P[i] = None # type: ignore
|
||||
C_nonzero[i] = None # type: ignore
|
||||
|
||||
i = (i + 1) % len(P)
|
||||
if i == 0:
|
||||
crypto.sc_copy(sc1, c)
|
||||
|
||||
if i & 3 == 0:
|
||||
gc.collect()
|
||||
|
||||
# Final scalar = a - c * (mu_P * p + mu_c * Z)
|
||||
crypto.sc_mul_into(tmp_sc, mu_P, p)
|
||||
crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
|
||||
crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
|
||||
crypto.encodeint_into(mg_buff[index + 1], tmp_sc)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert list_of_type(mg_buff, bytes)
|
||||
|
||||
mg_buff.append(crypto_helpers.encodeint(sc1))
|
||||
mg_buff.append(sD)
|
||||
return mg_buff
|
||||
|
||||
|
||||
def _key_vector(rows: int) -> list[Any]:
|
||||
return [None] * rows
|
@ -1,11 +1,8 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from trezor.enums import MoneroNetworkType
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto, crypto_helpers
|
||||
from apps.monero.xmr.addresses import encode_addr
|
||||
from apps.monero.xmr.networks import NetworkTypes, net_version
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519, Ge25519
|
||||
from apps.monero.xmr.networks import net_version
|
||||
|
||||
|
||||
class AccountCreds:
|
||||
@ -15,33 +12,33 @@ class AccountCreds:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
view_key_private: Sc25519 | None = None,
|
||||
spend_key_private: Sc25519 | None = None,
|
||||
view_key_public: Ge25519 | None = None,
|
||||
spend_key_public: Ge25519 | None = None,
|
||||
address: str | None = None,
|
||||
network_type=NetworkTypes.MAINNET,
|
||||
):
|
||||
view_key_private: crypto.Scalar,
|
||||
spend_key_private: crypto.Scalar,
|
||||
view_key_public: crypto.Point,
|
||||
spend_key_public: crypto.Point,
|
||||
address: str,
|
||||
network_type: MoneroNetworkType,
|
||||
) -> None:
|
||||
self.view_key_private = view_key_private
|
||||
self.view_key_public = view_key_public
|
||||
self.spend_key_private = spend_key_private
|
||||
self.spend_key_public = spend_key_public
|
||||
self.address = address
|
||||
self.network_type = network_type
|
||||
self.address: str | None = address
|
||||
self.network_type: MoneroNetworkType | None = network_type
|
||||
|
||||
@classmethod
|
||||
def new_wallet(
|
||||
cls,
|
||||
priv_view_key: Sc25519,
|
||||
priv_spend_key: Sc25519,
|
||||
network_type=NetworkTypes.MAINNET,
|
||||
):
|
||||
pub_view_key = crypto.scalarmult_base(priv_view_key)
|
||||
pub_spend_key = crypto.scalarmult_base(priv_spend_key)
|
||||
priv_view_key: crypto.Scalar,
|
||||
priv_spend_key: crypto.Scalar,
|
||||
network_type: MoneroNetworkType = MoneroNetworkType.MAINNET,
|
||||
) -> "AccountCreds":
|
||||
pub_view_key = crypto.scalarmult_base_into(None, priv_view_key)
|
||||
pub_spend_key = crypto.scalarmult_base_into(None, priv_spend_key)
|
||||
addr = encode_addr(
|
||||
net_version(network_type),
|
||||
crypto.encodepoint(pub_spend_key),
|
||||
crypto.encodepoint(pub_view_key),
|
||||
crypto_helpers.encodepoint(pub_spend_key),
|
||||
crypto_helpers.encodepoint(pub_view_key),
|
||||
)
|
||||
return cls(
|
||||
view_key_private=priv_view_key,
|
||||
|
@ -1,325 +0,0 @@
|
||||
# Author: Dusan Klinec, ph4r05, 2018
|
||||
#
|
||||
# Resources:
|
||||
# https://cr.yp.to
|
||||
# https://github.com/monero-project/mininero
|
||||
# https://godoc.org/github.com/agl/ed25519/edwards25519
|
||||
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-00#section-4
|
||||
# https://github.com/monero-project/research-lab
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.crypto import monero as tcry, random
|
||||
from trezor.crypto.hashlib import sha3_256
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519, Ge25519
|
||||
|
||||
|
||||
NULL_KEY_ENC = b"\x00" * 32
|
||||
|
||||
random_bytes = random.bytes
|
||||
ct_equals = tcry.ct_equals
|
||||
|
||||
|
||||
def keccak_factory(data=None):
|
||||
return sha3_256(data=data, keccak=True)
|
||||
|
||||
|
||||
get_keccak = keccak_factory
|
||||
keccak_hash = tcry.xmr_fast_hash
|
||||
keccak_hash_into = tcry.xmr_fast_hash
|
||||
|
||||
|
||||
def keccak_2hash(inp, buff=None):
|
||||
buff = buff if buff else bytearray(32)
|
||||
keccak_hash_into(buff, inp)
|
||||
keccak_hash_into(buff, buff)
|
||||
return buff
|
||||
|
||||
|
||||
def compute_hmac(key, msg):
|
||||
digestmod = keccak_factory
|
||||
inner = digestmod()
|
||||
block_size = inner.block_size
|
||||
if len(key) > block_size:
|
||||
key = digestmod(key).digest()
|
||||
key_block = bytearray(block_size)
|
||||
for i in range(block_size):
|
||||
key_block[i] = 0x36
|
||||
for i in range(len(key)):
|
||||
key_block[i] ^= key[i]
|
||||
inner.update(key_block)
|
||||
inner.update(msg)
|
||||
outer = digestmod()
|
||||
for i in range(block_size):
|
||||
key_block[i] = 0x5C
|
||||
for i in range(len(key)):
|
||||
key_block[i] ^= key[i]
|
||||
outer.update(key_block)
|
||||
outer.update(inner.digest())
|
||||
return outer.digest()
|
||||
|
||||
|
||||
#
|
||||
# EC
|
||||
#
|
||||
|
||||
|
||||
new_point = tcry.ge25519_set_neutral
|
||||
|
||||
|
||||
def new_scalar() -> Sc25519:
|
||||
return tcry.init256_modm(0)
|
||||
|
||||
|
||||
decodepoint = tcry.ge25519_unpack_vartime
|
||||
decodepoint_into = tcry.ge25519_unpack_vartime
|
||||
encodepoint = tcry.ge25519_pack
|
||||
encodepoint_into = tcry.ge25519_pack
|
||||
|
||||
decodeint = tcry.unpack256_modm
|
||||
decodeint_into_noreduce = tcry.unpack256_modm_noreduce
|
||||
decodeint_into = tcry.unpack256_modm
|
||||
encodeint = tcry.pack256_modm
|
||||
encodeint_into = tcry.pack256_modm
|
||||
|
||||
check_ed25519point = tcry.ge25519_check
|
||||
|
||||
scalarmult_base = tcry.ge25519_scalarmult_base
|
||||
scalarmult_base_into = tcry.ge25519_scalarmult_base
|
||||
scalarmult = tcry.ge25519_scalarmult
|
||||
scalarmult_into = tcry.ge25519_scalarmult
|
||||
|
||||
point_add = tcry.ge25519_add
|
||||
point_add_into = tcry.ge25519_add
|
||||
point_sub = tcry.ge25519_sub
|
||||
point_sub_into = tcry.ge25519_sub
|
||||
point_eq = tcry.ge25519_eq
|
||||
point_double = tcry.ge25519_double
|
||||
point_double_into = tcry.ge25519_double
|
||||
point_mul8 = tcry.ge25519_mul8
|
||||
point_mul8_into = tcry.ge25519_mul8
|
||||
|
||||
INV_EIGHT = b"\x79\x2f\xdc\xe2\x29\xe5\x06\x61\xd0\xda\x1c\x7d\xb3\x9d\xd3\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"
|
||||
INV_EIGHT_SC = decodeint(INV_EIGHT)
|
||||
|
||||
|
||||
def sc_inv_eight() -> Sc25519:
|
||||
return INV_EIGHT_SC
|
||||
|
||||
|
||||
#
|
||||
# Zmod(order), scalar values field
|
||||
#
|
||||
|
||||
|
||||
def sc_0() -> Sc25519:
|
||||
return tcry.init256_modm(0)
|
||||
|
||||
|
||||
def sc_0_into(r: Sc25519) -> Sc25519:
|
||||
return tcry.init256_modm(r, 0)
|
||||
|
||||
|
||||
def sc_init(x: int) -> Sc25519:
|
||||
if x >= (1 << 64):
|
||||
raise ValueError("Initialization works up to 64-bit only")
|
||||
return tcry.init256_modm(x)
|
||||
|
||||
|
||||
def sc_init_into(r: Sc25519, x: int) -> Sc25519:
|
||||
if x >= (1 << 64):
|
||||
raise ValueError("Initialization works up to 64-bit only")
|
||||
return tcry.init256_modm(r, x)
|
||||
|
||||
|
||||
sc_copy = tcry.init256_modm
|
||||
sc_get64 = tcry.get256_modm
|
||||
sc_check = tcry.check256_modm
|
||||
check_sc = tcry.check256_modm
|
||||
|
||||
sc_add = tcry.add256_modm
|
||||
sc_add_into = tcry.add256_modm
|
||||
sc_sub = tcry.sub256_modm
|
||||
sc_sub_into = tcry.sub256_modm
|
||||
sc_mul = tcry.mul256_modm
|
||||
sc_mul_into = tcry.mul256_modm
|
||||
|
||||
|
||||
def sc_isnonzero(c: Sc25519) -> bool:
|
||||
"""
|
||||
Returns true if scalar is non-zero
|
||||
"""
|
||||
return not tcry.iszero256_modm(c)
|
||||
|
||||
|
||||
sc_eq = tcry.eq256_modm
|
||||
sc_mulsub = tcry.mulsub256_modm
|
||||
sc_mulsub_into = tcry.mulsub256_modm
|
||||
sc_muladd = tcry.muladd256_modm
|
||||
sc_muladd_into = tcry.muladd256_modm
|
||||
sc_inv_into = tcry.inv256_modm
|
||||
|
||||
|
||||
def random_scalar(r=None) -> Sc25519:
|
||||
return tcry.xmr_random_scalar(r if r is not None else new_scalar())
|
||||
|
||||
|
||||
#
|
||||
# GE - ed25519 group
|
||||
#
|
||||
|
||||
|
||||
def ge25519_double_scalarmult_base_vartime(a, A, b) -> Ge25519:
|
||||
"""
|
||||
void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const bignum256modm s1, const bignum256modm s2);
|
||||
r = a * A + b * B
|
||||
"""
|
||||
R = tcry.ge25519_double_scalarmult_vartime(A, a, b)
|
||||
return R
|
||||
|
||||
|
||||
ge25519_double_scalarmult_vartime2 = tcry.xmr_add_keys3
|
||||
|
||||
|
||||
def identity(byte_enc=False) -> Ge25519 | bytes:
|
||||
idd = tcry.ge25519_set_neutral()
|
||||
return idd if not byte_enc else encodepoint(idd)
|
||||
|
||||
|
||||
identity_into = tcry.ge25519_set_neutral
|
||||
|
||||
"""
|
||||
https://www.imperialviolet.org/2013/12/25/elligator.html
|
||||
http://elligator.cr.yp.to/
|
||||
http://elligator.cr.yp.to/elligator-20130828.pdf
|
||||
"""
|
||||
|
||||
#
|
||||
# Monero specific
|
||||
#
|
||||
|
||||
|
||||
cn_fast_hash = keccak_hash
|
||||
|
||||
|
||||
def hash_to_scalar(data: bytes, length: int | None = None):
|
||||
"""
|
||||
H_s(P)
|
||||
"""
|
||||
dt = data[:length] if length else data
|
||||
return tcry.xmr_hash_to_scalar(dt)
|
||||
|
||||
|
||||
def hash_to_scalar_into(r: Sc25519, data: bytes, length: int | None = None):
|
||||
dt = data[:length] if length else data
|
||||
return tcry.xmr_hash_to_scalar(r, dt)
|
||||
|
||||
|
||||
# H_p(buf)
|
||||
#
|
||||
# Code adapted from MiniNero: https://github.com/monero-project/mininero
|
||||
# https://github.com/monero-project/research-lab/blob/master/whitepaper/ge_fromfe_writeup/ge_fromfe.pdf
|
||||
# http://archive.is/yfINb
|
||||
hash_to_point = tcry.xmr_hash_to_ec
|
||||
hash_to_point_into = tcry.xmr_hash_to_ec
|
||||
|
||||
|
||||
#
|
||||
# XMR
|
||||
#
|
||||
|
||||
|
||||
xmr_H = tcry.ge25519_set_h
|
||||
|
||||
|
||||
def scalarmult_h(i) -> Ge25519:
|
||||
return scalarmult(xmr_H(), sc_init(i) if isinstance(i, int) else i)
|
||||
|
||||
|
||||
add_keys2 = tcry.xmr_add_keys2_vartime
|
||||
add_keys2_into = tcry.xmr_add_keys2_vartime
|
||||
add_keys3 = tcry.xmr_add_keys3_vartime
|
||||
add_keys3_into = tcry.xmr_add_keys3_vartime
|
||||
gen_commitment = tcry.xmr_gen_c
|
||||
|
||||
|
||||
def generate_key_derivation(pub: Ge25519, sec: Sc25519) -> Ge25519:
|
||||
"""
|
||||
Key derivation: 8*(key2*key1)
|
||||
"""
|
||||
sc_check(sec) # checks that the secret key is uniform enough...
|
||||
check_ed25519point(pub)
|
||||
return tcry.xmr_generate_key_derivation(pub, sec)
|
||||
|
||||
|
||||
def derivation_to_scalar(derivation: Ge25519, output_index: int) -> Sc25519:
|
||||
"""
|
||||
H_s(derivation || varint(output_index))
|
||||
"""
|
||||
check_ed25519point(derivation)
|
||||
return tcry.xmr_derivation_to_scalar(derivation, output_index)
|
||||
|
||||
|
||||
def derive_public_key(derivation: Ge25519, output_index: int, B: Ge25519) -> Ge25519:
|
||||
"""
|
||||
H_s(derivation || varint(output_index))G + B
|
||||
"""
|
||||
check_ed25519point(B)
|
||||
return tcry.xmr_derive_public_key(derivation, output_index, B)
|
||||
|
||||
|
||||
def derive_secret_key(derivation: Ge25519, output_index: int, base: Sc25519) -> Sc25519:
|
||||
"""
|
||||
base + H_s(derivation || varint(output_index))
|
||||
"""
|
||||
sc_check(base)
|
||||
return tcry.xmr_derive_private_key(derivation, output_index, base)
|
||||
|
||||
|
||||
def get_subaddress_secret_key(
|
||||
secret_key: Sc25519, major: int = 0, minor: int = 0
|
||||
) -> Sc25519:
|
||||
"""
|
||||
Builds subaddress secret key from the subaddress index
|
||||
Hs(SubAddr || a || index_major || index_minor)
|
||||
"""
|
||||
return tcry.xmr_get_subaddress_secret_key(major, minor, secret_key)
|
||||
|
||||
|
||||
def generate_signature(data: bytes, priv: Sc25519) -> tuple[Sc25519, Sc25519, Ge25519]:
|
||||
"""
|
||||
Generate EC signature
|
||||
crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
|
||||
"""
|
||||
pub = scalarmult_base(priv)
|
||||
|
||||
k = random_scalar()
|
||||
comm = scalarmult_base(k)
|
||||
|
||||
buff = data + encodepoint(pub) + encodepoint(comm)
|
||||
c = hash_to_scalar(buff)
|
||||
r = sc_mulsub(priv, c, k)
|
||||
return c, r, pub
|
||||
|
||||
|
||||
def check_signature(data: bytes, c: Sc25519, r: Sc25519, pub: Ge25519) -> bool:
|
||||
"""
|
||||
EC signature verification
|
||||
"""
|
||||
check_ed25519point(pub)
|
||||
if sc_check(c) != 0 or sc_check(r) != 0:
|
||||
raise ValueError("Signature error")
|
||||
|
||||
tmp2 = point_add(scalarmult(pub, c), scalarmult_base(r))
|
||||
buff = data + encodepoint(pub) + encodepoint(tmp2)
|
||||
tmp_c = hash_to_scalar(buff)
|
||||
res = sc_sub(tmp_c, c)
|
||||
return not sc_isnonzero(res)
|
||||
|
||||
|
||||
def xor8(buff: bytes, key: bytes) -> bytes:
|
||||
for i in range(8):
|
||||
buff[i] ^= key[i]
|
||||
return buff
|
162
core/src/apps/monero/xmr/crypto_helpers.py
Normal file
162
core/src/apps/monero/xmr/crypto_helpers.py
Normal file
@ -0,0 +1,162 @@
|
||||
# Author: Dusan Klinec, ph4r05, 2018
|
||||
#
|
||||
# Resources:
|
||||
# https://cr.yp.to
|
||||
# https://github.com/monero-project/mininero
|
||||
# https://godoc.org/github.com/agl/ed25519/edwards25519
|
||||
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-00#section-4
|
||||
# https://github.com/monero-project/research-lab
|
||||
|
||||
from trezor.crypto import monero as tcry
|
||||
from trezor.crypto.hashlib import sha3_256
|
||||
|
||||
NULL_KEY_ENC = b"\x00" * 32
|
||||
|
||||
|
||||
def get_keccak(data: bytes | None = None) -> sha3_256:
|
||||
return sha3_256(data=data, keccak=True)
|
||||
|
||||
|
||||
def keccak_2hash(inp: bytes, buff: bytes | None = None) -> bytes:
|
||||
buff = buff if buff else bytearray(32)
|
||||
tcry.fast_hash_into(buff, inp)
|
||||
tcry.fast_hash_into(buff, buff)
|
||||
return buff
|
||||
|
||||
|
||||
def compute_hmac(key: bytes, msg: bytes) -> bytes:
|
||||
digestmod = get_keccak
|
||||
inner = digestmod()
|
||||
block_size = inner.block_size
|
||||
if len(key) > block_size:
|
||||
key = digestmod(key).digest()
|
||||
key_block = bytearray(block_size)
|
||||
for i in range(block_size):
|
||||
key_block[i] = 0x36
|
||||
for i in range(len(key)):
|
||||
key_block[i] ^= key[i]
|
||||
inner.update(key_block)
|
||||
inner.update(msg)
|
||||
outer = digestmod()
|
||||
for i in range(block_size):
|
||||
key_block[i] = 0x5C
|
||||
for i in range(len(key)):
|
||||
key_block[i] ^= key[i]
|
||||
outer.update(key_block)
|
||||
outer.update(inner.digest())
|
||||
return outer.digest()
|
||||
|
||||
|
||||
#
|
||||
# EC
|
||||
#
|
||||
|
||||
|
||||
def decodepoint(x: bytes) -> tcry.Point:
|
||||
return tcry.decodepoint_into(None, x)
|
||||
|
||||
|
||||
def encodepoint(x: tcry.Point, offset: int = 0) -> bytes:
|
||||
return tcry.encodepoint_into(None, x, offset)
|
||||
|
||||
|
||||
def encodeint(x: tcry.Scalar, offset: int = 0) -> bytes:
|
||||
return tcry.encodeint_into(None, x, offset)
|
||||
|
||||
|
||||
def decodeint(x: bytes) -> tcry.Scalar:
|
||||
return tcry.decodeint_into(None, x)
|
||||
|
||||
|
||||
INV_EIGHT = b"\x79\x2f\xdc\xe2\x29\xe5\x06\x61\xd0\xda\x1c\x7d\xb3\x9d\xd3\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"
|
||||
INV_EIGHT_SC = decodeint(INV_EIGHT)
|
||||
|
||||
|
||||
def generate_key_derivation(pub: tcry.Point, sec: tcry.Scalar) -> tcry.Point:
|
||||
"""
|
||||
Key derivation: 8*(key2*key1)
|
||||
"""
|
||||
tcry.sc_check(sec) # checks that the secret key is uniform enough...
|
||||
tcry.ge25519_check(pub)
|
||||
return tcry.xmr_generate_key_derivation(None, pub, sec)
|
||||
|
||||
|
||||
def derivation_to_scalar(derivation: tcry.Point, output_index: int) -> tcry.Scalar:
|
||||
"""
|
||||
H_s(derivation || varint(output_index))
|
||||
"""
|
||||
tcry.ge25519_check(derivation)
|
||||
return tcry.xmr_derivation_to_scalar(None, derivation, output_index)
|
||||
|
||||
|
||||
def derive_public_key(
|
||||
derivation: tcry.Point, output_index: int, B: tcry.Point
|
||||
) -> tcry.Point:
|
||||
"""
|
||||
H_s(derivation || varint(output_index))G + B
|
||||
"""
|
||||
tcry.ge25519_check(B)
|
||||
return tcry.xmr_derive_public_key(None, derivation, output_index, B)
|
||||
|
||||
|
||||
def derive_secret_key(
|
||||
derivation: tcry.Point, output_index: int, base: tcry.Scalar
|
||||
) -> tcry.Scalar:
|
||||
"""
|
||||
base + H_s(derivation || varint(output_index))
|
||||
"""
|
||||
tcry.sc_check(base)
|
||||
return tcry.xmr_derive_private_key(None, derivation, output_index, base)
|
||||
|
||||
|
||||
def get_subaddress_secret_key(
|
||||
secret_key: tcry.Scalar, major: int = 0, minor: int = 0
|
||||
) -> tcry.Scalar:
|
||||
"""
|
||||
Builds subaddress secret key from the subaddress index
|
||||
Hs(SubAddr || a || index_major || index_minor)
|
||||
"""
|
||||
return tcry.xmr_get_subaddress_secret_key(None, major, minor, secret_key)
|
||||
|
||||
|
||||
def generate_signature(
|
||||
data: bytes, priv: tcry.Scalar
|
||||
) -> tuple[tcry.Scalar, tcry.Scalar, tcry.Point]:
|
||||
"""
|
||||
Generate EC signature
|
||||
crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
|
||||
"""
|
||||
pub = tcry.scalarmult_base_into(None, priv)
|
||||
|
||||
k = tcry.random_scalar()
|
||||
comm = tcry.scalarmult_base_into(None, k)
|
||||
|
||||
buff = data + encodepoint(pub) + encodepoint(comm)
|
||||
c = tcry.hash_to_scalar_into(None, buff)
|
||||
r = tcry.sc_mulsub_into(None, priv, c, k)
|
||||
return c, r, pub
|
||||
|
||||
|
||||
def check_signature(
|
||||
data: bytes, c: tcry.Scalar, r: tcry.Scalar, pub: tcry.Point
|
||||
) -> bool:
|
||||
"""
|
||||
EC signature verification
|
||||
"""
|
||||
tcry.ge25519_check(pub)
|
||||
if tcry.sc_check(c) != 0 or tcry.sc_check(r) != 0:
|
||||
raise ValueError("Signature error")
|
||||
|
||||
tmp2 = tcry.point_add_into(
|
||||
None, tcry.scalarmult_into(None, pub, c), tcry.scalarmult_base_into(None, r)
|
||||
)
|
||||
buff = data + encodepoint(pub) + encodepoint(tmp2)
|
||||
tmp_c = tcry.hash_to_scalar_into(None, buff)
|
||||
res = tcry.sc_sub_into(None, tmp_c, c)
|
||||
return tcry.sc_iszero(res)
|
||||
|
||||
|
||||
def xor8(buff: bytearray, key: bytes) -> bytes:
|
||||
for i in range(8):
|
||||
buff[i] ^= key[i]
|
||||
return buff
|
@ -1,27 +1,32 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.utils import HashWriter
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto_helpers
|
||||
from apps.monero.xmr.serialize import int_serialize
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.utils import HashContext
|
||||
|
||||
|
||||
class KeccakXmrArchive:
|
||||
def __init__(self, ctx=None):
|
||||
def __init__(self, ctx: HashContext | None = None) -> None:
|
||||
self.kwriter = get_keccak_writer(ctx)
|
||||
|
||||
def get_digest(self):
|
||||
def get_digest(self) -> bytes:
|
||||
return self.kwriter.get_digest()
|
||||
|
||||
def buffer(self, buf):
|
||||
def buffer(self, buf: bytes) -> None:
|
||||
return self.kwriter.write(buf)
|
||||
|
||||
def uvarint(self, i):
|
||||
def uvarint(self, i: int) -> None:
|
||||
int_serialize.dump_uvarint(self.kwriter, i)
|
||||
|
||||
def uint(self, i, width):
|
||||
def uint(self, i: int, width: int) -> None:
|
||||
int_serialize.dump_uint(self.kwriter, i, width)
|
||||
|
||||
|
||||
def get_keccak_writer(ctx=None):
|
||||
def get_keccak_writer(ctx: HashContext | None = None) -> HashWriter:
|
||||
if ctx is None:
|
||||
ctx = crypto.get_keccak()
|
||||
ctx = crypto_helpers.get_keccak()
|
||||
return HashWriter(ctx)
|
||||
|
@ -1,19 +1,18 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr import crypto, monero
|
||||
from apps.monero.xmr import crypto, crypto_helpers, monero
|
||||
from apps.monero.xmr.serialize.int_serialize import dump_uvarint_b
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Ge25519, Sc25519
|
||||
from apps.monero.xmr.credentials import AccountCreds
|
||||
from trezor.messages import MoneroTransferDetails
|
||||
|
||||
Subaddresses = dict[bytes, tuple[int, int]]
|
||||
Sig = list[list[Sc25519]]
|
||||
Sig = list[list[crypto.Scalar]]
|
||||
|
||||
|
||||
def compute_hash(rr: MoneroTransferDetails) -> bytes:
|
||||
kck = crypto.get_keccak()
|
||||
kck = crypto_helpers.get_keccak()
|
||||
kck.update(rr.out_key)
|
||||
kck.update(rr.tx_pub_key)
|
||||
if rr.additional_tx_pub_keys:
|
||||
@ -25,17 +24,17 @@ def compute_hash(rr: MoneroTransferDetails) -> bytes:
|
||||
|
||||
def export_key_image(
|
||||
creds: AccountCreds, subaddresses: Subaddresses, td: MoneroTransferDetails
|
||||
) -> tuple[Ge25519, Sig]:
|
||||
out_key = crypto.decodepoint(td.out_key)
|
||||
tx_pub_key = crypto.decodepoint(td.tx_pub_key)
|
||||
) -> tuple[crypto.Point, Sig]:
|
||||
out_key = crypto_helpers.decodepoint(td.out_key)
|
||||
tx_pub_key = crypto_helpers.decodepoint(td.tx_pub_key)
|
||||
|
||||
additional_tx_pub_key = None
|
||||
if len(td.additional_tx_pub_keys) == 1: # compression
|
||||
additional_tx_pub_key = crypto.decodepoint(td.additional_tx_pub_keys[0])
|
||||
additional_tx_pub_key = crypto_helpers.decodepoint(td.additional_tx_pub_keys[0])
|
||||
elif td.additional_tx_pub_keys:
|
||||
if td.internal_output_index >= len(td.additional_tx_pub_keys):
|
||||
raise ValueError("Wrong number of additional derivations")
|
||||
additional_tx_pub_key = crypto.decodepoint(
|
||||
additional_tx_pub_key = crypto_helpers.decodepoint(
|
||||
td.additional_tx_pub_keys[td.internal_output_index]
|
||||
)
|
||||
|
||||
@ -56,14 +55,14 @@ def export_key_image(
|
||||
def _export_key_image(
|
||||
creds: AccountCreds,
|
||||
subaddresses: Subaddresses,
|
||||
pkey: Ge25519,
|
||||
tx_pub_key: Ge25519,
|
||||
additional_tx_pub_key: Ge25519 | None,
|
||||
pkey: crypto.Point,
|
||||
tx_pub_key: crypto.Point,
|
||||
additional_tx_pub_key: crypto.Point | None,
|
||||
out_idx: int,
|
||||
test: bool = True,
|
||||
sub_addr_major: int = None,
|
||||
sub_addr_minor: int = None,
|
||||
) -> tuple[Ge25519, Sig]:
|
||||
sub_addr_major: int | None = None,
|
||||
sub_addr_minor: int | None = None,
|
||||
) -> tuple[crypto.Point, Sig]:
|
||||
"""
|
||||
Generates key image for the TXO + signature for the key image
|
||||
"""
|
||||
@ -79,7 +78,7 @@ def _export_key_image(
|
||||
)
|
||||
xi, ki, _ = r[:3]
|
||||
|
||||
phash = crypto.encodepoint(ki)
|
||||
phash = crypto_helpers.encodepoint(ki)
|
||||
sig = generate_ring_signature(phash, ki, [pkey], xi, 0, test)
|
||||
|
||||
return ki, sig
|
||||
@ -87,9 +86,9 @@ def _export_key_image(
|
||||
|
||||
def generate_ring_signature(
|
||||
prefix_hash: bytes,
|
||||
image: Ge25519,
|
||||
pubs: list[Ge25519],
|
||||
sec: Sc25519,
|
||||
image: crypto.Point,
|
||||
pubs: list[crypto.Point],
|
||||
sec: crypto.Scalar,
|
||||
sec_idx: int,
|
||||
test: bool = False,
|
||||
) -> Sig:
|
||||
@ -100,59 +99,57 @@ def generate_ring_signature(
|
||||
from trezor.utils import memcpy
|
||||
|
||||
if test:
|
||||
t = crypto.scalarmult_base(sec)
|
||||
t = crypto.scalarmult_base_into(None, sec)
|
||||
if not crypto.point_eq(t, pubs[sec_idx]):
|
||||
raise ValueError("Invalid sec key")
|
||||
|
||||
k_i = monero.generate_key_image(crypto.encodepoint(pubs[sec_idx]), sec)
|
||||
k_i = monero.generate_key_image(crypto_helpers.encodepoint(pubs[sec_idx]), sec)
|
||||
if not crypto.point_eq(k_i, image):
|
||||
raise ValueError("Key image invalid")
|
||||
for k in pubs:
|
||||
crypto.check_ed25519point(k)
|
||||
crypto.ge25519_check(k)
|
||||
|
||||
buff_off = len(prefix_hash)
|
||||
buff = bytearray(buff_off + 2 * 32 * len(pubs))
|
||||
memcpy(buff, 0, prefix_hash, 0, buff_off)
|
||||
mvbuff = memoryview(buff)
|
||||
|
||||
sum = crypto.sc_0()
|
||||
k = crypto.sc_0()
|
||||
sum = crypto.Scalar(0)
|
||||
k = crypto.Scalar(0)
|
||||
sig = []
|
||||
|
||||
for _ in range(len(pubs)):
|
||||
sig.append([crypto.sc_0(), crypto.sc_0()]) # c, r
|
||||
sig.append([crypto.Scalar(0), crypto.Scalar(0)]) # c, r
|
||||
|
||||
for i in range(len(pubs)):
|
||||
if i == sec_idx:
|
||||
k = crypto.random_scalar()
|
||||
tmp3 = crypto.scalarmult_base(k)
|
||||
tmp3 = crypto.scalarmult_base_into(None, k)
|
||||
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp3)
|
||||
buff_off += 32
|
||||
|
||||
tmp3 = crypto.hash_to_point(crypto.encodepoint(pubs[i]))
|
||||
tmp2 = crypto.scalarmult(tmp3, k)
|
||||
tmp3 = crypto.hash_to_point_into(None, crypto_helpers.encodepoint(pubs[i]))
|
||||
tmp2 = crypto.scalarmult_into(None, tmp3, k)
|
||||
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
|
||||
buff_off += 32
|
||||
|
||||
else:
|
||||
sig[i] = [crypto.random_scalar(), crypto.random_scalar()]
|
||||
tmp3 = pubs[i]
|
||||
tmp2 = crypto.ge25519_double_scalarmult_base_vartime(
|
||||
sig[i][0], tmp3, sig[i][1]
|
||||
tmp2 = crypto.ge25519_double_scalarmult_vartime_into(
|
||||
None, tmp3, sig[i][0], sig[i][1]
|
||||
)
|
||||
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
|
||||
buff_off += 32
|
||||
|
||||
tmp3 = crypto.hash_to_point(crypto.encodepoint(tmp3))
|
||||
tmp2 = crypto.ge25519_double_scalarmult_vartime2(
|
||||
sig[i][1], tmp3, sig[i][0], image
|
||||
)
|
||||
tmp3 = crypto.hash_to_point_into(None, crypto_helpers.encodepoint(tmp3))
|
||||
tmp2 = crypto.add_keys3_into(None, sig[i][1], tmp3, sig[i][0], image)
|
||||
crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
|
||||
buff_off += 32
|
||||
|
||||
sum = crypto.sc_add(sum, sig[i][0])
|
||||
crypto.sc_add_into(sum, sum, sig[i][0])
|
||||
|
||||
h = crypto.hash_to_scalar(buff)
|
||||
sig[sec_idx][0] = crypto.sc_sub(h, sum)
|
||||
sig[sec_idx][1] = crypto.sc_mulsub(sig[sec_idx][0], sec, k)
|
||||
h = crypto.hash_to_scalar_into(None, buff)
|
||||
sig[sec_idx][0] = crypto.sc_sub_into(None, h, sum)
|
||||
sig[sec_idx][1] = crypto.sc_mulsub_into(None, sig[sec_idx][0], sec, k)
|
||||
return sig
|
||||
|
@ -1,493 +0,0 @@
|
||||
"""
|
||||
Multilayer Linkable Spontaneous Anonymous Group (MLSAG)
|
||||
Optimized versions with incremental hashing.
|
||||
|
||||
See https://eprint.iacr.org/2015/1098.pdf for details.
|
||||
Also explained in From Zero to Monero section 3.3 and 5.
|
||||
|
||||
----------
|
||||
|
||||
Please note, that the MLSAG code is written in a generic manner,
|
||||
where it is designed for multiple public keys (aka inputs). In another
|
||||
words, MLSAG should be used to sign multiple inputs, but that is currently
|
||||
not the case of Monero, where the inputs are signed one by one.
|
||||
So the public keys matrix has always two rows (one for public keys,
|
||||
one for commitments), although the algorithm is designed for `n` rows.
|
||||
|
||||
This has one unfortunate effect where `rows` is always equal to 2 and
|
||||
dsRows always to 1, but the algorithm is still written as the numbers
|
||||
are arbitrary. That's why there are loops such as `for i in range(dsRows)`
|
||||
where it is run only once currently.
|
||||
|
||||
----------
|
||||
|
||||
Also note, that the matrix of public keys is indexed by columns first.
|
||||
This is because the code was ported from the official Monero client,
|
||||
which is written in C++ and where it does have some memory advantages.
|
||||
|
||||
For ring size = 3 and one input the matrix M will look like this:
|
||||
|------------------------|------------------------|------------------------|
|
||||
| public key 0 | public key 1 | public key 2 |
|
||||
| cmt 0 - pseudo_out cmt | cmt 1 - pseudo_out cmt | cmt 2 - pseudo_out cmt |
|
||||
|
||||
and `sk` is equal to:
|
||||
|--------------|-----------------------------------------------------|
|
||||
| private key* | input secret key's mask - pseudo_out's mask (alpha) |
|
||||
|
||||
* corresponding to one of the public keys (`index` denotes which one)
|
||||
|
||||
----------
|
||||
|
||||
Mostly ported from official Monero client, but also inspired by Mininero.
|
||||
Author: Dusan Klinec, ph4r05, 2018
|
||||
"""
|
||||
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr.serialize import int_serialize
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Ge25519, Sc25519
|
||||
from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey
|
||||
from trezor.messages import MoneroRctKeyPublic
|
||||
|
||||
KeyM = list[list[bytes]]
|
||||
|
||||
|
||||
_HASH_KEY_CLSAG_ROUND = b"CLSAG_round\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
_HASH_KEY_CLSAG_AGG_0 = b"CLSAG_agg_0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
_HASH_KEY_CLSAG_AGG_1 = b"CLSAG_agg_1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
|
||||
def generate_mlsag_simple(
|
||||
message: bytes,
|
||||
pubs: list[MoneroRctKeyPublic],
|
||||
in_sk: CtKey,
|
||||
a: Sc25519,
|
||||
cout: Ge25519,
|
||||
index: int,
|
||||
mg_buff: list[bytes],
|
||||
) -> list[bytes]:
|
||||
"""
|
||||
MLSAG for RctType.Simple
|
||||
:param message: the full message to be signed (actually its hash)
|
||||
:param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
|
||||
:param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
|
||||
:param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
|
||||
:param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
|
||||
:param index: specifies corresponding public key to the `in_sk` in the pubs array
|
||||
:param mg_buff: buffer to store the signature to
|
||||
"""
|
||||
# Monero signs inputs separately, so `rows` always equals 2 (pubkey, commitment)
|
||||
# and `dsRows` is always 1 (denotes where the pubkeys "end")
|
||||
rows = 2
|
||||
dsRows = 1
|
||||
cols = len(pubs)
|
||||
if cols == 0:
|
||||
raise ValueError("Empty pubs")
|
||||
|
||||
sk = _key_vector(rows)
|
||||
M = _key_matrix(rows, cols)
|
||||
|
||||
sk[0] = in_sk.dest
|
||||
sk[1] = crypto.sc_sub(in_sk.mask, a)
|
||||
tmp_pt = crypto.new_point()
|
||||
|
||||
for i in range(cols):
|
||||
crypto.point_sub_into(
|
||||
tmp_pt, crypto.decodepoint_into(tmp_pt, pubs[i].commitment), cout
|
||||
)
|
||||
|
||||
M[i][0] = pubs[i].dest
|
||||
M[i][1] = crypto.encodepoint(tmp_pt)
|
||||
pubs[i] = None
|
||||
|
||||
del pubs
|
||||
gc.collect()
|
||||
|
||||
return generate_mlsag(message, M, sk, index, dsRows, mg_buff)
|
||||
|
||||
|
||||
def gen_mlsag_assert(pk: KeyM, xx: list[Sc25519], index: int, dsRows: int):
|
||||
"""
|
||||
Conditions check
|
||||
"""
|
||||
cols = len(pk)
|
||||
if cols <= 1:
|
||||
raise ValueError("Cols == 1")
|
||||
if index >= cols:
|
||||
raise ValueError("Index out of range")
|
||||
|
||||
rows = len(pk[0])
|
||||
if rows == 0:
|
||||
raise ValueError("Empty pk")
|
||||
|
||||
for i in range(cols):
|
||||
if len(pk[i]) != rows:
|
||||
raise ValueError("pk is not rectangular")
|
||||
if len(xx) != rows:
|
||||
raise ValueError("Bad xx size")
|
||||
if dsRows > rows:
|
||||
raise ValueError("Bad dsRows size")
|
||||
return rows, cols
|
||||
|
||||
|
||||
def generate_first_c_and_key_images(
|
||||
message: bytes,
|
||||
pk: KeyM,
|
||||
xx: list[Sc25519],
|
||||
index: int,
|
||||
dsRows: int,
|
||||
rows: int,
|
||||
cols: int,
|
||||
) -> tuple[Sc25519, list[Ge25519], list[Ge25519]]:
|
||||
"""
|
||||
MLSAG computation - the part with secret keys
|
||||
:param message: the full message to be signed (actually its hash)
|
||||
:param pk: matrix of public keys and commitments
|
||||
:param xx: input secret array composed of a private key and commitment mask
|
||||
:param index: specifies corresponding public key to the `xx`'s private key in the `pk` array
|
||||
:param dsRows: row number where the pubkeys "end" (and commitments follow)
|
||||
:param rows: total number of rows
|
||||
:param cols: size of ring
|
||||
"""
|
||||
II = _key_vector(dsRows)
|
||||
alpha = _key_vector(rows)
|
||||
|
||||
tmp_buff = bytearray(32)
|
||||
Hi = crypto.new_point()
|
||||
aGi = crypto.new_point()
|
||||
aHPi = crypto.new_point()
|
||||
hasher = _hasher_message(message)
|
||||
|
||||
for i in range(dsRows):
|
||||
# this is somewhat extra as compared to the Ring Confidential Tx paper
|
||||
# see footnote in From Zero to Monero section 3.3
|
||||
hasher.update(pk[index][i])
|
||||
|
||||
crypto.hash_to_point_into(Hi, pk[index][i])
|
||||
alpha[i] = crypto.random_scalar()
|
||||
# L = alpha_i * G
|
||||
crypto.scalarmult_base_into(aGi, alpha[i])
|
||||
# Ri = alpha_i * H(P_i)
|
||||
crypto.scalarmult_into(aHPi, Hi, alpha[i])
|
||||
# key image
|
||||
II[i] = crypto.scalarmult(Hi, xx[i])
|
||||
_hash_point(hasher, aGi, tmp_buff)
|
||||
_hash_point(hasher, aHPi, tmp_buff)
|
||||
|
||||
for i in range(dsRows, rows):
|
||||
alpha[i] = crypto.random_scalar()
|
||||
# L = alpha_i * G
|
||||
crypto.scalarmult_base_into(aGi, alpha[i])
|
||||
# for some reasons we omit calculating R here, which seems
|
||||
# contrary to the paper, but it is in the Monero official client
|
||||
# see https://github.com/monero-project/monero/blob/636153b2050aa0642ba86842c69ac55a5d81618d/src/ringct/rctSigs.cpp#L191
|
||||
hasher.update(pk[index][i])
|
||||
_hash_point(hasher, aGi, tmp_buff)
|
||||
|
||||
# the first c
|
||||
c_old = hasher.digest()
|
||||
c_old = crypto.decodeint(c_old)
|
||||
return c_old, II, alpha
|
||||
|
||||
|
||||
def generate_mlsag(
|
||||
message: bytes,
|
||||
pk: KeyM,
|
||||
xx: list[Sc25519],
|
||||
index: int,
|
||||
dsRows: int,
|
||||
mg_buff: list[bytes],
|
||||
) -> list[bytes]:
|
||||
"""
|
||||
Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
|
||||
|
||||
:param message: the full message to be signed (actually its hash)
|
||||
:param pk: matrix of public keys and commitments
|
||||
:param xx: input secret array composed of a private key and commitment mask
|
||||
:param index: specifies corresponding public key to the `xx`'s private key in the `pk` array
|
||||
:param dsRows: separates pubkeys from commitment
|
||||
:param mg_buff: mg signature buffer
|
||||
"""
|
||||
rows, cols = gen_mlsag_assert(pk, xx, index, dsRows)
|
||||
rows_b_size = int_serialize.uvarint_size(rows)
|
||||
|
||||
# Preallocation of the chunked buffer, len + cols + cc
|
||||
for _ in range(1 + cols + 1):
|
||||
mg_buff.append(None)
|
||||
|
||||
mg_buff[0] = int_serialize.dump_uvarint_b(cols)
|
||||
cc = crypto.new_scalar() # rv.cc
|
||||
c = crypto.new_scalar()
|
||||
L = crypto.new_point()
|
||||
R = crypto.new_point()
|
||||
Hi = crypto.new_point()
|
||||
|
||||
# calculates the "first" c, key images and random scalars alpha
|
||||
c_old, II, alpha = generate_first_c_and_key_images(
|
||||
message, pk, xx, index, dsRows, rows, cols
|
||||
)
|
||||
|
||||
i = (index + 1) % cols
|
||||
if i == 0:
|
||||
crypto.sc_copy(cc, c_old)
|
||||
|
||||
ss = [crypto.new_scalar() for _ in range(rows)]
|
||||
tmp_buff = bytearray(32)
|
||||
|
||||
while i != index:
|
||||
hasher = _hasher_message(message)
|
||||
|
||||
# Serialize size of the row
|
||||
mg_buff[i + 1] = bytearray(rows_b_size + 32 * rows)
|
||||
int_serialize.dump_uvarint_b_into(rows, mg_buff[i + 1])
|
||||
|
||||
for x in ss:
|
||||
crypto.random_scalar(x)
|
||||
|
||||
for j in range(dsRows):
|
||||
# L = rv.ss[i][j] * G + c_old * pk[i][j]
|
||||
crypto.add_keys2_into(
|
||||
L, ss[j], c_old, crypto.decodepoint_into(Hi, pk[i][j])
|
||||
)
|
||||
crypto.hash_to_point_into(Hi, pk[i][j])
|
||||
|
||||
# R = rv.ss[i][j] * H(pk[i][j]) + c_old * Ip[j]
|
||||
crypto.add_keys3_into(R, ss[j], Hi, c_old, II[j])
|
||||
|
||||
hasher.update(pk[i][j])
|
||||
_hash_point(hasher, L, tmp_buff)
|
||||
_hash_point(hasher, R, tmp_buff)
|
||||
|
||||
for j in range(dsRows, rows):
|
||||
# again, omitting R here as discussed above
|
||||
crypto.add_keys2_into(
|
||||
L, ss[j], c_old, crypto.decodepoint_into(Hi, pk[i][j])
|
||||
)
|
||||
hasher.update(pk[i][j])
|
||||
_hash_point(hasher, L, tmp_buff)
|
||||
|
||||
for si in range(rows):
|
||||
crypto.encodeint_into(mg_buff[i + 1], ss[si], rows_b_size + 32 * si)
|
||||
|
||||
crypto.decodeint_into(c, hasher.digest())
|
||||
crypto.sc_copy(c_old, c)
|
||||
pk[i] = None
|
||||
i = (i + 1) % cols
|
||||
|
||||
if i == 0:
|
||||
crypto.sc_copy(cc, c_old)
|
||||
gc.collect()
|
||||
|
||||
del II
|
||||
|
||||
# Finalizing rv.ss by processing rv.ss[index]
|
||||
mg_buff[index + 1] = bytearray(rows_b_size + 32 * rows)
|
||||
int_serialize.dump_uvarint_b_into(rows, mg_buff[index + 1])
|
||||
for j in range(rows):
|
||||
crypto.sc_mulsub_into(ss[j], c, xx[j], alpha[j])
|
||||
crypto.encodeint_into(mg_buff[index + 1], ss[j], rows_b_size + 32 * j)
|
||||
|
||||
# rv.cc
|
||||
mg_buff[-1] = crypto.encodeint(cc)
|
||||
return mg_buff
|
||||
|
||||
|
||||
def generate_clsag_simple(
|
||||
message: bytes,
|
||||
pubs: list[MoneroRctKeyPublic],
|
||||
in_sk: CtKey,
|
||||
a: Sc25519,
|
||||
cout: Ge25519,
|
||||
index: int,
|
||||
mg_buff: list[bytes],
|
||||
) -> list[bytes]:
|
||||
"""
|
||||
CLSAG for RctType.Simple
|
||||
https://eprint.iacr.org/2019/654.pdf
|
||||
|
||||
Corresponds to proveRctCLSAGSimple in rctSigs.cpp
|
||||
|
||||
:param message: the full message to be signed (actually its hash)
|
||||
:param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
|
||||
:param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
|
||||
:param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
|
||||
:param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
|
||||
:param index: specifies corresponding public key to the `in_sk` in the pubs array
|
||||
:param mg_buff: buffer to store the signature to
|
||||
"""
|
||||
cols = len(pubs)
|
||||
if cols == 0:
|
||||
raise ValueError("Empty pubs")
|
||||
|
||||
P = _key_vector(cols)
|
||||
C_nonzero = _key_vector(cols)
|
||||
p = in_sk.dest
|
||||
z = crypto.sc_sub(in_sk.mask, a)
|
||||
|
||||
for i in range(cols):
|
||||
P[i] = pubs[i].dest
|
||||
C_nonzero[i] = pubs[i].commitment
|
||||
pubs[i] = None
|
||||
|
||||
del pubs
|
||||
gc.collect()
|
||||
|
||||
return _generate_clsag(message, P, p, C_nonzero, z, cout, index, mg_buff)
|
||||
|
||||
|
||||
def _generate_clsag(
|
||||
message: bytes,
|
||||
P: list[bytes],
|
||||
p: Sc25519,
|
||||
C_nonzero: list[bytes],
|
||||
z: Sc25519,
|
||||
Cout: Ge25519,
|
||||
index: int,
|
||||
mg_buff: list[bytes],
|
||||
) -> list[bytes]:
|
||||
sI = crypto.new_point() # sig.I
|
||||
sD = crypto.new_point() # sig.D
|
||||
sc1 = crypto.new_scalar() # sig.c1
|
||||
a = crypto.random_scalar()
|
||||
H = crypto.new_point()
|
||||
D = crypto.new_point()
|
||||
Cout_bf = crypto.encodepoint(Cout)
|
||||
|
||||
tmp_sc = crypto.new_scalar()
|
||||
tmp = crypto.new_point()
|
||||
tmp_bf = bytearray(32)
|
||||
|
||||
crypto.hash_to_point_into(H, P[index])
|
||||
crypto.scalarmult_into(sI, H, p) # I = p*H
|
||||
crypto.scalarmult_into(D, H, z) # D = z*H
|
||||
crypto.sc_mul_into(tmp_sc, z, crypto.sc_inv_eight()) # 1/8*z
|
||||
crypto.scalarmult_into(sD, H, tmp_sc) # sig.D = 1/8*z*H
|
||||
sD = crypto.encodepoint(sD)
|
||||
|
||||
hsh_P = crypto.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_C = crypto.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_P.update(_HASH_KEY_CLSAG_AGG_0)
|
||||
hsh_C.update(_HASH_KEY_CLSAG_AGG_1)
|
||||
|
||||
def hsh_PC(x):
|
||||
nonlocal hsh_P, hsh_C
|
||||
hsh_P.update(x)
|
||||
hsh_C.update(x)
|
||||
|
||||
for x in P:
|
||||
hsh_PC(x)
|
||||
|
||||
for x in C_nonzero:
|
||||
hsh_PC(x)
|
||||
|
||||
hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
|
||||
hsh_PC(sD)
|
||||
hsh_PC(Cout_bf)
|
||||
mu_P = crypto.decodeint(hsh_P.digest())
|
||||
mu_C = crypto.decodeint(hsh_C.digest())
|
||||
|
||||
del (hsh_PC, hsh_P, hsh_C)
|
||||
c_to_hash = crypto.get_keccak() # domain, P, C, C_offset, message, aG, aH
|
||||
c_to_hash.update(_HASH_KEY_CLSAG_ROUND)
|
||||
for i in range(len(P)):
|
||||
c_to_hash.update(P[i])
|
||||
for i in range(len(P)):
|
||||
c_to_hash.update(C_nonzero[i])
|
||||
c_to_hash.update(Cout_bf)
|
||||
c_to_hash.update(message)
|
||||
|
||||
chasher = c_to_hash.copy()
|
||||
crypto.scalarmult_base_into(tmp, a)
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aG
|
||||
crypto.scalarmult_into(tmp, H, a)
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aH
|
||||
c = crypto.decodeint(chasher.digest())
|
||||
del (chasher, H)
|
||||
|
||||
L = crypto.new_point()
|
||||
R = crypto.new_point()
|
||||
c_p = crypto.new_scalar()
|
||||
c_c = crypto.new_scalar()
|
||||
i = (index + 1) % len(P)
|
||||
if i == 0:
|
||||
crypto.sc_copy(sc1, c)
|
||||
|
||||
mg_buff.append(int_serialize.dump_uvarint_b(len(P)))
|
||||
for _ in range(len(P)):
|
||||
mg_buff.append(bytearray(32))
|
||||
|
||||
while i != index:
|
||||
crypto.random_scalar(tmp_sc)
|
||||
crypto.encodeint_into(mg_buff[i + 1], tmp_sc)
|
||||
|
||||
crypto.sc_mul_into(c_p, mu_P, c)
|
||||
crypto.sc_mul_into(c_c, mu_C, c)
|
||||
|
||||
# L = tmp_sc * G + c_P * P[i] + c_c * C[i]
|
||||
crypto.add_keys2_into(L, tmp_sc, c_p, crypto.decodepoint_into(tmp, P[i]))
|
||||
crypto.decodepoint_into(tmp, C_nonzero[i]) # C = C_nonzero - Cout
|
||||
crypto.point_sub_into(tmp, tmp, Cout)
|
||||
crypto.scalarmult_into(tmp, tmp, c_c)
|
||||
crypto.point_add_into(L, L, tmp)
|
||||
|
||||
# R = tmp_sc * HP + c_p * I + c_c * D
|
||||
crypto.hash_to_point_into(tmp, P[i])
|
||||
crypto.add_keys3_into(R, tmp_sc, tmp, c_p, sI)
|
||||
crypto.point_add_into(R, R, crypto.scalarmult_into(tmp, D, c_c))
|
||||
|
||||
chasher = c_to_hash.copy()
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, L))
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, R))
|
||||
crypto.decodeint_into(c, chasher.digest())
|
||||
|
||||
P[i] = None
|
||||
C_nonzero[i] = None
|
||||
|
||||
i = (i + 1) % len(P)
|
||||
if i == 0:
|
||||
crypto.sc_copy(sc1, c)
|
||||
|
||||
if i & 3 == 0:
|
||||
gc.collect()
|
||||
|
||||
# Final scalar = a - c * (mu_P * p + mu_c * Z)
|
||||
crypto.sc_mul_into(tmp_sc, mu_P, p)
|
||||
crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
|
||||
crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
|
||||
crypto.encodeint_into(mg_buff[index + 1], tmp_sc)
|
||||
|
||||
mg_buff.append(crypto.encodeint(sc1))
|
||||
mg_buff.append(sD)
|
||||
return mg_buff
|
||||
|
||||
|
||||
def _key_vector(rows):
|
||||
return [None] * rows
|
||||
|
||||
|
||||
def _key_matrix(rows, cols):
|
||||
"""
|
||||
first index is columns (so slightly backward from math)
|
||||
"""
|
||||
rv = [None] * cols
|
||||
for i in range(0, cols):
|
||||
rv[i] = _key_vector(rows)
|
||||
return rv
|
||||
|
||||
|
||||
def _hasher_message(message):
|
||||
"""
|
||||
Returns incremental hasher for MLSAG
|
||||
"""
|
||||
ctx = crypto.get_keccak()
|
||||
ctx.update(message)
|
||||
return ctx
|
||||
|
||||
|
||||
def _hash_point(hasher, point, tmp_buff):
|
||||
crypto.encodepoint_into(tmp_buff, point)
|
||||
hasher.update(tmp_buff)
|
@ -1,10 +1,12 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto_helpers
|
||||
from apps.monero.xmr.keccak_hasher import KeccakXmrArchive
|
||||
|
||||
from .serialize_messages.tx_rsig_bulletproof import Bulletproof
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof
|
||||
from trezor.utils import HashContext
|
||||
|
||||
|
||||
class PreMlsagHasher:
|
||||
@ -12,50 +14,50 @@ class PreMlsagHasher:
|
||||
Iterative construction of the pre_mlsag_hash
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.state = 0
|
||||
self.kc_master = crypto.get_keccak()
|
||||
self.rsig_hasher = crypto.get_keccak()
|
||||
self.rtcsig_hasher = KeccakXmrArchive()
|
||||
self.kc_master: HashContext = crypto_helpers.get_keccak()
|
||||
self.rsig_hasher: HashContext = crypto_helpers.get_keccak()
|
||||
self.rtcsig_hasher: KeccakXmrArchive = KeccakXmrArchive()
|
||||
|
||||
def init(self):
|
||||
def init(self) -> None:
|
||||
if self.state != 0:
|
||||
raise ValueError("State error")
|
||||
|
||||
self.state = 1
|
||||
|
||||
def set_message(self, message: bytes):
|
||||
def set_message(self, message: bytes) -> None:
|
||||
self.kc_master.update(message)
|
||||
|
||||
def set_type_fee(self, rv_type: int, fee: int):
|
||||
def set_type_fee(self, rv_type: int, fee: int) -> None:
|
||||
if self.state != 1:
|
||||
raise ValueError("State error")
|
||||
self.state = 2
|
||||
self.rtcsig_hasher.uint(rv_type, 1) # UInt8
|
||||
self.rtcsig_hasher.uvarint(fee) # UVarintType
|
||||
|
||||
def set_ecdh(self, ecdh: bytes):
|
||||
def set_ecdh(self, ecdh: bytes) -> None:
|
||||
if self.state not in (2, 3, 4):
|
||||
raise ValueError("State error")
|
||||
self.state = 4
|
||||
self.rtcsig_hasher.buffer(ecdh)
|
||||
|
||||
def set_out_pk_commitment(self, out_pk_commitment: bytes):
|
||||
def set_out_pk_commitment(self, out_pk_commitment: bytes) -> None:
|
||||
if self.state not in (4, 5):
|
||||
raise ValueError("State error")
|
||||
self.state = 5
|
||||
self.rtcsig_hasher.buffer(out_pk_commitment) # ECKey
|
||||
|
||||
def rctsig_base_done(self):
|
||||
def rctsig_base_done(self) -> None:
|
||||
if self.state != 5:
|
||||
raise ValueError("State error")
|
||||
self.state = 6
|
||||
|
||||
c_hash = self.rtcsig_hasher.get_digest()
|
||||
self.kc_master.update(c_hash)
|
||||
self.rtcsig_hasher = None
|
||||
self.rtcsig_hasher = None # type: ignore
|
||||
|
||||
def rsig_val(self, p: bytes | list[bytes] | Bulletproof, raw: bool = False):
|
||||
def rsig_val(self, p: bytes | list[bytes] | Bulletproof, raw: bool = False) -> None:
|
||||
if self.state == 8:
|
||||
raise ValueError("State error")
|
||||
|
||||
@ -68,9 +70,12 @@ class PreMlsagHasher:
|
||||
for x in p:
|
||||
self.rsig_hasher.update(x)
|
||||
else:
|
||||
assert isinstance(p, bytes)
|
||||
self.rsig_hasher.update(p)
|
||||
return
|
||||
|
||||
assert isinstance(p, Bulletproof)
|
||||
|
||||
# Hash Bulletproof
|
||||
self.rsig_hasher.update(p.A)
|
||||
self.rsig_hasher.update(p.S)
|
||||
@ -92,7 +97,7 @@ class PreMlsagHasher:
|
||||
self.state = 8
|
||||
|
||||
c_hash = self.rsig_hasher.digest()
|
||||
self.rsig_hasher = None
|
||||
self.rsig_hasher = None # type: ignore
|
||||
|
||||
self.kc_master.update(c_hash)
|
||||
return self.kc_master.digest()
|
||||
|
@ -1,9 +1,8 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto, crypto_helpers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Ge25519, Sc25519
|
||||
from apps.monero.xmr.credentials import AccountCreds
|
||||
|
||||
Subaddresses = dict[bytes, tuple[int, int]]
|
||||
@ -17,7 +16,9 @@ class XmrNoSuchAddressException(XmrException):
|
||||
pass
|
||||
|
||||
|
||||
def get_subaddress_secret_key(secret_key: Sc25519, major: int = 0, minor: int = 0):
|
||||
def get_subaddress_secret_key(
|
||||
secret_key: crypto.Scalar, major: int = 0, minor: int = 0
|
||||
) -> crypto.Scalar:
|
||||
"""
|
||||
Builds subaddress secret key from the subaddress index
|
||||
Hs(SubAddr || a || index_major || index_minor)
|
||||
@ -25,12 +26,12 @@ def get_subaddress_secret_key(secret_key: Sc25519, major: int = 0, minor: int =
|
||||
if major == 0 and minor == 0:
|
||||
return secret_key
|
||||
|
||||
return crypto.get_subaddress_secret_key(secret_key, major, minor)
|
||||
return crypto_helpers.get_subaddress_secret_key(secret_key, major, minor)
|
||||
|
||||
|
||||
def get_subaddress_spend_public_key(
|
||||
view_private: Sc25519, spend_public: Ge25519, major: int, minor: int
|
||||
) -> Ge25519:
|
||||
view_private: crypto.Scalar, spend_public: crypto.Point, major: int, minor: int
|
||||
) -> crypto.Point:
|
||||
"""
|
||||
Generates subaddress spend public key D_{major, minor}
|
||||
"""
|
||||
@ -38,43 +39,43 @@ def get_subaddress_spend_public_key(
|
||||
return spend_public
|
||||
|
||||
m = get_subaddress_secret_key(view_private, major=major, minor=minor)
|
||||
M = crypto.scalarmult_base(m)
|
||||
D = crypto.point_add(spend_public, M)
|
||||
M = crypto.scalarmult_base_into(None, m)
|
||||
D = crypto.point_add_into(None, spend_public, M)
|
||||
return D
|
||||
|
||||
|
||||
def derive_subaddress_public_key(
|
||||
out_key: Ge25519, derivation: Ge25519, output_index: int
|
||||
) -> Ge25519:
|
||||
out_key: crypto.Point, derivation: crypto.Point, output_index: int
|
||||
) -> crypto.Point:
|
||||
"""
|
||||
out_key - H_s(derivation || varint(output_index))G
|
||||
"""
|
||||
crypto.check_ed25519point(out_key)
|
||||
scalar = crypto.derivation_to_scalar(derivation, output_index)
|
||||
point2 = crypto.scalarmult_base(scalar)
|
||||
point4 = crypto.point_sub(out_key, point2)
|
||||
crypto.ge25519_check(out_key)
|
||||
scalar = crypto_helpers.derivation_to_scalar(derivation, output_index)
|
||||
point2 = crypto.scalarmult_base_into(None, scalar)
|
||||
point4 = crypto.point_sub_into(None, out_key, point2)
|
||||
return point4
|
||||
|
||||
|
||||
def generate_key_image(public_key: bytes, secret_key: Sc25519) -> Ge25519:
|
||||
def generate_key_image(public_key: bytes, secret_key: crypto.Scalar) -> crypto.Point:
|
||||
"""
|
||||
Key image: secret_key * H_p(pub_key)
|
||||
"""
|
||||
point = crypto.hash_to_point(public_key)
|
||||
point2 = crypto.scalarmult(point, secret_key)
|
||||
point = crypto.hash_to_point_into(None, public_key)
|
||||
point2 = crypto.scalarmult_into(None, point, secret_key)
|
||||
return point2
|
||||
|
||||
|
||||
def is_out_to_account(
|
||||
subaddresses: Subaddresses,
|
||||
out_key: Ge25519,
|
||||
derivation: Ge25519,
|
||||
additional_derivation: Ge25519,
|
||||
out_key: crypto.Point,
|
||||
derivation: crypto.Point,
|
||||
additional_derivation: crypto.Point | None,
|
||||
output_index: int,
|
||||
creds: AccountCreds | None = None,
|
||||
sub_addr_major: int = None,
|
||||
sub_addr_minor: int = None,
|
||||
):
|
||||
creds: AccountCreds | None,
|
||||
sub_addr_major: int | None,
|
||||
sub_addr_minor: int | None,
|
||||
) -> tuple[tuple[int, int], crypto.Point] | None:
|
||||
"""
|
||||
Checks whether the given transaction is sent to the account.
|
||||
Searches subaddresses for the computed subaddress_spendkey.
|
||||
@ -101,7 +102,7 @@ def is_out_to_account(
|
||||
return (sub_addr_major, sub_addr_minor), derivation
|
||||
|
||||
if subaddresses:
|
||||
subaddress_spendkey = crypto.encodepoint(subaddress_spendkey_obj)
|
||||
subaddress_spendkey = crypto_helpers.encodepoint(subaddress_spendkey_obj)
|
||||
if subaddress_spendkey in subaddresses:
|
||||
return subaddresses[subaddress_spendkey], derivation
|
||||
|
||||
@ -111,10 +112,12 @@ def is_out_to_account(
|
||||
)
|
||||
|
||||
if sub_pub_key and crypto.point_eq(subaddress_spendkey_obj, sub_pub_key):
|
||||
# sub_pub_key is only set if sub_addr_{major, minor} are set
|
||||
assert sub_addr_major is not None and sub_addr_minor is not None
|
||||
return (sub_addr_major, sub_addr_minor), additional_derivation
|
||||
|
||||
if subaddresses:
|
||||
subaddress_spendkey = crypto.encodepoint(subaddress_spendkey_obj)
|
||||
subaddress_spendkey = crypto_helpers.encodepoint(subaddress_spendkey_obj)
|
||||
if subaddress_spendkey in subaddresses:
|
||||
return subaddresses[subaddress_spendkey], additional_derivation
|
||||
|
||||
@ -123,11 +126,11 @@ def is_out_to_account(
|
||||
|
||||
def generate_tx_spend_and_key_image(
|
||||
ack: AccountCreds,
|
||||
out_key: Ge25519,
|
||||
recv_derivation: Ge25519,
|
||||
out_key: crypto.Point,
|
||||
recv_derivation: crypto.Point,
|
||||
real_output_index: int,
|
||||
received_index: tuple[int, int],
|
||||
) -> tuple[Sc25519, Ge25519] | None:
|
||||
) -> tuple[crypto.Scalar, crypto.Point]:
|
||||
"""
|
||||
Generates UTXO spending key and key image.
|
||||
Corresponds to generate_key_image_helper_precomp() in the Monero codebase.
|
||||
@ -140,11 +143,11 @@ def generate_tx_spend_and_key_image(
|
||||
:param received_index: subaddress index this payment was received to
|
||||
:return:
|
||||
"""
|
||||
if not crypto.sc_isnonzero(ack.spend_key_private):
|
||||
if crypto.sc_iszero(ack.spend_key_private):
|
||||
raise ValueError("Watch-only wallet not supported")
|
||||
|
||||
# derive secret key with subaddress - step 1: original CN derivation
|
||||
scalar_step1 = crypto.derive_secret_key(
|
||||
scalar_step1 = crypto_helpers.derive_secret_key(
|
||||
recv_derivation, real_output_index, ack.spend_key_private
|
||||
)
|
||||
|
||||
@ -156,10 +159,10 @@ def generate_tx_spend_and_key_image(
|
||||
subaddr_sk = get_subaddress_secret_key(
|
||||
ack.view_key_private, major=received_index[0], minor=received_index[1]
|
||||
)
|
||||
scalar_step2 = crypto.sc_add(scalar_step1, subaddr_sk)
|
||||
scalar_step2 = crypto.sc_add_into(None, scalar_step1, subaddr_sk)
|
||||
|
||||
# When not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
|
||||
pub_ver = crypto.scalarmult_base(scalar_step2)
|
||||
pub_ver = crypto.scalarmult_base_into(None, scalar_step2)
|
||||
|
||||
# <Multisig>, branch deactivated until implemented
|
||||
# # When in multisig, we only know the partial spend secret key. But we do know the full spend public key,
|
||||
@ -179,20 +182,20 @@ def generate_tx_spend_and_key_image(
|
||||
"key image helper precomp: given output pubkey doesn't match the derived one"
|
||||
)
|
||||
|
||||
ki = generate_key_image(crypto.encodepoint(pub_ver), scalar_step2)
|
||||
ki = generate_key_image(crypto_helpers.encodepoint(pub_ver), scalar_step2)
|
||||
return scalar_step2, ki
|
||||
|
||||
|
||||
def generate_tx_spend_and_key_image_and_derivation(
|
||||
creds: AccountCreds,
|
||||
subaddresses: Subaddresses,
|
||||
out_key: Ge25519,
|
||||
tx_public_key: Ge25519,
|
||||
additional_tx_public_key: Ge25519,
|
||||
real_output_index: int,
|
||||
sub_addr_major: int = None,
|
||||
sub_addr_minor: int = None,
|
||||
) -> tuple[Sc25519, Ge25519, Ge25519]:
|
||||
out_key: crypto.Point,
|
||||
tx_public_key: crypto.Point,
|
||||
additional_tx_public_key: crypto.Point | None,
|
||||
real_output_index: int | None,
|
||||
sub_addr_major: int | None,
|
||||
sub_addr_minor: int | None,
|
||||
) -> tuple[crypto.Scalar, crypto.Point, crypto.Point]:
|
||||
"""
|
||||
Generates UTXO spending key and key image and corresponding derivation.
|
||||
Supports subaddresses.
|
||||
@ -208,12 +211,14 @@ def generate_tx_spend_and_key_image_and_derivation(
|
||||
:param sub_addr_minor: subaddress minor index
|
||||
:return:
|
||||
"""
|
||||
recv_derivation = crypto.generate_key_derivation(
|
||||
recv_derivation = crypto_helpers.generate_key_derivation(
|
||||
tx_public_key, creds.view_key_private
|
||||
)
|
||||
|
||||
additional_recv_derivation = (
|
||||
crypto.generate_key_derivation(additional_tx_public_key, creds.view_key_private)
|
||||
crypto_helpers.generate_key_derivation(
|
||||
additional_tx_public_key, creds.view_key_private
|
||||
)
|
||||
if additional_tx_public_key
|
||||
else None
|
||||
)
|
||||
@ -257,56 +262,55 @@ def compute_subaddresses(
|
||||
|
||||
for idx in indices:
|
||||
if account == 0 and idx == 0:
|
||||
subaddresses[crypto.encodepoint(creds.spend_key_public)] = (0, 0)
|
||||
subaddresses[crypto_helpers.encodepoint(creds.spend_key_public)] = (0, 0)
|
||||
continue
|
||||
|
||||
pub = get_subaddress_spend_public_key(
|
||||
creds.view_key_private, creds.spend_key_public, major=account, minor=idx
|
||||
)
|
||||
pub = crypto.encodepoint(pub)
|
||||
pub = crypto_helpers.encodepoint(pub)
|
||||
subaddresses[pub] = (account, idx)
|
||||
return subaddresses
|
||||
|
||||
|
||||
def generate_keys(recovery_key: Sc25519) -> tuple[Sc25519, Ge25519]:
|
||||
pub = crypto.scalarmult_base(recovery_key)
|
||||
def generate_keys(recovery_key: crypto.Scalar) -> tuple[crypto.Scalar, crypto.Point]:
|
||||
pub = crypto.scalarmult_base_into(None, recovery_key)
|
||||
return recovery_key, pub
|
||||
|
||||
|
||||
def generate_monero_keys(seed: bytes) -> tuple[Sc25519, Ge25519, Sc25519, Ge25519]:
|
||||
def generate_monero_keys(
|
||||
seed: bytes,
|
||||
) -> tuple[crypto.Scalar, crypto.Point, crypto.Scalar, crypto.Point]:
|
||||
"""
|
||||
Generates spend key / view key from the seed in the same manner as Monero code does.
|
||||
|
||||
account.cpp:
|
||||
crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random).
|
||||
"""
|
||||
spend_sec, spend_pub = generate_keys(crypto.decodeint(seed))
|
||||
hash = crypto.cn_fast_hash(crypto.encodeint(spend_sec))
|
||||
view_sec, view_pub = generate_keys(crypto.decodeint(hash))
|
||||
spend_sec, spend_pub = generate_keys(crypto_helpers.decodeint(seed))
|
||||
hash = crypto.fast_hash_into(None, crypto_helpers.encodeint(spend_sec))
|
||||
view_sec, view_pub = generate_keys(crypto_helpers.decodeint(hash))
|
||||
return spend_sec, spend_pub, view_sec, view_pub
|
||||
|
||||
|
||||
def generate_sub_address_keys(
|
||||
view_sec: Sc25519, spend_pub: Ge25519, major: int, minor: int
|
||||
) -> tuple[Ge25519, Ge25519]:
|
||||
view_sec: crypto.Scalar, spend_pub: crypto.Point, major: int, minor: int
|
||||
) -> tuple[crypto.Point, crypto.Point]:
|
||||
if major == 0 and minor == 0: # special case, Monero-defined
|
||||
return spend_pub, crypto.scalarmult_base(view_sec)
|
||||
return spend_pub, crypto.scalarmult_base_into(None, view_sec)
|
||||
|
||||
m = get_subaddress_secret_key(view_sec, major=major, minor=minor)
|
||||
M = crypto.scalarmult_base(m)
|
||||
D = crypto.point_add(spend_pub, M)
|
||||
C = crypto.scalarmult(D, view_sec)
|
||||
M = crypto.scalarmult_base_into(None, m)
|
||||
D = crypto.point_add_into(None, spend_pub, M)
|
||||
C = crypto.scalarmult_into(None, D, view_sec)
|
||||
return D, C
|
||||
|
||||
|
||||
def commitment_mask(key: bytes, buff: Sc25519 | None = None) -> Sc25519:
|
||||
def commitment_mask(key: bytes, buff: crypto.Scalar | None = None) -> crypto.Scalar:
|
||||
"""
|
||||
Generates deterministic commitment mask for Bulletproof2
|
||||
"""
|
||||
data = bytearray(15 + 32)
|
||||
data[0:15] = b"commitment_mask"
|
||||
data[15:] = key
|
||||
if buff:
|
||||
return crypto.hash_to_scalar_into(buff, data)
|
||||
else:
|
||||
return crypto.hash_to_scalar(data)
|
||||
|
@ -1,8 +1,4 @@
|
||||
class NetworkTypes:
|
||||
MAINNET = 0
|
||||
TESTNET = 1
|
||||
STAGENET = 2
|
||||
FAKECHAIN = 3
|
||||
from trezor.enums import MoneroNetworkType
|
||||
|
||||
|
||||
class MainNet:
|
||||
@ -24,8 +20,10 @@ class StageNet:
|
||||
|
||||
|
||||
def net_version(
|
||||
network_type=NetworkTypes.MAINNET, is_subaddr=False, is_integrated=False
|
||||
):
|
||||
network_type: MoneroNetworkType = MoneroNetworkType.MAINNET,
|
||||
is_subaddr: bool = False,
|
||||
is_integrated: bool = False,
|
||||
) -> bytes:
|
||||
"""
|
||||
Network version bytes used for address construction
|
||||
"""
|
||||
@ -33,11 +31,11 @@ def net_version(
|
||||
raise ValueError("Subaddress cannot be integrated")
|
||||
|
||||
c_net = None
|
||||
if network_type is None or network_type == NetworkTypes.MAINNET:
|
||||
if network_type == MoneroNetworkType.MAINNET:
|
||||
c_net = MainNet
|
||||
elif network_type == NetworkTypes.TESTNET:
|
||||
elif network_type == MoneroNetworkType.TESTNET:
|
||||
c_net = TestNet
|
||||
elif network_type == NetworkTypes.STAGENET:
|
||||
elif network_type == MoneroNetworkType.STAGENET:
|
||||
c_net = StageNet
|
||||
else:
|
||||
raise ValueError(f"Unknown network type: {network_type}")
|
||||
|
@ -11,35 +11,36 @@ Author: Dusan Klinec, ph4r05, 2018
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr import crypto
|
||||
from apps.monero.xmr import crypto, crypto_helpers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.monero.xmr.types import Sc25519
|
||||
from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof
|
||||
|
||||
|
||||
def prove_range_bp_batch(amounts: list[int], masks: list[Sc25519]) -> Bulletproof:
|
||||
def prove_range_bp_batch(amounts: list[int], masks: list[crypto.Scalar]) -> Bulletproof:
|
||||
"""Calculates Bulletproof in batches"""
|
||||
from apps.monero.xmr import bulletproof as bp
|
||||
|
||||
bpi = bp.BulletProofBuilder()
|
||||
bp_proof = bpi.prove_batch([crypto.sc_init(a) for a in amounts], masks)
|
||||
bp_proof = bpi.prove_batch([crypto.Scalar(a) for a in amounts], masks)
|
||||
del (bpi, bp)
|
||||
gc.collect()
|
||||
|
||||
return bp_proof
|
||||
|
||||
|
||||
def verify_bp(bp_proof: Bulletproof, amounts: list[int], masks: list[Sc25519]) -> bool:
|
||||
def verify_bp(
|
||||
bp_proof: Bulletproof, amounts: list[int], masks: list[crypto.Scalar]
|
||||
) -> bool:
|
||||
"""Verifies Bulletproof"""
|
||||
from apps.monero.xmr import bulletproof as bp
|
||||
|
||||
if amounts:
|
||||
bp_proof.V = []
|
||||
for i in range(len(amounts)):
|
||||
C = crypto.gen_commitment(masks[i], amounts[i])
|
||||
crypto.scalarmult_into(C, C, crypto.sc_inv_eight())
|
||||
bp_proof.V.append(crypto.encodepoint(C))
|
||||
C = crypto.gen_commitment_into(None, masks[i], amounts[i])
|
||||
crypto.scalarmult_into(C, C, crypto_helpers.INV_EIGHT_SC)
|
||||
bp_proof.V.append(crypto_helpers.encodepoint(C))
|
||||
|
||||
bpi = bp.BulletProofBuilder()
|
||||
res = bpi.verify(bp_proof)
|
||||
|
@ -1,14 +1,24 @@
|
||||
import gc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import TypeVar
|
||||
|
||||
from .message_types import MessageType
|
||||
|
||||
T = TypeVar("T", bound=MessageType)
|
||||
|
||||
|
||||
def parse_msg(buf: bytes, msg_type):
|
||||
def parse_msg(buf: bytes, msg_type: type[T]) -> T:
|
||||
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter
|
||||
|
||||
reader = MemoryReaderWriter(memoryview(buf))
|
||||
return msg_type.load(reader)
|
||||
|
||||
|
||||
def dump_msg(msg, preallocate: int = None, prefix: bytes = None) -> bytes:
|
||||
def dump_msg(
|
||||
msg: MessageType, preallocate: int | None = None, prefix: bytes | None = None
|
||||
) -> bytes:
|
||||
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter
|
||||
|
||||
writer = MemoryReaderWriter(preallocate=preallocate)
|
||||
@ -20,7 +30,9 @@ def dump_msg(msg, preallocate: int = None, prefix: bytes = None) -> bytes:
|
||||
return writer.get_buffer()
|
||||
|
||||
|
||||
def dump_msg_gc(msg, preallocate: int = None, prefix: bytes = None) -> bytes:
|
||||
def dump_msg_gc(
|
||||
msg: MessageType, preallocate: int | None = None, prefix: bytes | None = None
|
||||
) -> bytes:
|
||||
buf = dump_msg(msg, preallocate, prefix)
|
||||
del msg
|
||||
gc.collect()
|
||||
|
@ -1,3 +1,5 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr.serialize.int_serialize import (
|
||||
dump_uint,
|
||||
dump_uvarint,
|
||||
@ -5,30 +7,60 @@ from apps.monero.xmr.serialize.int_serialize import (
|
||||
load_uvarint,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Protocol, TypeVar, Union
|
||||
|
||||
class XmrType:
|
||||
pass
|
||||
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 XmrStructuredType(XmrType):
|
||||
def f_specs(self) -> XmrFspec:
|
||||
...
|
||||
|
||||
|
||||
class UVarintType(XmrType):
|
||||
class UVarintType:
|
||||
@staticmethod
|
||||
def load(reader) -> int:
|
||||
def load(reader: Reader) -> int:
|
||||
return load_uvarint(reader)
|
||||
|
||||
@staticmethod
|
||||
def dump(writer, n: int):
|
||||
def dump(writer: Writer, n: int) -> None:
|
||||
return dump_uvarint(writer, n)
|
||||
|
||||
|
||||
class IntType(XmrType):
|
||||
class IntType:
|
||||
WIDTH = 0
|
||||
|
||||
@classmethod
|
||||
def load(cls, reader) -> int:
|
||||
def load(cls, reader: Reader) -> int:
|
||||
return load_uint(reader, cls.WIDTH)
|
||||
|
||||
@classmethod
|
||||
def dump(cls, writer, n: int):
|
||||
def dump(cls, writer: Writer, n: int):
|
||||
return dump_uint(writer, n, cls.WIDTH)
|
||||
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .base_types import Reader, Writer
|
||||
|
||||
_UINT_BUFFER = bytearray(1)
|
||||
|
||||
|
||||
def load_uint(reader, width):
|
||||
def load_uint(reader: Reader, width: int) -> int:
|
||||
"""
|
||||
Constant-width integer serialization
|
||||
"""
|
||||
@ -15,7 +20,7 @@ def load_uint(reader, width):
|
||||
return result
|
||||
|
||||
|
||||
def dump_uint(writer, n, width):
|
||||
def dump_uint(writer: Writer, n: int, width: int) -> None:
|
||||
"""
|
||||
Constant-width integer serialization
|
||||
"""
|
||||
@ -26,7 +31,7 @@ def dump_uint(writer, n, width):
|
||||
n >>= 8
|
||||
|
||||
|
||||
def uvarint_size(n):
|
||||
def uvarint_size(n: int) -> int:
|
||||
"""
|
||||
Returns size in bytes n would occupy serialized as varint
|
||||
"""
|
||||
@ -37,7 +42,7 @@ def uvarint_size(n):
|
||||
return bts
|
||||
|
||||
|
||||
def load_uvarint_b(buffer):
|
||||
def load_uvarint_b(buffer: bytes) -> int:
|
||||
"""
|
||||
Variable int deserialization, synchronous from buffer.
|
||||
"""
|
||||
@ -51,7 +56,7 @@ def load_uvarint_b(buffer):
|
||||
return result
|
||||
|
||||
|
||||
def dump_uvarint_b(n):
|
||||
def dump_uvarint_b(n: int) -> bytearray:
|
||||
"""
|
||||
Serializes uvarint to the buffer
|
||||
"""
|
||||
@ -59,7 +64,7 @@ def dump_uvarint_b(n):
|
||||
return dump_uvarint_b_into(n, buffer, 0)
|
||||
|
||||
|
||||
def dump_uvarint_b_into(n, buffer, offset=0):
|
||||
def dump_uvarint_b_into(n: int, buffer: bytearray, offset: int = 0) -> bytearray:
|
||||
"""
|
||||
Serializes n as variable size integer to the provided buffer.
|
||||
"""
|
||||
@ -74,7 +79,9 @@ def dump_uvarint_b_into(n, buffer, offset=0):
|
||||
return buffer
|
||||
|
||||
|
||||
def dump_uint_b_into(n, width, buffer, offset=0):
|
||||
def dump_uint_b_into(
|
||||
n: int, width: int, buffer: bytearray, offset: int = 0
|
||||
) -> bytearray:
|
||||
"""
|
||||
Serializes fixed size integer to the buffer
|
||||
"""
|
||||
@ -84,7 +91,7 @@ def dump_uint_b_into(n, width, buffer, offset=0):
|
||||
return buffer
|
||||
|
||||
|
||||
def load_uvarint(reader):
|
||||
def load_uvarint(reader: Reader) -> int:
|
||||
buffer = _UINT_BUFFER
|
||||
result = 0
|
||||
shift = 0
|
||||
@ -97,7 +104,7 @@ def load_uvarint(reader):
|
||||
return result
|
||||
|
||||
|
||||
def dump_uvarint(writer, n):
|
||||
def dump_uvarint(writer: Writer, n: int) -> None:
|
||||
if n < 0:
|
||||
raise ValueError("Cannot dump signed value, convert it to unsigned first.")
|
||||
buffer = _UINT_BUFFER
|
||||
|
@ -1,33 +1,41 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.utils import obj_eq, obj_repr
|
||||
|
||||
from apps.monero.xmr.serialize.base_types import XmrType
|
||||
from apps.monero.xmr.serialize.int_serialize import (
|
||||
dump_uint,
|
||||
dump_uvarint,
|
||||
load_uint,
|
||||
load_uvarint,
|
||||
)
|
||||
from apps.monero.xmr.serialize.int_serialize import dump_uvarint, load_uvarint
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
from .base_types import XmrType, XmrFspec, Reader, Writer
|
||||
|
||||
T = TypeVar("T", bound=XmrType)
|
||||
MT = TypeVar("MT", bound="MessageType")
|
||||
|
||||
else:
|
||||
Generic = XmrType = [object]
|
||||
T = 0
|
||||
|
||||
|
||||
class UnicodeType(XmrType):
|
||||
class UnicodeType:
|
||||
"""
|
||||
Unicode data in UTF-8 encoding.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def dump(writer, s):
|
||||
def dump(writer: Writer, s: str) -> None:
|
||||
dump_uvarint(writer, len(s))
|
||||
writer.write(bytes(s))
|
||||
writer.write(s.encode())
|
||||
|
||||
@staticmethod
|
||||
def load(reader):
|
||||
def load(reader: Reader) -> str:
|
||||
ivalue = load_uvarint(reader)
|
||||
fvalue = bytearray(ivalue)
|
||||
reader.readinto(fvalue)
|
||||
return str(fvalue)
|
||||
|
||||
|
||||
class BlobType(XmrType):
|
||||
class BlobType:
|
||||
"""
|
||||
Binary data, represented as bytearray. BlobType is only a scheme
|
||||
descriptor. Behaves in the same way as primitive types.
|
||||
@ -37,7 +45,7 @@ class BlobType(XmrType):
|
||||
SIZE = 0
|
||||
|
||||
@classmethod
|
||||
def dump(cls, writer, elem: bytes):
|
||||
def dump(cls, writer: Writer, elem: bytes) -> None:
|
||||
if cls.FIX_SIZE:
|
||||
if cls.SIZE != len(elem):
|
||||
raise ValueError("Size mismatch")
|
||||
@ -46,7 +54,7 @@ class BlobType(XmrType):
|
||||
writer.write(elem)
|
||||
|
||||
@classmethod
|
||||
def load(cls, reader) -> bytearray:
|
||||
def load(cls, reader: Reader) -> bytes:
|
||||
if cls.FIX_SIZE:
|
||||
size = cls.SIZE
|
||||
else:
|
||||
@ -56,7 +64,7 @@ class BlobType(XmrType):
|
||||
return elem
|
||||
|
||||
|
||||
class ContainerType(XmrType):
|
||||
class ContainerType(Generic[T]):
|
||||
"""
|
||||
Array of elements, represented as a list of items. ContainerType is only a
|
||||
scheme descriptor.
|
||||
@ -64,10 +72,12 @@ class ContainerType(XmrType):
|
||||
|
||||
FIX_SIZE = 0
|
||||
SIZE = 0
|
||||
ELEM_TYPE = None
|
||||
ELEM_TYPE: XmrType[T]
|
||||
|
||||
@classmethod
|
||||
def dump(cls, writer, elems, elem_type=None):
|
||||
def dump(
|
||||
cls, writer: Writer, elems: list[T], elem_type: XmrType[T] | None = None
|
||||
) -> None:
|
||||
if elem_type is None:
|
||||
elem_type = cls.ELEM_TYPE
|
||||
if cls.FIX_SIZE:
|
||||
@ -79,7 +89,7 @@ class ContainerType(XmrType):
|
||||
elem_type.dump(writer, elem)
|
||||
|
||||
@classmethod
|
||||
def load(cls, reader, elem_type=None):
|
||||
def load(cls, reader: Reader, elem_type: XmrType[T] | None = None) -> list[T]:
|
||||
if elem_type is None:
|
||||
elem_type = cls.ELEM_TYPE
|
||||
if cls.FIX_SIZE:
|
||||
@ -93,42 +103,7 @@ class ContainerType(XmrType):
|
||||
return elems
|
||||
|
||||
|
||||
class VariantType(XmrType):
|
||||
"""
|
||||
Union of types, differentiated by variant tags. VariantType is only a scheme
|
||||
descriptor.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def dump(cls, writer, elem):
|
||||
for field in cls.f_specs():
|
||||
ftype = field[1]
|
||||
if isinstance(elem, ftype):
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"Unrecognized variant: {elem}")
|
||||
|
||||
dump_uint(writer, ftype.VARIANT_CODE, 1)
|
||||
ftype.dump(writer, elem)
|
||||
|
||||
@classmethod
|
||||
def load(cls, reader):
|
||||
tag = load_uint(reader, 1)
|
||||
for field in cls.f_specs():
|
||||
ftype = field[1]
|
||||
if ftype.VARIANT_CODE == tag:
|
||||
fvalue = ftype.load(reader)
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"Unknown tag: {tag}")
|
||||
return fvalue
|
||||
|
||||
@classmethod
|
||||
def f_specs(cls):
|
||||
return ()
|
||||
|
||||
|
||||
class MessageType(XmrType):
|
||||
class MessageType:
|
||||
"""
|
||||
Message composed of fields with specific types.
|
||||
"""
|
||||
@ -141,7 +116,7 @@ class MessageType(XmrType):
|
||||
__repr__ = obj_repr
|
||||
|
||||
@classmethod
|
||||
def dump(cls, writer, msg):
|
||||
def dump(cls: type[MT], writer: Writer, msg: MT) -> None:
|
||||
defs = cls.f_specs()
|
||||
for field in defs:
|
||||
fname, ftype, *fparams = field
|
||||
@ -149,7 +124,7 @@ class MessageType(XmrType):
|
||||
ftype.dump(writer, fvalue, *fparams)
|
||||
|
||||
@classmethod
|
||||
def load(cls, reader):
|
||||
def load(cls: type[MT], reader: Reader) -> MT:
|
||||
msg = cls()
|
||||
defs = cls.f_specs()
|
||||
for field in defs:
|
||||
@ -159,5 +134,5 @@ class MessageType(XmrType):
|
||||
return msg
|
||||
|
||||
@classmethod
|
||||
def f_specs(cls):
|
||||
def f_specs(cls) -> XmrFspec:
|
||||
return ()
|
||||
|
@ -4,14 +4,12 @@ import gc
|
||||
class MemoryReaderWriter:
|
||||
def __init__(
|
||||
self,
|
||||
buffer=None,
|
||||
read_empty=False,
|
||||
threshold=None,
|
||||
do_gc=False,
|
||||
preallocate=None,
|
||||
**kwargs
|
||||
):
|
||||
self.buffer = buffer
|
||||
buffer: bytearray | memoryview | None = None,
|
||||
read_empty: bool = False,
|
||||
threshold: int | None = None,
|
||||
do_gc: bool = False,
|
||||
preallocate: int | None = None,
|
||||
) -> None:
|
||||
self.nread = 0
|
||||
self.nwritten = 0
|
||||
|
||||
@ -25,20 +23,21 @@ class MemoryReaderWriter:
|
||||
|
||||
if preallocate is not None:
|
||||
self.preallocate(preallocate)
|
||||
elif self.buffer is None:
|
||||
elif buffer is None:
|
||||
self.buffer = bytearray(0)
|
||||
else:
|
||||
self.buffer = buffer
|
||||
self.woffset = len(buffer)
|
||||
|
||||
def is_empty(self):
|
||||
def is_empty(self) -> bool:
|
||||
return self.offset == len(self.buffer) or self.offset == self.woffset
|
||||
|
||||
def preallocate(self, size):
|
||||
def preallocate(self, size: int) -> None:
|
||||
self.buffer = bytearray(size)
|
||||
self.offset = 0
|
||||
self.woffset = 0
|
||||
|
||||
def readinto(self, buf):
|
||||
def readinto(self, buf: bytearray | memoryview) -> int:
|
||||
ln = len(buf)
|
||||
if not self.read_empty and ln > 0 and self.offset == len(self.buffer):
|
||||
raise EOFError
|
||||
@ -62,7 +61,8 @@ class MemoryReaderWriter:
|
||||
|
||||
return nread
|
||||
|
||||
def write(self, buf):
|
||||
def write(self, buf: bytes) -> None:
|
||||
assert isinstance(self.buffer, bytearray)
|
||||
nwritten = len(buf)
|
||||
nall = len(self.buffer)
|
||||
towrite = nwritten
|
||||
@ -93,8 +93,8 @@ class MemoryReaderWriter:
|
||||
|
||||
self.nwritten += nwritten
|
||||
self.ndata += nwritten
|
||||
return nwritten
|
||||
# return nwritten
|
||||
|
||||
def get_buffer(self):
|
||||
def get_buffer(self) -> bytes:
|
||||
mv = memoryview(self.buffer)
|
||||
return mv[self.offset : self.woffset]
|
||||
|
@ -1,6 +1,12 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import crypto
|
||||
|
||||
|
||||
class CtKey:
|
||||
__slots__ = ("dest", "mask")
|
||||
|
||||
def __init__(self, dest, mask):
|
||||
def __init__(self, dest: crypto.Scalar, mask: crypto.Scalar) -> None:
|
||||
self.dest = dest
|
||||
self.mask = mask
|
||||
|
@ -10,7 +10,7 @@ class TxinToKey(MessageType):
|
||||
VARIANT_CODE = const(0x2)
|
||||
|
||||
@classmethod
|
||||
def f_specs(cls):
|
||||
def f_specs(cls) -> tuple:
|
||||
return (
|
||||
("amount", UVarintType),
|
||||
("key_offsets", ContainerType, UVarintType),
|
||||
|
@ -1,19 +1,23 @@
|
||||
from micropython import const
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from apps.monero.xmr.serialize.message_types import ContainerType, MessageType
|
||||
from apps.monero.xmr.serialize_messages.base import ECKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..serialize.base_types import XmrType
|
||||
|
||||
|
||||
class _KeyV(ContainerType):
|
||||
FIX_SIZE = const(0)
|
||||
ELEM_TYPE = ECKey
|
||||
ELEM_TYPE: XmrType[bytes] = ECKey
|
||||
|
||||
|
||||
class Bulletproof(MessageType):
|
||||
__slots__ = ("A", "S", "T1", "T2", "taux", "mu", "L", "R", "a", "b", "t")
|
||||
__slots__ = ("A", "S", "T1", "T2", "taux", "mu", "L", "R", "a", "b", "t", "V")
|
||||
|
||||
@classmethod
|
||||
def f_specs(cls):
|
||||
def f_specs(cls) -> tuple:
|
||||
return (
|
||||
("A", ECKey),
|
||||
("S", ECKey),
|
||||
|
@ -1,7 +0,0 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.crypto import monero as tcry
|
||||
|
||||
Ge25519 = tcry.ge25519
|
||||
Sc25519 = tcry.bignum256modm
|
8
core/src/trezor/enums/MoneroNetworkType.py
Normal file
8
core/src/trezor/enums/MoneroNetworkType.py
Normal file
@ -0,0 +1,8 @@
|
||||
# Automatically generated by pb2py
|
||||
# fmt: off
|
||||
# isort:skip_file
|
||||
|
||||
MAINNET = 0
|
||||
TESTNET = 1
|
||||
STAGENET = 2
|
||||
FAKECHAIN = 3
|
@ -456,6 +456,12 @@ if TYPE_CHECKING:
|
||||
ARRAY = 7
|
||||
STRUCT = 8
|
||||
|
||||
class MoneroNetworkType(IntEnum):
|
||||
MAINNET = 0
|
||||
TESTNET = 1
|
||||
STAGENET = 2
|
||||
FAKECHAIN = 3
|
||||
|
||||
class NEMMosaicLevy(IntEnum):
|
||||
MosaicLevy_Absolute = 1
|
||||
MosaicLevy_Percentile = 2
|
||||
|
@ -39,6 +39,7 @@ if TYPE_CHECKING:
|
||||
from trezor.enums import FailureType # noqa: F401
|
||||
from trezor.enums import InputScriptType # noqa: F401
|
||||
from trezor.enums import MessageType # noqa: F401
|
||||
from trezor.enums import MoneroNetworkType # noqa: F401
|
||||
from trezor.enums import NEMImportanceTransferMode # noqa: F401
|
||||
from trezor.enums import NEMModificationType # noqa: F401
|
||||
from trezor.enums import NEMMosaicLevy # noqa: F401
|
||||
@ -3691,7 +3692,7 @@ if TYPE_CHECKING:
|
||||
class MoneroGetAddress(protobuf.MessageType):
|
||||
address_n: "list[int]"
|
||||
show_display: "bool | None"
|
||||
network_type: "int | None"
|
||||
network_type: "MoneroNetworkType"
|
||||
account: "int | None"
|
||||
minor: "int | None"
|
||||
payment_id: "bytes | None"
|
||||
@ -3701,7 +3702,7 @@ if TYPE_CHECKING:
|
||||
*,
|
||||
address_n: "list[int] | None" = None,
|
||||
show_display: "bool | None" = None,
|
||||
network_type: "int | None" = None,
|
||||
network_type: "MoneroNetworkType | None" = None,
|
||||
account: "int | None" = None,
|
||||
minor: "int | None" = None,
|
||||
payment_id: "bytes | None" = None,
|
||||
@ -3728,13 +3729,13 @@ if TYPE_CHECKING:
|
||||
|
||||
class MoneroGetWatchKey(protobuf.MessageType):
|
||||
address_n: "list[int]"
|
||||
network_type: "int | None"
|
||||
network_type: "MoneroNetworkType"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
address_n: "list[int] | None" = None,
|
||||
network_type: "int | None" = None,
|
||||
network_type: "MoneroNetworkType | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -3761,7 +3762,7 @@ if TYPE_CHECKING:
|
||||
class MoneroTransactionInitRequest(protobuf.MessageType):
|
||||
version: "int | None"
|
||||
address_n: "list[int]"
|
||||
network_type: "int | None"
|
||||
network_type: "MoneroNetworkType"
|
||||
tsx_data: "MoneroTransactionData | None"
|
||||
|
||||
def __init__(
|
||||
@ -3769,7 +3770,7 @@ if TYPE_CHECKING:
|
||||
*,
|
||||
address_n: "list[int] | None" = None,
|
||||
version: "int | None" = None,
|
||||
network_type: "int | None" = None,
|
||||
network_type: "MoneroNetworkType | None" = None,
|
||||
tsx_data: "MoneroTransactionData | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
@ -4051,20 +4052,20 @@ if TYPE_CHECKING:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class MoneroKeyImageExportInitRequest(protobuf.MessageType):
|
||||
num: "int | None"
|
||||
hash: "bytes | None"
|
||||
num: "int"
|
||||
hash: "bytes"
|
||||
address_n: "list[int]"
|
||||
network_type: "int | None"
|
||||
network_type: "MoneroNetworkType"
|
||||
subs: "list[MoneroSubAddressIndicesList]"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
num: "int",
|
||||
hash: "bytes",
|
||||
address_n: "list[int] | None" = None,
|
||||
subs: "list[MoneroSubAddressIndicesList] | None" = None,
|
||||
num: "int | None" = None,
|
||||
hash: "bytes | None" = None,
|
||||
network_type: "int | None" = None,
|
||||
network_type: "MoneroNetworkType | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -4128,23 +4129,23 @@ if TYPE_CHECKING:
|
||||
|
||||
class MoneroGetTxKeyRequest(protobuf.MessageType):
|
||||
address_n: "list[int]"
|
||||
network_type: "int | None"
|
||||
salt1: "bytes | None"
|
||||
salt2: "bytes | None"
|
||||
tx_enc_keys: "bytes | None"
|
||||
tx_prefix_hash: "bytes | None"
|
||||
network_type: "MoneroNetworkType"
|
||||
salt1: "bytes"
|
||||
salt2: "bytes"
|
||||
tx_enc_keys: "bytes"
|
||||
tx_prefix_hash: "bytes"
|
||||
reason: "int | None"
|
||||
view_public_key: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
salt1: "bytes",
|
||||
salt2: "bytes",
|
||||
tx_enc_keys: "bytes",
|
||||
tx_prefix_hash: "bytes",
|
||||
address_n: "list[int] | None" = None,
|
||||
network_type: "int | None" = None,
|
||||
salt1: "bytes | None" = None,
|
||||
salt2: "bytes | None" = None,
|
||||
tx_enc_keys: "bytes | None" = None,
|
||||
tx_prefix_hash: "bytes | None" = None,
|
||||
network_type: "MoneroNetworkType | None" = None,
|
||||
reason: "int | None" = None,
|
||||
view_public_key: "bytes | None" = None,
|
||||
) -> None:
|
||||
@ -4174,13 +4175,13 @@ if TYPE_CHECKING:
|
||||
|
||||
class MoneroLiveRefreshStartRequest(protobuf.MessageType):
|
||||
address_n: "list[int]"
|
||||
network_type: "int | None"
|
||||
network_type: "MoneroNetworkType"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
address_n: "list[int] | None" = None,
|
||||
network_type: "int | None" = None,
|
||||
network_type: "MoneroNetworkType | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -4195,20 +4196,20 @@ if TYPE_CHECKING:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class MoneroLiveRefreshStepRequest(protobuf.MessageType):
|
||||
out_key: "bytes | None"
|
||||
recv_deriv: "bytes | None"
|
||||
real_out_idx: "int | None"
|
||||
sub_addr_major: "int | None"
|
||||
sub_addr_minor: "int | None"
|
||||
out_key: "bytes"
|
||||
recv_deriv: "bytes"
|
||||
real_out_idx: "int"
|
||||
sub_addr_major: "int"
|
||||
sub_addr_minor: "int"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
out_key: "bytes | None" = None,
|
||||
recv_deriv: "bytes | None" = None,
|
||||
real_out_idx: "int | None" = None,
|
||||
sub_addr_major: "int | None" = None,
|
||||
sub_addr_minor: "int | None" = None,
|
||||
out_key: "bytes",
|
||||
recv_deriv: "bytes",
|
||||
real_out_idx: "int",
|
||||
sub_addr_major: "int",
|
||||
sub_addr_minor: "int",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -4329,14 +4330,14 @@ if TYPE_CHECKING:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class MoneroRctKeyPublic(protobuf.MessageType):
|
||||
dest: "bytes | None"
|
||||
commitment: "bytes | None"
|
||||
dest: "bytes"
|
||||
commitment: "bytes"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
dest: "bytes | None" = None,
|
||||
commitment: "bytes | None" = None,
|
||||
dest: "bytes",
|
||||
commitment: "bytes",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -4421,14 +4422,14 @@ if TYPE_CHECKING:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class MoneroSubAddressIndicesList(protobuf.MessageType):
|
||||
account: "int | None"
|
||||
account: "int"
|
||||
minor_indices: "list[int]"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
account: "int",
|
||||
minor_indices: "list[int] | None" = None,
|
||||
account: "int | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -4437,20 +4438,20 @@ if TYPE_CHECKING:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class MoneroTransferDetails(protobuf.MessageType):
|
||||
out_key: "bytes | None"
|
||||
tx_pub_key: "bytes | None"
|
||||
out_key: "bytes"
|
||||
tx_pub_key: "bytes"
|
||||
additional_tx_pub_keys: "list[bytes]"
|
||||
internal_output_index: "int | None"
|
||||
internal_output_index: "int"
|
||||
sub_addr_major: "int | None"
|
||||
sub_addr_minor: "int | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
out_key: "bytes",
|
||||
tx_pub_key: "bytes",
|
||||
internal_output_index: "int",
|
||||
additional_tx_pub_keys: "list[bytes] | None" = None,
|
||||
out_key: "bytes | None" = None,
|
||||
tx_pub_key: "bytes | None" = None,
|
||||
internal_output_index: "int | None" = None,
|
||||
sub_addr_major: "int | None" = None,
|
||||
sub_addr_minor: "int | None" = None,
|
||||
) -> None:
|
||||
|
@ -12,8 +12,8 @@ class TestMoneroBulletproof(unittest.TestCase):
|
||||
pass
|
||||
|
||||
def mask_consistency_check(self, bpi):
|
||||
sv = [crypto.sc_init(123)]
|
||||
gamma = [crypto.sc_init(432)]
|
||||
sv = [crypto.Scalar(123)]
|
||||
gamma = [crypto.Scalar(432)]
|
||||
|
||||
M, logM, aL, aR, V, gamma = bpi.prove_setup(sv, gamma)
|
||||
x = bp._ensure_dst_key()
|
||||
@ -293,15 +293,15 @@ class TestMoneroBulletproof(unittest.TestCase):
|
||||
|
||||
def test_prove(self):
|
||||
bpi = bp.BulletProofBuilder()
|
||||
val = crypto.sc_init(123)
|
||||
mask = crypto.sc_init(432)
|
||||
val = crypto.Scalar(123)
|
||||
mask = crypto.Scalar(432)
|
||||
|
||||
bp_res = bpi.prove(val, mask)
|
||||
bpi.verify(bp_res)
|
||||
|
||||
def test_prove_2(self):
|
||||
bpi = bp.BulletProofBuilder()
|
||||
val = crypto.sc_init((1 << 30) - 1 + 16)
|
||||
val = crypto.Scalar((1 << 30) - 1 + 16)
|
||||
mask = crypto.random_scalar()
|
||||
|
||||
bp_res = bpi.prove(val, mask)
|
||||
@ -322,7 +322,7 @@ class TestMoneroBulletproof(unittest.TestCase):
|
||||
def test_prove_random_masks(self):
|
||||
bpi = bp.BulletProofBuilder()
|
||||
bpi.use_det_masks = False # trully randomly generated mask vectors
|
||||
val = crypto.sc_init((1 << 30) - 1 + 16)
|
||||
val = crypto.Scalar((1 << 30) - 1 + 16)
|
||||
mask = crypto.random_scalar()
|
||||
|
||||
bp_res = bpi.prove(val, mask)
|
||||
@ -331,8 +331,8 @@ class TestMoneroBulletproof(unittest.TestCase):
|
||||
def ctest_multiexp(self):
|
||||
scalars = [0, 1, 2, 3, 4, 99]
|
||||
point_base = [0, 2, 4, 7, 12, 18]
|
||||
scalar_sc = [crypto.sc_init(x) for x in scalars]
|
||||
points = [crypto.scalarmult_base(crypto.sc_init(x)) for x in point_base]
|
||||
scalar_sc = [crypto.Scalar(x) for x in scalars]
|
||||
points = [crypto.scalarmult_base_into(None, crypto.Scalar(x)) for x in point_base]
|
||||
|
||||
muex = bp.MultiExp(scalars=[crypto.encodeint(x) for x in scalar_sc],
|
||||
point_fnc=lambda i, d: crypto.encodepoint(points[i]))
|
||||
@ -340,24 +340,24 @@ class TestMoneroBulletproof(unittest.TestCase):
|
||||
self.assertEqual(len(muex), len(scalars))
|
||||
res = bp.multiexp(None, muex)
|
||||
res2 = bp.vector_exponent_custom(
|
||||
A=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base(crypto.sc_init(point_base[i])), d)),
|
||||
B=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base(crypto.sc_init(point_base[3+i])), d)),
|
||||
a=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.sc_init(scalars[i]), d),),
|
||||
b=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.sc_init(scalars[i+3]), d)),
|
||||
A=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base_into(None, crypto.Scalar(point_base[i])), d)),
|
||||
B=bp.KeyVEval(3, lambda i, d: crypto.encodepoint_into(crypto.scalarmult_base_into(None, crypto.Scalar(point_base[3+i])), d)),
|
||||
a=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.Scalar(scalars[i]), d),),
|
||||
b=bp.KeyVEval(3, lambda i, d: crypto.encodeint_into(crypto.Scalar(scalars[i+3]), d)),
|
||||
)
|
||||
self.assertEqual(res, res2)
|
||||
|
||||
def test_prove_batch(self):
|
||||
bpi = bp.BulletProofBuilder()
|
||||
sv = [crypto.sc_init(123), crypto.sc_init(768)]
|
||||
gamma = [crypto.sc_init(456), crypto.sc_init(901)]
|
||||
sv = [crypto.Scalar(123), crypto.Scalar(768)]
|
||||
gamma = [crypto.Scalar(456), crypto.Scalar(901)]
|
||||
proof = bpi.prove_batch(sv, gamma)
|
||||
bpi.verify_batch([proof])
|
||||
|
||||
def test_prove_batch16(self):
|
||||
bpi = bp.BulletProofBuilder()
|
||||
sv = [crypto.sc_init(137*i) for i in range(16)]
|
||||
gamma = [crypto.sc_init(991*i) for i in range(16)]
|
||||
sv = [crypto.Scalar(137*i) for i in range(16)]
|
||||
gamma = [crypto.Scalar(991*i) for i in range(16)]
|
||||
proof = bpi.prove_batch(sv, gamma)
|
||||
bpi.verify_batch([proof])
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
from common import *
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
from apps.monero.xmr import crypto, mlsag
|
||||
from apps.monero.xmr import crypto, crypto_helpers, clsag
|
||||
from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey
|
||||
from trezor.crypto import monero as tcry
|
||||
from trezor.crypto import random
|
||||
import ubinascii
|
||||
|
||||
point_mul8_into = tcry.ge25519_mul8
|
||||
|
||||
|
||||
class TmpKey:
|
||||
def __init__(self, d, c):
|
||||
@ -17,18 +20,18 @@ class TmpKey:
|
||||
class TestMoneroClsag(unittest.TestCase):
|
||||
def verify_clsag(self, msg, ss, sc1, sI, sD, pubs, C_offset):
|
||||
n = len(pubs)
|
||||
c = crypto.new_scalar()
|
||||
D_8 = crypto.new_point()
|
||||
c = crypto.Scalar()
|
||||
D_8 = crypto.Point()
|
||||
tmp_bf = bytearray(32)
|
||||
C_offset_bf = crypto.encodepoint(C_offset)
|
||||
C_offset_bf = crypto_helpers.encodepoint(C_offset)
|
||||
|
||||
crypto.sc_copy(c, sc1)
|
||||
crypto.point_mul8_into(D_8, sD)
|
||||
point_mul8_into(D_8, sD)
|
||||
|
||||
hsh_P = crypto.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_C = crypto.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_P.update(mlsag._HASH_KEY_CLSAG_AGG_0)
|
||||
hsh_C.update(mlsag._HASH_KEY_CLSAG_AGG_1)
|
||||
hsh_P = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_C = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset
|
||||
hsh_P.update(clsag._HASH_KEY_CLSAG_AGG_0)
|
||||
hsh_C.update(clsag._HASH_KEY_CLSAG_AGG_1)
|
||||
|
||||
def hsh_PC(x):
|
||||
hsh_P.update(x)
|
||||
@ -43,11 +46,11 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
|
||||
hsh_PC(crypto.encodepoint_into(tmp_bf, sD))
|
||||
hsh_PC(C_offset_bf)
|
||||
mu_P = crypto.decodeint(hsh_P.digest())
|
||||
mu_C = crypto.decodeint(hsh_C.digest())
|
||||
mu_P = crypto_helpers.decodeint(hsh_P.digest())
|
||||
mu_C = crypto_helpers.decodeint(hsh_C.digest())
|
||||
|
||||
c_to_hash = crypto.get_keccak() # domain, P, C, C_offset, message, L, R
|
||||
c_to_hash.update(mlsag._HASH_KEY_CLSAG_ROUND)
|
||||
c_to_hash = crypto_helpers.get_keccak() # domain, P, C, C_offset, message, L, R
|
||||
c_to_hash.update(clsag._HASH_KEY_CLSAG_ROUND)
|
||||
for i in range(len(pubs)):
|
||||
c_to_hash.update(pubs[i].dest)
|
||||
for i in range(len(pubs)):
|
||||
@ -55,25 +58,25 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
c_to_hash.update(C_offset_bf)
|
||||
c_to_hash.update(msg)
|
||||
|
||||
c_p = crypto.new_scalar()
|
||||
c_c = crypto.new_scalar()
|
||||
L = crypto.new_point()
|
||||
R = crypto.new_point()
|
||||
tmp_pt = crypto.new_point()
|
||||
c_p = crypto.Scalar()
|
||||
c_c = crypto.Scalar()
|
||||
L = crypto.Point()
|
||||
R = crypto.Point()
|
||||
tmp_pt = crypto.Point()
|
||||
i = 0
|
||||
while i < n:
|
||||
crypto.sc_mul_into(c_p, mu_P, c)
|
||||
crypto.sc_mul_into(c_c, mu_C, c)
|
||||
|
||||
C_P = crypto.point_sub(
|
||||
crypto.decodepoint_into(tmp_pt, pubs[i].commitment), C_offset
|
||||
C_P = crypto.point_sub_into(
|
||||
None, crypto.decodepoint_into(tmp_pt, pubs[i].commitment), C_offset
|
||||
)
|
||||
crypto.add_keys2_into(
|
||||
L, ss[i], c_p, crypto.decodepoint_into(tmp_pt, pubs[i].dest)
|
||||
)
|
||||
crypto.point_add_into(L, L, crypto.scalarmult_into(tmp_pt, C_P, c_c))
|
||||
|
||||
HP = crypto.hash_to_point(pubs[i].dest)
|
||||
HP = crypto.hash_to_point_into(None, pubs[i].dest)
|
||||
crypto.add_keys3_into(R, ss[i], HP, c_p, sI)
|
||||
crypto.point_add_into(R, R, crypto.scalarmult_into(tmp_pt, D_8, c_c))
|
||||
|
||||
@ -82,8 +85,8 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
chasher.update(crypto.encodepoint_into(tmp_bf, R))
|
||||
crypto.decodeint_into(c, chasher.digest())
|
||||
i += 1
|
||||
res = crypto.sc_sub(c, sc1)
|
||||
if not crypto.sc_eq(res, crypto.sc_0()):
|
||||
res = crypto.sc_sub_into(None, c, sc1)
|
||||
if not crypto.sc_eq(res, crypto.Scalar(0)):
|
||||
raise ValueError("Signature error")
|
||||
|
||||
def gen_clsag_test(self, ring_size=11, index=None):
|
||||
@ -93,60 +96,67 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
|
||||
def gen_clsag_sig(self, ring_size=11, index=None):
|
||||
msg = random.bytes(32)
|
||||
amnt = crypto.sc_init(random.uniform(0xFFFFFF) + 12)
|
||||
amnt = crypto.Scalar(random.uniform(0xFFFFFF) + 12)
|
||||
priv = crypto.random_scalar()
|
||||
msk = crypto.random_scalar()
|
||||
alpha = crypto.random_scalar()
|
||||
P = crypto.scalarmult_base(priv)
|
||||
C = crypto.add_keys2(msk, amnt, crypto.xmr_H())
|
||||
Cp = crypto.add_keys2(alpha, amnt, crypto.xmr_H())
|
||||
P = crypto.scalarmult_base_into(None, priv)
|
||||
C = crypto.add_keys2_into(None, msk, amnt, crypto.xmr_H())
|
||||
Cp = crypto.add_keys2_into(None, alpha, amnt, crypto.xmr_H())
|
||||
|
||||
ring = []
|
||||
for i in range(ring_size - 1):
|
||||
tk = TmpKey(
|
||||
crypto.encodepoint(crypto.scalarmult_base(crypto.random_scalar())),
|
||||
crypto.encodepoint(crypto.scalarmult_base(crypto.random_scalar())),
|
||||
crypto_helpers.encodepoint(
|
||||
crypto.scalarmult_base_into(None, crypto.random_scalar())
|
||||
),
|
||||
crypto_helpers.encodepoint(
|
||||
crypto.scalarmult_base_into(None, crypto.random_scalar())
|
||||
),
|
||||
)
|
||||
ring.append(tk)
|
||||
|
||||
index = index if index is not None else random.uniform(len(ring))
|
||||
ring.insert(index, TmpKey(crypto.encodepoint(P), crypto.encodepoint(C)))
|
||||
ring.insert(index, TmpKey(crypto_helpers.encodepoint(P), crypto_helpers.encodepoint(C)))
|
||||
ring2 = list(ring)
|
||||
mg_buffer = []
|
||||
|
||||
self.assertTrue(
|
||||
crypto.point_eq(
|
||||
crypto.scalarmult_base(priv), crypto.decodepoint(ring[index].dest)
|
||||
crypto.scalarmult_base_into(None, priv),
|
||||
crypto_helpers.decodepoint(ring[index].dest),
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
crypto.point_eq(
|
||||
crypto.scalarmult_base(crypto.sc_sub(msk, alpha)),
|
||||
crypto.point_sub(crypto.decodepoint(ring[index].commitment), Cp),
|
||||
crypto.scalarmult_base_into(None, crypto.sc_sub_into(None, msk, alpha)),
|
||||
crypto.point_sub_into(
|
||||
None, crypto_helpers.decodepoint(ring[index].commitment), Cp
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
mlsag.generate_clsag_simple(
|
||||
clsag.generate_clsag_simple(
|
||||
msg, ring, CtKey(priv, msk), alpha, Cp, index, mg_buffer,
|
||||
)
|
||||
|
||||
sD = crypto.decodepoint(mg_buffer[-1])
|
||||
sc1 = crypto.decodeint(mg_buffer[-2])
|
||||
scalars = [crypto.decodeint(x) for x in mg_buffer[1:-2]]
|
||||
H = crypto.new_point()
|
||||
sI = crypto.new_point()
|
||||
sD = crypto_helpers.decodepoint(mg_buffer[-1])
|
||||
sc1 = crypto_helpers.decodeint(mg_buffer[-2])
|
||||
scalars = [crypto_helpers.decodeint(x) for x in mg_buffer[1:-2]]
|
||||
H = crypto.Point()
|
||||
sI = crypto.Point()
|
||||
|
||||
crypto.hash_to_point_into(H, crypto.encodepoint(P))
|
||||
crypto.hash_to_point_into(H, crypto_helpers.encodepoint(P))
|
||||
crypto.scalarmult_into(sI, H, priv) # I = p*H
|
||||
return msg, scalars, sc1, sI, sD, ring2, Cp
|
||||
|
||||
def verify_monero_generated(self, clsag):
|
||||
msg = ubinascii.unhexlify(clsag["msg"])
|
||||
sI = crypto.decodepoint(ubinascii.unhexlify(clsag["sI"]))
|
||||
sD = crypto.decodepoint(ubinascii.unhexlify(clsag["sD"]))
|
||||
sc1 = crypto.decodeint(ubinascii.unhexlify(clsag["sc1"]))
|
||||
Cout = crypto.decodepoint(ubinascii.unhexlify(clsag["cout"]))
|
||||
scalars = [crypto.decodeint(ubinascii.unhexlify(x)) for x in clsag["ss"]]
|
||||
sI = crypto_helpers.decodepoint(ubinascii.unhexlify(clsag["sI"]))
|
||||
sD = crypto_helpers.decodepoint(ubinascii.unhexlify(clsag["sD"]))
|
||||
sc1 = crypto_helpers.decodeint(ubinascii.unhexlify(clsag["sc1"]))
|
||||
Cout = crypto_helpers.decodepoint(ubinascii.unhexlify(clsag["cout"]))
|
||||
scalars = [crypto_helpers.decodeint(ubinascii.unhexlify(x)) for x in clsag["ss"]]
|
||||
ring = []
|
||||
for e in clsag["ring"]:
|
||||
ring.append(TmpKey(ubinascii.unhexlify(e[0]), ubinascii.unhexlify(e[1])))
|
||||
@ -160,43 +170,65 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
"sI": "a1c7f4a316ddd16374fe495d402be60566047ae5a1352554e98ebff118705303",
|
||||
"sD": "cd80b5c7f3f597de6e20bcef669a4ba9eb3eb89ead12ab1c24c92acd609afcb2",
|
||||
"sc1": "cf4f48ed60771d4e8d02e9e0af37281ceeb66573bd528ac256a7e17794a75602",
|
||||
"ss":
|
||||
["aaeffa564b5b0ff1e4ed72c9b595cd0241ac64eeb41b902a35688e369922d704"
|
||||
, "1defc134a853252d734d19b29d8f2fabc85a8ae24ebcf8f050d4daf8a335e901"
|
||||
, "cdf9ac576f0c7ceb7eb22c1a1254a801d0d2915e59870be8b1ab68cd1281120d"
|
||||
, "d1973493d8224aaa9732878b9a88d448ea16185f94e5bafd82816277682fa108"
|
||||
, "a130e076845e512687575942bf3694bcb44eb19eb1181af9a1fc2254949b7c0f"
|
||||
, "26f5b6ea154d6bd4a969c742563d75f1bfcd5ded3af78669e45ba95e76c48605"
|
||||
, "5b695d3be46b826fd11e043028dee2aa25cf36910e86537fcd1cd3f5cb49650e"
|
||||
, "37e811ebb4a2b9c35556b4af911a03a93468f599956c034092c3ece9e1169208"
|
||||
, "a361ceec9aacd65da6d3e686fbcd0c1aef26096321be7f01653157ee6096a201"
|
||||
, "f9b762ef1df69bb12ca76a97dce11f7840b8ec63c3dc2683f7ae71cb79c49103"
|
||||
, "ea010fa6a35f3bd3d7899a7a2a8df4d3ef9c9dfbbd56fe43ff5c7442821d3508"
|
||||
]
|
||||
, "ring": [
|
||||
["241c0295b4c3a149e5ac7997963e125d0fc6cc8adad9349df3b01ff611936c87",
|
||||
"3a24a4c418ccb2ceb83672d01534a73ff1e9f548937d5ddd7f1971c9b398868c"],
|
||||
["ec432ccfbf730077cb2d8c59968e2796148a590eec7928ecf268d883ced0de5b",
|
||||
"2973d6e9c27538fd0f7c003e014311e9403dcb6e7d86b66df65176a579943bda"],
|
||||
["0cfeafc313a6a2e60110778d53d61fa1705e9049b8afba0f51c1127f6855c07f",
|
||||
"ffa4d4c77202907832294243a96886920017b67fbe5b3800bcc1457c4a4a1ff0"],
|
||||
["bd4eca22dc010a214524901b88bdda27e427217ff784c47520ee76743caba036",
|
||||
"e07135f8398459133c2969184e70610b9b995f73e44acf54b6eaed6227e68bbc"],
|
||||
["73c8d57d0128c99fc2ab0be8cee5fe5c1288b98e51822a6681846035fcc53fea",
|
||||
"2987499fde3f4353013206d89fe2d7c6ad3cd9a66c9a36d17749e39112513572"],
|
||||
["385c538901b79c6bd2ddea5191e808b1414c9dfdcaf424841d843dd788cb89ad",
|
||||
"ec5f987fe138c6cb1d47ff75d77852b7c0a94ba1f0b93d22c0463f75986605bd"],
|
||||
["fed06cb761745a6f087d1af13f84670ecbf1523d72b46e8bd0698d1cdfb398bc",
|
||||
"5d81df981fb885f947b9404cb63cb06fe4e001be281f2bdfb3c638d54ec6e49e"],
|
||||
["667d1edfb83a17bd81fcf7831362b6c9038f26340ee1fe56d41f62cb0b32e989",
|
||||
"e9ceba97867b43cd5420c94fa61cc5f11e440e261df74dfc8b1c07ec4b13aa3c"],
|
||||
["e1e76da5bd52fc065f9af40efde5f733f9673974d14c6af8d200d8576ac3a90d",
|
||||
"97358d6ddad38b2707fb864bfcaaab935851af66d50bcbac569d159d740bdf71"],
|
||||
["4fd5d0db88283c63905d5095a76b11a75337e43f403f8469175ba9c49741552e",
|
||||
"af0ab85872a6355d5c82c1f9a2a41488146e19b272887a1f7385cc26bef3f1d8"],
|
||||
["37e1a4c49a22340fa5ac2c22c1b7a891e7191cdc53911700a317c0d8b92bbf4e",
|
||||
"5c89d29dad77de7d76ece8bb81c7c8cd15008f63c5a14ab1c984b3833e7bbce3"]
|
||||
]
|
||||
"ss": [
|
||||
"aaeffa564b5b0ff1e4ed72c9b595cd0241ac64eeb41b902a35688e369922d704",
|
||||
"1defc134a853252d734d19b29d8f2fabc85a8ae24ebcf8f050d4daf8a335e901",
|
||||
"cdf9ac576f0c7ceb7eb22c1a1254a801d0d2915e59870be8b1ab68cd1281120d",
|
||||
"d1973493d8224aaa9732878b9a88d448ea16185f94e5bafd82816277682fa108",
|
||||
"a130e076845e512687575942bf3694bcb44eb19eb1181af9a1fc2254949b7c0f",
|
||||
"26f5b6ea154d6bd4a969c742563d75f1bfcd5ded3af78669e45ba95e76c48605",
|
||||
"5b695d3be46b826fd11e043028dee2aa25cf36910e86537fcd1cd3f5cb49650e",
|
||||
"37e811ebb4a2b9c35556b4af911a03a93468f599956c034092c3ece9e1169208",
|
||||
"a361ceec9aacd65da6d3e686fbcd0c1aef26096321be7f01653157ee6096a201",
|
||||
"f9b762ef1df69bb12ca76a97dce11f7840b8ec63c3dc2683f7ae71cb79c49103",
|
||||
"ea010fa6a35f3bd3d7899a7a2a8df4d3ef9c9dfbbd56fe43ff5c7442821d3508",
|
||||
],
|
||||
"ring": [
|
||||
[
|
||||
"241c0295b4c3a149e5ac7997963e125d0fc6cc8adad9349df3b01ff611936c87",
|
||||
"3a24a4c418ccb2ceb83672d01534a73ff1e9f548937d5ddd7f1971c9b398868c",
|
||||
],
|
||||
[
|
||||
"ec432ccfbf730077cb2d8c59968e2796148a590eec7928ecf268d883ced0de5b",
|
||||
"2973d6e9c27538fd0f7c003e014311e9403dcb6e7d86b66df65176a579943bda",
|
||||
],
|
||||
[
|
||||
"0cfeafc313a6a2e60110778d53d61fa1705e9049b8afba0f51c1127f6855c07f",
|
||||
"ffa4d4c77202907832294243a96886920017b67fbe5b3800bcc1457c4a4a1ff0",
|
||||
],
|
||||
[
|
||||
"bd4eca22dc010a214524901b88bdda27e427217ff784c47520ee76743caba036",
|
||||
"e07135f8398459133c2969184e70610b9b995f73e44acf54b6eaed6227e68bbc",
|
||||
],
|
||||
[
|
||||
"73c8d57d0128c99fc2ab0be8cee5fe5c1288b98e51822a6681846035fcc53fea",
|
||||
"2987499fde3f4353013206d89fe2d7c6ad3cd9a66c9a36d17749e39112513572",
|
||||
],
|
||||
[
|
||||
"385c538901b79c6bd2ddea5191e808b1414c9dfdcaf424841d843dd788cb89ad",
|
||||
"ec5f987fe138c6cb1d47ff75d77852b7c0a94ba1f0b93d22c0463f75986605bd",
|
||||
],
|
||||
[
|
||||
"fed06cb761745a6f087d1af13f84670ecbf1523d72b46e8bd0698d1cdfb398bc",
|
||||
"5d81df981fb885f947b9404cb63cb06fe4e001be281f2bdfb3c638d54ec6e49e",
|
||||
],
|
||||
[
|
||||
"667d1edfb83a17bd81fcf7831362b6c9038f26340ee1fe56d41f62cb0b32e989",
|
||||
"e9ceba97867b43cd5420c94fa61cc5f11e440e261df74dfc8b1c07ec4b13aa3c",
|
||||
],
|
||||
[
|
||||
"e1e76da5bd52fc065f9af40efde5f733f9673974d14c6af8d200d8576ac3a90d",
|
||||
"97358d6ddad38b2707fb864bfcaaab935851af66d50bcbac569d159d740bdf71",
|
||||
],
|
||||
[
|
||||
"4fd5d0db88283c63905d5095a76b11a75337e43f403f8469175ba9c49741552e",
|
||||
"af0ab85872a6355d5c82c1f9a2a41488146e19b272887a1f7385cc26bef3f1d8",
|
||||
],
|
||||
[
|
||||
"37e1a4c49a22340fa5ac2c22c1b7a891e7191cdc53911700a317c0d8b92bbf4e",
|
||||
"5c89d29dad77de7d76ece8bb81c7c8cd15008f63c5a14ab1c984b3833e7bbce3",
|
||||
],
|
||||
],
|
||||
}
|
||||
self.verify_monero_generated(clsag)
|
||||
|
||||
@ -207,43 +239,65 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
"sI": "917fdd3086c056503ffdb1840f03c78d48bfe6d9d60b4efb194bd9798d03acaa",
|
||||
"sD": "769d0ca9b272ac02c5efad7df6b5c00f2995c99ca80f4597136decba9a0dd36f",
|
||||
"sc1": "fe5c7eb39a32d2aea12e6d127d847b72ea810bfbf3d5bbe23c40e7abdd12900e",
|
||||
"ss":
|
||||
["da2940c66cc2405032d959325c8804e216f76b36e71b2ae6b76417ed9c10a80a"
|
||||
, "ca763505c2e5ebacf72098f8cba89ea6826aa448501f03d439c7a838a88bba0e"
|
||||
, "b2eadee4c121e85b0c2a09d56c665ba19ee8ebc451f1e9e96cf72c874f945104"
|
||||
, "5a79523fdc0df9a54ab3937c878bd5a02e62bff77efc338728deb060ecda4509"
|
||||
, "dfadddc51866cde5206269270f44ca2f6350ca0b1328a968773fcacf57031502"
|
||||
, "a964f3549a10fc8bdb2f8217df0e9b08e90477be19a665b94b73ce417622450b"
|
||||
, "48e805427109268b04bf378c869501dbebb79c0cbe664bf7eb0ca222376d1c0f"
|
||||
, "33f36d9a699e92a66d4b9fdf6c1123ae99701b117fbe8f0af9faec51e45eb409"
|
||||
, "25ef746a03aaf59701d1d47ea3b9e9f092662cebc9d44902ce18e81cc5035f01"
|
||||
, "2ba3022d4f9b57da7429499715592073f1608cf270318840a5fd3890bbf5950a"
|
||||
, "8149ec0d965c9881d6a4adedca7d3c9090359dbfae56dbab526be102722aab09"
|
||||
]
|
||||
, "ring": [
|
||||
["081b048be784e1ff6f3b7ebe602690c27723b5d9952405bcdcbed31d16125067",
|
||||
"6090eccb73d2e1fc7bc7644a4fad04e5fe93d953a1258307c44d5b23cd636bf9"],
|
||||
["e2f0f100f1634d7c7dd5a09bc6dd7ee53506d73536aa743e8ea049528e4cb2aa",
|
||||
"632438f9aeda72eb9c6c434391cf9fa2f71788bea598a5d5729a5d502865932a"],
|
||||
["6744197cfde37ad1901d518f112c0f4d820c23122a016949e300eec2ab88916c",
|
||||
"1b251d5b32e22de29a4f99a0ed1de32754636175075e21b25d7283036eb85541"],
|
||||
["0e86bb7ee0b4728f2fedde7ac5019b54de7b2bb19b44d1864e6346dac6c171ab",
|
||||
"5a3c85e93890f802d4148140733dcdcd676353fce1bd774ce28034fc2ec00253"],
|
||||
["1847ce49d9552651395b2fa80637c131a31036f0bfc5abb63526701cd1a32320",
|
||||
"a9cb55bc24e6e1fb894c511f2edd4b7bda4c75a608657d952e85bab83ec98a52"],
|
||||
["5c5d0b678f5045b0304e3c48027bd7e9ccaee1dac4449ed1f34b204868ca5651",
|
||||
"badf83ccba38f2194f924a4f7fb7c2fd966b1e16c1fddeb3658033aa009febe0"],
|
||||
["81961aa4c241a91d498d8f3057b31373d9fc72b6e7d7f98bf497e3dfe705eeaa",
|
||||
"a0e632fbb801d6bce99ef97d7bb6acd945aff5cd7fab56c0e6fec6900a3babd7"],
|
||||
["cbd89f10ddf152bd9c756d145ef4cda1d56a31f1e1936759bee04b7a8a815c76",
|
||||
"8b835b8180f36e79ba79528e0d3401f439cc1c7f99e4bcfb3cb4aa2b60b1afc1"],
|
||||
["a7bc55e955a825730f5dcdc3f8126717d7647cbca8a6b90e08b77269aeed3533",
|
||||
"8da31e80698c9b5181b2e8d9773136083a34e3e72c92134d8201d9c368d89284"],
|
||||
["a7902cec90d3f2de25c8ddc87075159fd00f219a51a1e7dcac17c2b8a91887e9",
|
||||
"2b1e848b6649abefbd6b399504a169252358e7ff6bde8fa7a773b9cf0a167069"],
|
||||
["9fc3d5fb7de8cfc59982f7b20f3f5c145ad191088e7f59c10908dc5d55863bee",
|
||||
"b8de2bc9bb46d475007230a92af14afb6f9dd2804b5c31355a282b40ccdadc92"]
|
||||
]
|
||||
"ss": [
|
||||
"da2940c66cc2405032d959325c8804e216f76b36e71b2ae6b76417ed9c10a80a",
|
||||
"ca763505c2e5ebacf72098f8cba89ea6826aa448501f03d439c7a838a88bba0e",
|
||||
"b2eadee4c121e85b0c2a09d56c665ba19ee8ebc451f1e9e96cf72c874f945104",
|
||||
"5a79523fdc0df9a54ab3937c878bd5a02e62bff77efc338728deb060ecda4509",
|
||||
"dfadddc51866cde5206269270f44ca2f6350ca0b1328a968773fcacf57031502",
|
||||
"a964f3549a10fc8bdb2f8217df0e9b08e90477be19a665b94b73ce417622450b",
|
||||
"48e805427109268b04bf378c869501dbebb79c0cbe664bf7eb0ca222376d1c0f",
|
||||
"33f36d9a699e92a66d4b9fdf6c1123ae99701b117fbe8f0af9faec51e45eb409",
|
||||
"25ef746a03aaf59701d1d47ea3b9e9f092662cebc9d44902ce18e81cc5035f01",
|
||||
"2ba3022d4f9b57da7429499715592073f1608cf270318840a5fd3890bbf5950a",
|
||||
"8149ec0d965c9881d6a4adedca7d3c9090359dbfae56dbab526be102722aab09",
|
||||
],
|
||||
"ring": [
|
||||
[
|
||||
"081b048be784e1ff6f3b7ebe602690c27723b5d9952405bcdcbed31d16125067",
|
||||
"6090eccb73d2e1fc7bc7644a4fad04e5fe93d953a1258307c44d5b23cd636bf9",
|
||||
],
|
||||
[
|
||||
"e2f0f100f1634d7c7dd5a09bc6dd7ee53506d73536aa743e8ea049528e4cb2aa",
|
||||
"632438f9aeda72eb9c6c434391cf9fa2f71788bea598a5d5729a5d502865932a",
|
||||
],
|
||||
[
|
||||
"6744197cfde37ad1901d518f112c0f4d820c23122a016949e300eec2ab88916c",
|
||||
"1b251d5b32e22de29a4f99a0ed1de32754636175075e21b25d7283036eb85541",
|
||||
],
|
||||
[
|
||||
"0e86bb7ee0b4728f2fedde7ac5019b54de7b2bb19b44d1864e6346dac6c171ab",
|
||||
"5a3c85e93890f802d4148140733dcdcd676353fce1bd774ce28034fc2ec00253",
|
||||
],
|
||||
[
|
||||
"1847ce49d9552651395b2fa80637c131a31036f0bfc5abb63526701cd1a32320",
|
||||
"a9cb55bc24e6e1fb894c511f2edd4b7bda4c75a608657d952e85bab83ec98a52",
|
||||
],
|
||||
[
|
||||
"5c5d0b678f5045b0304e3c48027bd7e9ccaee1dac4449ed1f34b204868ca5651",
|
||||
"badf83ccba38f2194f924a4f7fb7c2fd966b1e16c1fddeb3658033aa009febe0",
|
||||
],
|
||||
[
|
||||
"81961aa4c241a91d498d8f3057b31373d9fc72b6e7d7f98bf497e3dfe705eeaa",
|
||||
"a0e632fbb801d6bce99ef97d7bb6acd945aff5cd7fab56c0e6fec6900a3babd7",
|
||||
],
|
||||
[
|
||||
"cbd89f10ddf152bd9c756d145ef4cda1d56a31f1e1936759bee04b7a8a815c76",
|
||||
"8b835b8180f36e79ba79528e0d3401f439cc1c7f99e4bcfb3cb4aa2b60b1afc1",
|
||||
],
|
||||
[
|
||||
"a7bc55e955a825730f5dcdc3f8126717d7647cbca8a6b90e08b77269aeed3533",
|
||||
"8da31e80698c9b5181b2e8d9773136083a34e3e72c92134d8201d9c368d89284",
|
||||
],
|
||||
[
|
||||
"a7902cec90d3f2de25c8ddc87075159fd00f219a51a1e7dcac17c2b8a91887e9",
|
||||
"2b1e848b6649abefbd6b399504a169252358e7ff6bde8fa7a773b9cf0a167069",
|
||||
],
|
||||
[
|
||||
"9fc3d5fb7de8cfc59982f7b20f3f5c145ad191088e7f59c10908dc5d55863bee",
|
||||
"b8de2bc9bb46d475007230a92af14afb6f9dd2804b5c31355a282b40ccdadc92",
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
self.verify_monero_generated(clsag)
|
||||
@ -261,22 +315,22 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
res = self.gen_clsag_sig(ring_size=11, index=5)
|
||||
msg, scalars, sc1, sI, sD, ring2, Cp = res
|
||||
with self.assertRaises(ValueError):
|
||||
sI = crypto.point_mul8(sI)
|
||||
sI = point_mul8_into(None, sI)
|
||||
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
|
||||
|
||||
def test_clsag_invalid_sD(self):
|
||||
res = self.gen_clsag_sig(ring_size=11, index=5)
|
||||
msg, scalars, sc1, sI, sD, ring2, Cp = res
|
||||
with self.assertRaises(ValueError):
|
||||
sD = crypto.scalarmult_base(crypto.random_scalar())
|
||||
sD = crypto.scalarmult_base_into(None, crypto.random_scalar())
|
||||
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
|
||||
|
||||
def test_clsag_invalid_P(self):
|
||||
res = self.gen_clsag_sig(ring_size=11, index=5)
|
||||
msg, scalars, sc1, sI, sD, ring2, Cp = res
|
||||
with self.assertRaises(ValueError):
|
||||
ring2[5].dest = crypto.encodepoint(
|
||||
crypto.point_mul8(crypto.decodepoint(ring2[5].dest))
|
||||
ring2[5].dest = crypto_helpers.encodepoint(
|
||||
point_mul8_into(None, crypto_helpers.decodepoint(ring2[5].dest))
|
||||
)
|
||||
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
|
||||
|
||||
@ -284,8 +338,8 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
res = self.gen_clsag_sig(ring_size=11, index=5)
|
||||
msg, scalars, sc1, sI, sD, ring2, Cp = res
|
||||
with self.assertRaises(ValueError):
|
||||
ring2[5].commitment = crypto.encodepoint(
|
||||
crypto.point_mul8(crypto.decodepoint(ring2[5].dest))
|
||||
ring2[5].commitment = crypto_helpers.encodepoint(
|
||||
point_mul8_into(None, crypto_helpers.decodepoint(ring2[5].dest))
|
||||
)
|
||||
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
|
||||
|
||||
@ -293,7 +347,9 @@ class TestMoneroClsag(unittest.TestCase):
|
||||
res = self.gen_clsag_sig(ring_size=11, index=5)
|
||||
msg, scalars, sc1, sI, sD, ring2, Cp = res
|
||||
with self.assertRaises(ValueError):
|
||||
Cp = crypto.point_add(Cp, crypto.scalarmult_base(crypto.sc_init(1)))
|
||||
Cp = crypto.point_add_into(
|
||||
None, Cp, crypto.scalarmult_base_into(None, crypto.Scalar(1))
|
||||
)
|
||||
self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
|
||||
|
||||
def test_clsag_invalid_index(self):
|
||||
|
@ -1,29 +1,29 @@
|
||||
from common import *
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
from apps.monero.xmr import crypto, monero
|
||||
from trezor.enums import MoneroNetworkType
|
||||
from apps.monero.xmr import crypto, crypto_helpers, monero
|
||||
from apps.monero.xmr.addresses import encode_addr
|
||||
from apps.monero.xmr.credentials import AccountCreds
|
||||
from apps.monero.xmr.networks import NetworkTypes, net_version
|
||||
from apps.monero.xmr.networks import net_version
|
||||
|
||||
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
class TestMoneroCrypto(unittest.TestCase):
|
||||
|
||||
def test_encoding(self):
|
||||
point = unhexlify(
|
||||
b"2486224797d05cae3cba4be043be2db0df381f3f19cfa113f86ab38e3d8d2bd0"
|
||||
)
|
||||
self.assertEqual(point, crypto.encodepoint(crypto.decodepoint(point)))
|
||||
self.assertEqual(point, crypto_helpers.encodepoint(crypto_helpers.decodepoint(point)))
|
||||
self.assertTrue(
|
||||
crypto.point_eq(
|
||||
crypto.decodepoint(point),
|
||||
crypto.decodepoint(crypto.encodepoint(crypto.decodepoint(point))),
|
||||
crypto_helpers.decodepoint(point),
|
||||
crypto_helpers.decodepoint(crypto_helpers.encodepoint(crypto_helpers.decodepoint(point))),
|
||||
)
|
||||
)
|
||||
|
||||
def test_scalarmult_base(self):
|
||||
scalar = crypto.decodeint(
|
||||
scalar = crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"a0eea49140a3b036da30eacf64bd9d56ce3ef68ba82ef13571ec511edbcf8303"
|
||||
)
|
||||
@ -32,11 +32,11 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
b"16bb4a3c44e2ced511fc0d4cd86b13b3af21efc99fb0356199fac489f2544c09"
|
||||
)
|
||||
|
||||
res = crypto.scalarmult_base(scalar)
|
||||
self.assertEqual(exp, crypto.encodepoint(res))
|
||||
self.assertTrue(crypto.point_eq(crypto.decodepoint(exp), res))
|
||||
res = crypto.scalarmult_base_into(None, scalar)
|
||||
self.assertEqual(exp, crypto_helpers.encodepoint(res))
|
||||
self.assertTrue(crypto.point_eq(crypto_helpers.decodepoint(exp), res))
|
||||
|
||||
scalar = crypto.decodeint(
|
||||
scalar = crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"fd290dce39f781aebbdbd24584ed6d48bd300de19d9c3decfda0a6e2c6751d0f"
|
||||
)
|
||||
@ -45,9 +45,9 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
b"123daf90fc26f13c6529e6b49bfed498995ac383ef19c0db6771143f24ba8dd5"
|
||||
)
|
||||
|
||||
res = crypto.scalarmult_base(scalar)
|
||||
self.assertEqual(exp, crypto.encodepoint(res))
|
||||
self.assertTrue(crypto.point_eq(crypto.decodepoint(exp), res))
|
||||
res = crypto.scalarmult_base_into(None, scalar)
|
||||
self.assertEqual(exp, crypto_helpers.encodepoint(res))
|
||||
self.assertTrue(crypto.point_eq(crypto_helpers.decodepoint(exp), res))
|
||||
|
||||
def test_scalarmult(self):
|
||||
priv = unhexlify(
|
||||
@ -60,15 +60,15 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
b"adcd1f5881f46f254900a03c654e71950a88a0236fa0a3a946c9b8daed6ef43d"
|
||||
)
|
||||
|
||||
res = crypto.scalarmult(crypto.decodepoint(pub), crypto.decodeint(priv))
|
||||
self.assertEqual(exp, crypto.encodepoint(res))
|
||||
self.assertTrue(crypto.point_eq(crypto.decodepoint(exp), res))
|
||||
res = crypto.scalarmult_into(None, crypto_helpers.decodepoint(pub), crypto_helpers.decodeint(priv))
|
||||
self.assertEqual(exp, crypto_helpers.encodepoint(res))
|
||||
self.assertTrue(crypto.point_eq(crypto_helpers.decodepoint(exp), res))
|
||||
|
||||
def test_cn_fast_hash(self):
|
||||
inp = unhexlify(
|
||||
b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff6405"
|
||||
)
|
||||
res = crypto.cn_fast_hash(inp)
|
||||
res = crypto.fast_hash_into(None, inp)
|
||||
self.assertEqual(
|
||||
res,
|
||||
unhexlify(
|
||||
@ -81,8 +81,8 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff6405"
|
||||
)
|
||||
|
||||
res = crypto.hash_to_scalar(inp)
|
||||
exp = crypto.decodeint(
|
||||
res = crypto.hash_to_scalar_into(None, inp)
|
||||
exp = crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"9907925b254e12162609fc0dfd0fef2aa4d605b0d10e6507cac253dd31a3ec06"
|
||||
)
|
||||
@ -93,8 +93,8 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
data = unhexlify(
|
||||
b"42f6835bf83114a1f5f6076fe79bdfa0bd67c74b88f127d54572d3910dd09201"
|
||||
)
|
||||
res = crypto.hash_to_point(data)
|
||||
res_p = crypto.encodepoint(res)
|
||||
res = crypto.hash_to_point_into(None, data)
|
||||
res_p = crypto_helpers.encodepoint(res)
|
||||
self.assertEqual(
|
||||
res_p,
|
||||
unhexlify(
|
||||
@ -110,16 +110,16 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
b"25d08763414c379aa9cf989cdcb3cadd36bd5193b500107d6bf5f921f18e470e"
|
||||
)
|
||||
|
||||
sc_int = crypto.derivation_to_scalar(crypto.decodepoint(derivation), 0)
|
||||
self.assertEqual(scalar, crypto.encodeint(sc_int))
|
||||
sc_int = crypto_helpers.derivation_to_scalar(crypto_helpers.decodepoint(derivation), 0)
|
||||
self.assertEqual(scalar, crypto_helpers.encodeint(sc_int))
|
||||
|
||||
def test_generate_key_derivation(self):
|
||||
key_pub = crypto.decodepoint(
|
||||
key_pub = crypto_helpers.decodepoint(
|
||||
unhexlify(
|
||||
b"7739c95d3298e2f87362dba9e0e0b3980a692ae8e2f16796b0e382098cd6bd83"
|
||||
)
|
||||
)
|
||||
key_priv = crypto.decodeint(
|
||||
key_priv = crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a"
|
||||
)
|
||||
@ -130,18 +130,18 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
deriv_exp,
|
||||
crypto.encodepoint(crypto.generate_key_derivation(key_pub, key_priv)),
|
||||
crypto_helpers.encodepoint(crypto_helpers.generate_key_derivation(key_pub, key_priv)),
|
||||
)
|
||||
|
||||
def test_h(self):
|
||||
H = unhexlify(
|
||||
b"8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"
|
||||
)
|
||||
self.assertEqual(crypto.encodepoint(crypto.xmr_H()), H)
|
||||
self.assertEqual(crypto_helpers.encodepoint(crypto.xmr_H()), H)
|
||||
|
||||
def test_sc_inversion(self):
|
||||
res = crypto.new_scalar()
|
||||
inp = crypto.decodeint(
|
||||
res = crypto.Scalar()
|
||||
inp = crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a"
|
||||
)
|
||||
@ -149,7 +149,7 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
|
||||
crypto.sc_inv_into(res, inp)
|
||||
self.assertEqual(
|
||||
hexlify(crypto.encodeint(res)),
|
||||
hexlify(crypto_helpers.encodeint(res)),
|
||||
b"bcf365a551e6358f3f281a6241d4a25eded60230b60a1d48c67b51a85e33d70e",
|
||||
)
|
||||
|
||||
@ -166,39 +166,39 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
addr,
|
||||
b"43tpGG9PKbwCpjRvNLn1jwXPpnacw2uVUcszAtgmDiVcZK4VgHwjJT9BJz1WGF9eMxSYASp8yNMkuLjeQfWqJn3CNWdWfzV",
|
||||
"43tpGG9PKbwCpjRvNLn1jwXPpnacw2uVUcszAtgmDiVcZK4VgHwjJT9BJz1WGF9eMxSYASp8yNMkuLjeQfWqJn3CNWdWfzV",
|
||||
)
|
||||
|
||||
w = AccountCreds.new_wallet(
|
||||
crypto.decodeint(
|
||||
crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"4ce88c168e0f5f8d6524f712d5f8d7d83233b1e7a2a60b5aba5206cc0ea2bc08"
|
||||
)
|
||||
),
|
||||
crypto.decodeint(
|
||||
crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"f2644a3dd97d43e87887e74d1691d52baa0614206ad1b0c239ff4aa3b501750a"
|
||||
)
|
||||
),
|
||||
network_type=NetworkTypes.TESTNET,
|
||||
network_type=MoneroNetworkType.TESTNET,
|
||||
)
|
||||
self.assertEqual(
|
||||
w.address,
|
||||
b"9vacMKaj8JJV6MnwDzh2oNVdwTLJfTDyNRiB6NzV9TT7fqvzLivH2dB8Tv7VYR3ncn8vCb3KdNMJzQWrPAF1otYJ9cPKpkr",
|
||||
"9vacMKaj8JJV6MnwDzh2oNVdwTLJfTDyNRiB6NzV9TT7fqvzLivH2dB8Tv7VYR3ncn8vCb3KdNMJzQWrPAF1otYJ9cPKpkr",
|
||||
)
|
||||
|
||||
def test_derive_subaddress_public_key(self):
|
||||
out_key = crypto.decodepoint(
|
||||
out_key = crypto_helpers.decodepoint(
|
||||
unhexlify(
|
||||
b"f4efc29da4ccd6bc6e81f52a6f47b2952966442a7efb49901cce06a7a3bef3e5"
|
||||
)
|
||||
)
|
||||
deriv = crypto.decodepoint(
|
||||
deriv = crypto_helpers.decodepoint(
|
||||
unhexlify(
|
||||
b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff64"
|
||||
)
|
||||
)
|
||||
res = crypto.encodepoint(monero.derive_subaddress_public_key(out_key, deriv, 5))
|
||||
res = crypto_helpers.encodepoint(monero.derive_subaddress_public_key(out_key, deriv, 5))
|
||||
self.assertEqual(
|
||||
res,
|
||||
unhexlify(
|
||||
@ -207,14 +207,14 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_get_subaddress_secret_key(self):
|
||||
a = crypto.decodeint(
|
||||
a = crypto_helpers.decodeint(
|
||||
unhexlify(
|
||||
b"4ce88c168e0f5f8d6524f712d5f8d7d83233b1e7a2a60b5aba5206cc0ea2bc08"
|
||||
)
|
||||
)
|
||||
m = monero.get_subaddress_secret_key(secret_key=a, major=0, minor=1)
|
||||
self.assertEqual(
|
||||
crypto.encodeint(m),
|
||||
crypto_helpers.encodeint(m),
|
||||
unhexlify(
|
||||
b"b6ff4d689b95e3310efbf683850c075bcde46361923054e42ef30016b287ff0c"
|
||||
),
|
||||
@ -231,10 +231,10 @@ class TestMoneroCrypto(unittest.TestCase):
|
||||
b"0846cae7405077b6b7800f0b932c10a186448370b6db318f8c9e13f781dab546"
|
||||
)
|
||||
|
||||
pkey_comp = crypto.derive_public_key(
|
||||
crypto.decodepoint(derivation), 0, crypto.decodepoint(base)
|
||||
pkey_comp = crypto_helpers.derive_public_key(
|
||||
crypto_helpers.decodepoint(derivation), 0, crypto_helpers.decodepoint(base)
|
||||
)
|
||||
self.assertEqual(pkey_ex, crypto.encodepoint(pkey_comp))
|
||||
self.assertEqual(pkey_ex, crypto_helpers.encodepoint(pkey_comp))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -11,33 +11,53 @@ if not utils.BITCOIN_ONLY:
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
class TestMoneroProto(unittest.TestCase):
|
||||
def test_sign_keys(self):
|
||||
mst = ubinascii.unhexlify(b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c")
|
||||
self.assertEqual(offloading_keys.key_signature(mst, 0, True)[:12], ubinascii.unhexlify(b'bb665d97ac7c77995578e352'))
|
||||
self.assertEqual(offloading_keys.key_signature(mst, 0, False), ubinascii.unhexlify(b'87bb70af81bb7325f73e8b962167579454d126ff8ee51472922d7c103fc60f5f'))
|
||||
self.assertEqual(offloading_keys.key_signature(mst, 3, True)[:12], ubinascii.unhexlify(b'b2ef8e4e4eec72ce3096622a'))
|
||||
self.assertEqual(offloading_keys.key_signature(mst, 3, False), ubinascii.unhexlify(b'e4331602a83a68c892a83693a1b961564048d9349111b85b8b4b52a1adcf36da'))
|
||||
mst = ubinascii.unhexlify(
|
||||
b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c"
|
||||
)
|
||||
self.assertEqual(
|
||||
offloading_keys.key_signature(mst, 0, True)[:12],
|
||||
ubinascii.unhexlify(b"bb665d97ac7c77995578e352"),
|
||||
)
|
||||
self.assertEqual(
|
||||
offloading_keys.key_signature(mst, 0, False),
|
||||
ubinascii.unhexlify(
|
||||
b"87bb70af81bb7325f73e8b962167579454d126ff8ee51472922d7c103fc60f5f"
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
offloading_keys.key_signature(mst, 3, True)[:12],
|
||||
ubinascii.unhexlify(b"b2ef8e4e4eec72ce3096622a"),
|
||||
)
|
||||
self.assertEqual(
|
||||
offloading_keys.key_signature(mst, 3, False),
|
||||
ubinascii.unhexlify(
|
||||
b"e4331602a83a68c892a83693a1b961564048d9349111b85b8b4b52a1adcf36da"
|
||||
),
|
||||
)
|
||||
|
||||
def test_sig_seal(self):
|
||||
mst = ubinascii.unhexlify(b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c")
|
||||
mst = ubinascii.unhexlify(
|
||||
b"ca3bbe08a178a4508c3992a47ba775799e7626a365ed136e803fe5f2df2ce01c"
|
||||
)
|
||||
st = State(None)
|
||||
st.last_step = st.STEP_SIGN
|
||||
st.opening_key = mst
|
||||
st.current_input_index = 3
|
||||
|
||||
mg_buff = [
|
||||
'0b',
|
||||
'02fe9ee789007254215b41351109f186620624a3c1ad2ba89628194528672adf04f900ebf9ad3b0cc1ac9ae1f03167f74d6e04175df5001c91d09d29dbefd6bc0b',
|
||||
'021d46f6db8a349caca48a4dfee155b9dee927d0f25cdf5bcd724358c611b47906de6cedad47fd26070927f3954bcaf7a0e126699bf961ca4e8124abefe8aaeb05',
|
||||
'02ae933994effe2b348b09bfab783bf9adb58b09659d8f5bd058cca252d763b600541807dcb0ea9fe253e59f23ce36cc811d627acae5e2abdc00b7ed155f3e6b0f',
|
||||
'0203dd7138c7378444fe3c1b1572a351f88505aeab2d9f8ed4a8f67d66e76983072d8ae6e496b3953a8603543c2dc64749ee15fe3575e4505b502bfe696f06690e',
|
||||
'0287b572b6c096bc11a8c10fe1fc4ba2085633f8e1bdd2e39df8f46c9bf733ca068261d8006f22ee2bfaf4366e26d42b00befdddd9058a5c87a0f39c757f121909',
|
||||
'021e2ea38aa07601e07a3d7623a97e68d3251525304d2a748548c7b46d07c20b0c78506b19cae49d569d0a8c4979c74f7d8d19f7e595d307ddf00faf3d8f621c0d',
|
||||
'0214f758c8fb4a521a1e3d25b9fb535974f6aab1c1dda5988e986dda7e17140909a7b7bdb3d5e17a2ebd5deb3530d10c6f5d6966f525c1cbca408059949ff65304',
|
||||
'02f707c4a37066a692986ddfdd2ca71f68c6f45a956d45eaf6e8e7a2e5272ac3033eb26ca2b55bf86e90ab8ddcdbad88a82ded88deb552614190440169afcee004',
|
||||
'02edb8a5b8cc02a2e03b95ea068084ae2496f21d4dfd0842c63836137e37047b06d5a0160994396c98630d8b47878e9c18fea4fb824588c143e05c4b18bfea2301',
|
||||
'02aa59c2ef76ac97c261279a1c6ed3724d66a437fe8df0b85e8858703947a2b10f04e49912a0626c09849c3b4a3ea46166cd909b9fd561257730c91cbccf4abe07',
|
||||
'02c64a98c59c4a3d7c583de65404c5a54b350a25011dfca70cd84e3f6e570428026236028fce31bfd8d9fc5401867ab5349eb0859c65df05b380899a7bdfee9003',
|
||||
'03da465e27f7feec31353cb668f0e8965391f983b06c0684b35b00af38533603',
|
||||
"0b",
|
||||
"02fe9ee789007254215b41351109f186620624a3c1ad2ba89628194528672adf04f900ebf9ad3b0cc1ac9ae1f03167f74d6e04175df5001c91d09d29dbefd6bc0b",
|
||||
"021d46f6db8a349caca48a4dfee155b9dee927d0f25cdf5bcd724358c611b47906de6cedad47fd26070927f3954bcaf7a0e126699bf961ca4e8124abefe8aaeb05",
|
||||
"02ae933994effe2b348b09bfab783bf9adb58b09659d8f5bd058cca252d763b600541807dcb0ea9fe253e59f23ce36cc811d627acae5e2abdc00b7ed155f3e6b0f",
|
||||
"0203dd7138c7378444fe3c1b1572a351f88505aeab2d9f8ed4a8f67d66e76983072d8ae6e496b3953a8603543c2dc64749ee15fe3575e4505b502bfe696f06690e",
|
||||
"0287b572b6c096bc11a8c10fe1fc4ba2085633f8e1bdd2e39df8f46c9bf733ca068261d8006f22ee2bfaf4366e26d42b00befdddd9058a5c87a0f39c757f121909",
|
||||
"021e2ea38aa07601e07a3d7623a97e68d3251525304d2a748548c7b46d07c20b0c78506b19cae49d569d0a8c4979c74f7d8d19f7e595d307ddf00faf3d8f621c0d",
|
||||
"0214f758c8fb4a521a1e3d25b9fb535974f6aab1c1dda5988e986dda7e17140909a7b7bdb3d5e17a2ebd5deb3530d10c6f5d6966f525c1cbca408059949ff65304",
|
||||
"02f707c4a37066a692986ddfdd2ca71f68c6f45a956d45eaf6e8e7a2e5272ac3033eb26ca2b55bf86e90ab8ddcdbad88a82ded88deb552614190440169afcee004",
|
||||
"02edb8a5b8cc02a2e03b95ea068084ae2496f21d4dfd0842c63836137e37047b06d5a0160994396c98630d8b47878e9c18fea4fb824588c143e05c4b18bfea2301",
|
||||
"02aa59c2ef76ac97c261279a1c6ed3724d66a437fe8df0b85e8858703947a2b10f04e49912a0626c09849c3b4a3ea46166cd909b9fd561257730c91cbccf4abe07",
|
||||
"02c64a98c59c4a3d7c583de65404c5a54b350a25011dfca70cd84e3f6e570428026236028fce31bfd8d9fc5401867ab5349eb0859c65df05b380899a7bdfee9003",
|
||||
"03da465e27f7feec31353cb668f0e8965391f983b06c0684b35b00af38533603",
|
||||
]
|
||||
|
||||
mg_buff = [ubinascii.unhexlify(x) for x in mg_buff]
|
||||
|
@ -10,9 +10,7 @@ if not utils.BITCOIN_ONLY:
|
||||
)
|
||||
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter
|
||||
from apps.monero.xmr.serialize_messages.base import ECPoint
|
||||
from apps.monero.xmr.serialize_messages.tx_prefix import (
|
||||
TxinToKey,
|
||||
)
|
||||
from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey
|
||||
|
||||
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
@ -66,7 +64,8 @@ class TestMoneroSerializer(unittest.TestCase):
|
||||
test_deser = TxinToKey.load(MemoryReaderWriter(writer.get_buffer()))
|
||||
|
||||
self.assertEqual(msg.amount, test_deser.amount)
|
||||
self.assertEqual(msg, test_deser)
|
||||
self.assertEqual(msg.k_image, test_deser.k_image)
|
||||
self.assertEqual(msg.key_offsets, test_deser.key_offsets)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -491,6 +491,13 @@ class EthereumDataType(IntEnum):
|
||||
STRUCT = 8
|
||||
|
||||
|
||||
class MoneroNetworkType(IntEnum):
|
||||
MAINNET = 0
|
||||
TESTNET = 1
|
||||
STAGENET = 2
|
||||
FAKECHAIN = 3
|
||||
|
||||
|
||||
class NEMMosaicLevy(IntEnum):
|
||||
MosaicLevy_Absolute = 1
|
||||
MosaicLevy_Percentile = 2
|
||||
@ -5148,7 +5155,7 @@ class MoneroGetAddress(protobuf.MessageType):
|
||||
FIELDS = {
|
||||
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
|
||||
2: protobuf.Field("show_display", "bool", repeated=False, required=False),
|
||||
3: protobuf.Field("network_type", "uint32", repeated=False, required=False),
|
||||
3: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
|
||||
4: protobuf.Field("account", "uint32", repeated=False, required=False),
|
||||
5: protobuf.Field("minor", "uint32", repeated=False, required=False),
|
||||
6: protobuf.Field("payment_id", "bytes", repeated=False, required=False),
|
||||
@ -5159,7 +5166,7 @@ class MoneroGetAddress(protobuf.MessageType):
|
||||
*,
|
||||
address_n: Optional[Sequence["int"]] = None,
|
||||
show_display: Optional["bool"] = None,
|
||||
network_type: Optional["int"] = None,
|
||||
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
|
||||
account: Optional["int"] = None,
|
||||
minor: Optional["int"] = None,
|
||||
payment_id: Optional["bytes"] = None,
|
||||
@ -5190,14 +5197,14 @@ class MoneroGetWatchKey(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 542
|
||||
FIELDS = {
|
||||
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
|
||||
2: protobuf.Field("network_type", "uint32", repeated=False, required=False),
|
||||
2: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
address_n: Optional[Sequence["int"]] = None,
|
||||
network_type: Optional["int"] = None,
|
||||
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
|
||||
) -> None:
|
||||
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||
self.network_type = network_type
|
||||
@ -5225,7 +5232,7 @@ class MoneroTransactionInitRequest(protobuf.MessageType):
|
||||
FIELDS = {
|
||||
1: protobuf.Field("version", "uint32", repeated=False, required=False),
|
||||
2: protobuf.Field("address_n", "uint32", repeated=True, required=False),
|
||||
3: protobuf.Field("network_type", "uint32", repeated=False, required=False),
|
||||
3: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
|
||||
4: protobuf.Field("tsx_data", "MoneroTransactionData", repeated=False, required=False),
|
||||
}
|
||||
|
||||
@ -5234,7 +5241,7 @@ class MoneroTransactionInitRequest(protobuf.MessageType):
|
||||
*,
|
||||
address_n: Optional[Sequence["int"]] = None,
|
||||
version: Optional["int"] = None,
|
||||
network_type: Optional["int"] = None,
|
||||
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
|
||||
tsx_data: Optional["MoneroTransactionData"] = None,
|
||||
) -> None:
|
||||
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||
@ -5543,21 +5550,21 @@ class MoneroTransactionFinalAck(protobuf.MessageType):
|
||||
class MoneroKeyImageExportInitRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 530
|
||||
FIELDS = {
|
||||
1: protobuf.Field("num", "uint64", repeated=False, required=False),
|
||||
2: protobuf.Field("hash", "bytes", repeated=False, required=False),
|
||||
1: protobuf.Field("num", "uint64", repeated=False, required=True),
|
||||
2: protobuf.Field("hash", "bytes", repeated=False, required=True),
|
||||
3: protobuf.Field("address_n", "uint32", repeated=True, required=False),
|
||||
4: protobuf.Field("network_type", "uint32", repeated=False, required=False),
|
||||
4: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
|
||||
5: protobuf.Field("subs", "MoneroSubAddressIndicesList", repeated=True, required=False),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
num: "int",
|
||||
hash: "bytes",
|
||||
address_n: Optional[Sequence["int"]] = None,
|
||||
subs: Optional[Sequence["MoneroSubAddressIndicesList"]] = None,
|
||||
num: Optional["int"] = None,
|
||||
hash: Optional["bytes"] = None,
|
||||
network_type: Optional["int"] = None,
|
||||
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
|
||||
) -> None:
|
||||
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||
self.subs: Sequence["MoneroSubAddressIndicesList"] = subs if subs is not None else []
|
||||
@ -5620,11 +5627,11 @@ class MoneroGetTxKeyRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 550
|
||||
FIELDS = {
|
||||
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
|
||||
2: protobuf.Field("network_type", "uint32", repeated=False, required=False),
|
||||
3: protobuf.Field("salt1", "bytes", repeated=False, required=False),
|
||||
4: protobuf.Field("salt2", "bytes", repeated=False, required=False),
|
||||
5: protobuf.Field("tx_enc_keys", "bytes", repeated=False, required=False),
|
||||
6: protobuf.Field("tx_prefix_hash", "bytes", repeated=False, required=False),
|
||||
2: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
|
||||
3: protobuf.Field("salt1", "bytes", repeated=False, required=True),
|
||||
4: protobuf.Field("salt2", "bytes", repeated=False, required=True),
|
||||
5: protobuf.Field("tx_enc_keys", "bytes", repeated=False, required=True),
|
||||
6: protobuf.Field("tx_prefix_hash", "bytes", repeated=False, required=True),
|
||||
7: protobuf.Field("reason", "uint32", repeated=False, required=False),
|
||||
8: protobuf.Field("view_public_key", "bytes", repeated=False, required=False),
|
||||
}
|
||||
@ -5632,21 +5639,21 @@ class MoneroGetTxKeyRequest(protobuf.MessageType):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
salt1: "bytes",
|
||||
salt2: "bytes",
|
||||
tx_enc_keys: "bytes",
|
||||
tx_prefix_hash: "bytes",
|
||||
address_n: Optional[Sequence["int"]] = None,
|
||||
network_type: Optional["int"] = None,
|
||||
salt1: Optional["bytes"] = None,
|
||||
salt2: Optional["bytes"] = None,
|
||||
tx_enc_keys: Optional["bytes"] = None,
|
||||
tx_prefix_hash: Optional["bytes"] = None,
|
||||
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
|
||||
reason: Optional["int"] = None,
|
||||
view_public_key: Optional["bytes"] = None,
|
||||
) -> None:
|
||||
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||
self.network_type = network_type
|
||||
self.salt1 = salt1
|
||||
self.salt2 = salt2
|
||||
self.tx_enc_keys = tx_enc_keys
|
||||
self.tx_prefix_hash = tx_prefix_hash
|
||||
self.network_type = network_type
|
||||
self.reason = reason
|
||||
self.view_public_key = view_public_key
|
||||
|
||||
@ -5675,14 +5682,14 @@ class MoneroLiveRefreshStartRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 552
|
||||
FIELDS = {
|
||||
1: protobuf.Field("address_n", "uint32", repeated=True, required=False),
|
||||
2: protobuf.Field("network_type", "uint32", repeated=False, required=False),
|
||||
2: protobuf.Field("network_type", "MoneroNetworkType", repeated=False, required=False),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
address_n: Optional[Sequence["int"]] = None,
|
||||
network_type: Optional["int"] = None,
|
||||
network_type: Optional["MoneroNetworkType"] = MoneroNetworkType.MAINNET,
|
||||
) -> None:
|
||||
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||
self.network_type = network_type
|
||||
@ -5695,21 +5702,21 @@ class MoneroLiveRefreshStartAck(protobuf.MessageType):
|
||||
class MoneroLiveRefreshStepRequest(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = 554
|
||||
FIELDS = {
|
||||
1: protobuf.Field("out_key", "bytes", repeated=False, required=False),
|
||||
2: protobuf.Field("recv_deriv", "bytes", repeated=False, required=False),
|
||||
3: protobuf.Field("real_out_idx", "uint64", repeated=False, required=False),
|
||||
4: protobuf.Field("sub_addr_major", "uint32", repeated=False, required=False),
|
||||
5: protobuf.Field("sub_addr_minor", "uint32", repeated=False, required=False),
|
||||
1: protobuf.Field("out_key", "bytes", repeated=False, required=True),
|
||||
2: protobuf.Field("recv_deriv", "bytes", repeated=False, required=True),
|
||||
3: protobuf.Field("real_out_idx", "uint64", repeated=False, required=True),
|
||||
4: protobuf.Field("sub_addr_major", "uint32", repeated=False, required=True),
|
||||
5: protobuf.Field("sub_addr_minor", "uint32", repeated=False, required=True),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
out_key: Optional["bytes"] = None,
|
||||
recv_deriv: Optional["bytes"] = None,
|
||||
real_out_idx: Optional["int"] = None,
|
||||
sub_addr_major: Optional["int"] = None,
|
||||
sub_addr_minor: Optional["int"] = None,
|
||||
out_key: "bytes",
|
||||
recv_deriv: "bytes",
|
||||
real_out_idx: "int",
|
||||
sub_addr_major: "int",
|
||||
sub_addr_minor: "int",
|
||||
) -> None:
|
||||
self.out_key = out_key
|
||||
self.recv_deriv = recv_deriv
|
||||
@ -5844,15 +5851,15 @@ class MoneroMultisigKLRki(protobuf.MessageType):
|
||||
class MoneroRctKeyPublic(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = None
|
||||
FIELDS = {
|
||||
1: protobuf.Field("dest", "bytes", repeated=False, required=False),
|
||||
2: protobuf.Field("commitment", "bytes", repeated=False, required=False),
|
||||
1: protobuf.Field("dest", "bytes", repeated=False, required=True),
|
||||
2: protobuf.Field("commitment", "bytes", repeated=False, required=True),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
dest: Optional["bytes"] = None,
|
||||
commitment: Optional["bytes"] = None,
|
||||
dest: "bytes",
|
||||
commitment: "bytes",
|
||||
) -> None:
|
||||
self.dest = dest
|
||||
self.commitment = commitment
|
||||
@ -5954,15 +5961,15 @@ class MoneroRingCtSig(protobuf.MessageType):
|
||||
class MoneroSubAddressIndicesList(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = None
|
||||
FIELDS = {
|
||||
1: protobuf.Field("account", "uint32", repeated=False, required=False),
|
||||
1: protobuf.Field("account", "uint32", repeated=False, required=True),
|
||||
2: protobuf.Field("minor_indices", "uint32", repeated=True, required=False),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
account: "int",
|
||||
minor_indices: Optional[Sequence["int"]] = None,
|
||||
account: Optional["int"] = None,
|
||||
) -> None:
|
||||
self.minor_indices: Sequence["int"] = minor_indices if minor_indices is not None else []
|
||||
self.account = account
|
||||
@ -5971,10 +5978,10 @@ class MoneroSubAddressIndicesList(protobuf.MessageType):
|
||||
class MoneroTransferDetails(protobuf.MessageType):
|
||||
MESSAGE_WIRE_TYPE = None
|
||||
FIELDS = {
|
||||
1: protobuf.Field("out_key", "bytes", repeated=False, required=False),
|
||||
2: protobuf.Field("tx_pub_key", "bytes", repeated=False, required=False),
|
||||
1: protobuf.Field("out_key", "bytes", repeated=False, required=True),
|
||||
2: protobuf.Field("tx_pub_key", "bytes", repeated=False, required=True),
|
||||
3: protobuf.Field("additional_tx_pub_keys", "bytes", repeated=True, required=False),
|
||||
4: protobuf.Field("internal_output_index", "uint64", repeated=False, required=False),
|
||||
4: protobuf.Field("internal_output_index", "uint64", repeated=False, required=True),
|
||||
5: protobuf.Field("sub_addr_major", "uint32", repeated=False, required=False),
|
||||
6: protobuf.Field("sub_addr_minor", "uint32", repeated=False, required=False),
|
||||
}
|
||||
@ -5982,10 +5989,10 @@ class MoneroTransferDetails(protobuf.MessageType):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
out_key: "bytes",
|
||||
tx_pub_key: "bytes",
|
||||
internal_output_index: "int",
|
||||
additional_tx_pub_keys: Optional[Sequence["bytes"]] = None,
|
||||
out_key: Optional["bytes"] = None,
|
||||
tx_pub_key: Optional["bytes"] = None,
|
||||
internal_output_index: Optional["int"] = None,
|
||||
sub_addr_major: Optional["int"] = None,
|
||||
sub_addr_minor: Optional["int"] = None,
|
||||
) -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user