From c1a6eb8bbbc1cc7974ce0938e9d8f920d0ad3ae9 Mon Sep 17 00:00:00 2001 From: j-berman Date: Tue, 21 Jun 2022 17:48:30 +0100 Subject: [PATCH 01/11] support for hf 15 (view tags) --- doc/developer/blue-app-commands.rst | 51 +++++++++++++++++++++++++++++ src/monero_api.h | 3 ++ src/monero_crypto.c | 16 +++++++++ src/monero_dispatch.c | 4 +++ src/monero_key.c | 34 ++++++++++++++++++- src/monero_types.h | 1 + tests/monero_client/monero_types.py | 1 + tests/test_crypto.py | 12 +++++++ 8 files changed, 121 insertions(+), 1 deletion(-) diff --git a/doc/developer/blue-app-commands.rst b/doc/developer/blue-app-commands.rst index fde67f2..ee6fd81 100644 --- a/doc/developer/blue-app-commands.rst +++ b/doc/developer/blue-app-commands.rst @@ -104,6 +104,8 @@ .. |eDRVout| replace:: :math:`\widetilde{\mathfrak{D}_\mathrm{out}}` .. |AKout| replace:: :math:`\mathcal{AK}_\mathrm{amount}` .. |eAKout| replace:: :math:`\widetilde{\mathcal{AK}_\mathrm{amount}}` +.. |vtf| replace:: :math:`\mathit{view_tag_full}` +.. |vt| replace:: :math:`\mathit{view_tag}` .. |ctH| replace:: :math:`\mathcal{H}_\mathrm{commitment}` @@ -198,6 +200,7 @@ To summarize, the signature process is: - compute the range proof - blind the amount + - compute the view tag . Compute the final confidential ring signature @@ -1142,6 +1145,54 @@ return |Img|. +--------+-----------------------------------------------------------------+ +Derive View Tag +~~~~~~~~~~~~~~~~~~ + +**Monero** + +crypto::derive_view_tag. + +**Description** + +Derive the view tag of an output. + + | compute |Drv| = |dec|[|spk|](|eDrv|) + | compute |vtf| = |Hs|("view_tag" \|, |Drv|, |idx|) + | compute |vt| = |vtf|[0:1] + +return |vt|. + +**Command** + ++-----+-----+-----+-----+----------+ +| CLA | INS | P1 | P2 | LC | ++=====+=====+=====+=====+==========+ +| 03 | 3B | 00 | 00 | 25 or 45 | ++-----+-----+-----+-----+----------+ + +**Command data** + ++--------+-----------------------------------------------------------------+ +| Length | Value | ++========+=================================================================+ +| 01 | 00 | ++--------+-----------------------------------------------------------------+ +| 20 | encrypted key derivation |eDrv| | ++--------+-----------------------------------------------------------------+ +| 20 | ephemeral hmac (optional, only during active transaction) | ++--------+-----------------------------------------------------------------+ +| 04 | index | ++--------+-----------------------------------------------------------------+ + +**Response data** + ++--------+-----------------------------------------------------------------+ +| Length | Value | ++========+=================================================================+ +| 01 | view tag |vt| | ++--------+-----------------------------------------------------------------+ + + Generate Keypair ~~~~~~~~~~~~~~~~ diff --git a/src/monero_api.h b/src/monero_api.h index e37a785..5247d7d 100644 --- a/src/monero_api.h +++ b/src/monero_api.h @@ -47,6 +47,7 @@ int monero_apdu_derivation_to_scalar(void); int monero_apdu_derive_public_key(void); int monero_apdu_derive_secret_key(void); int monero_apdu_generate_key_image(void); +int monero_apdu_derive_view_tag(void); int monero_apdu_derive_subaddress_public_key(void); int monero_apdu_get_subaddress(void); int monero_apdu_get_subaddress_spend_public_key(void); @@ -169,6 +170,8 @@ void monero_derive_public_key(unsigned char *x, unsigned char *drv_data, unsigne unsigned char *ec_pub); void monero_secret_key_to_public_key(unsigned char *ec_pub, unsigned char *ec_priv); void monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned char *x); +void monero_derive_view_tag(unsigned char *view_tag, unsigned char *drv_data, + unsigned int out_idx); void monero_derive_subaddress_public_key(unsigned char *x, unsigned char *pub, unsigned char *drv_data, unsigned int index); diff --git a/src/monero_crypto.c b/src/monero_crypto.c index 8776fed..df01036 100644 --- a/src/monero_crypto.c +++ b/src/monero_crypto.c @@ -519,6 +519,22 @@ void monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned ch monero_ecmul_k(img, I, x); } +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +void monero_derive_view_tag(unsigned char *view_tag, unsigned char *drv_data, + unsigned int out_idx) { + unsigned char varint[8 + 32 + 8]; + unsigned int len_varint; + + os_memmove(varint, "view_tag", 8); + os_memmove(varint + 8, drv_data, 32); + len_varint = monero_encode_varint(varint + 8 + 32, 8, out_idx); + len_varint += 8 + 32; + monero_keccak_F(varint, len_varint, varint); + os_memmove(view_tag, varint, 1); +} + /* ======================================================================= */ /* SUB ADDRESS */ /* ======================================================================= */ diff --git a/src/monero_dispatch.c b/src/monero_dispatch.c index 839e57f..b11ff4e 100644 --- a/src/monero_dispatch.c +++ b/src/monero_dispatch.c @@ -84,6 +84,7 @@ int check_ins_access() { case INS_DERIVE_PUBLIC_KEY: case INS_DERIVE_SECRET_KEY: case INS_GEN_KEY_IMAGE: + case INS_DERIVE_VIEW_TAG: case INS_SECRET_KEY_TO_PUBLIC_KEY: case INS_SECRET_KEY_ADD: case INS_GENERATE_KEYPAIR: @@ -188,6 +189,9 @@ int monero_dispatch() { case INS_GEN_KEY_IMAGE: sw = monero_apdu_generate_key_image(); break; + case INS_DERIVE_VIEW_TAG: + sw = monero_apdu_derive_view_tag(); + break; case INS_SECRET_KEY_ADD: sw = monero_apdu_sc_add(); break; diff --git a/src/monero_key.c b/src/monero_key.c index f80a4d5..0f552db 100644 --- a/src/monero_key.c +++ b/src/monero_key.c @@ -604,6 +604,27 @@ int monero_apdu_generate_key_image( return SW_OK; } +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +int monero_apdu_derive_view_tag( + /*const crypto::key_derivation &derivation, const size_t output_index, crypto::view_tag &view_tag*/) { + unsigned char derivation[32]; + unsigned int output_index; + unsigned char res[1]; + + // fetch + monero_io_fetch_decrypt(derivation, 32, TYPE_DERIVATION); + output_index = monero_io_fetch_u32(); + monero_io_discard(0); + + // derive and keep + monero_derive_view_tag(res, derivation, output_index); + + monero_io_insert(res, 1); + return SW_OK; +} + /* ----------------------------------------------------------------------- */ /* --- --- */ /* ----------------------------------------------------------------------- */ @@ -692,7 +713,7 @@ int monero_apdu_get_subaddress_secret_key(/*const crypto::secret_key& sec, const /* --- --- */ /* ----------------------------------------------------------------------- */ -int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_sec, crypto::public_key Aout, crypto::public_key Bout, size_t output_index, bool is_change, bool is_subaddress, bool need_additional_key*/) { +int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_sec, crypto::public_key Aout, crypto::public_key Bout, size_t output_index, bool is_change, bool is_subaddress, bool need_additional_key, bool use_view_tags*/) { // IN unsigned int tx_version; unsigned char tx_key[32]; @@ -704,10 +725,12 @@ int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_se unsigned char is_subaddress; unsigned char need_additional_txkeys; unsigned char additional_txkey_sec[32]; + unsigned char use_view_tags; // OUT unsigned char additional_txkey_pub[32]; #define amount_key tx_key #define out_eph_public_key additional_txkey_sec + unsigned char view_tag[1]; // TMP unsigned char derivation[32]; @@ -728,6 +751,7 @@ int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_se } else { monero_io_fetch(NULL, 32); } + use_view_tags = monero_io_fetch_u8(); // update outkeys hash control if (G_monero_vstate.tx_sig_mode == TRANSACTION_CREATE_REAL) { @@ -769,6 +793,11 @@ int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_se // compute ephemeral output key monero_derive_public_key(out_eph_public_key, derivation, output_index, Bout); + // compute view tag + if (use_view_tags) { + monero_derive_view_tag(view_tag, derivation, output_index); + } + // send all monero_io_discard(0); monero_io_insert_encrypt(amount_key, 32, TYPE_AMOUNT_KEY); @@ -776,6 +805,9 @@ int monero_apu_generate_txout_keys(/*size_t tx_version, crypto::secret_key tx_se if (need_additional_txkeys) { monero_io_insert(additional_txkey_pub, 32); } + if (use_view_tags) { + monero_io_insert(view_tag, 1); + } G_monero_vstate.tx_output_cnt++; return SW_OK; } diff --git a/src/monero_types.h b/src/monero_types.h index febb84f..d83c4d5 100644 --- a/src/monero_types.h +++ b/src/monero_types.h @@ -252,6 +252,7 @@ typedef struct monero_v_state_s monero_v_state_t; #define INS_DERIVE_PUBLIC_KEY 0x36 #define INS_DERIVE_SECRET_KEY 0x38 #define INS_GEN_KEY_IMAGE 0x3A +#define INS_DERIVE_VIEW_TAG 0x3B #define INS_SECRET_KEY_ADD 0x3C #define INS_GENERATE_KEYPAIR 0x40 #define INS_SECRET_SCAL_MUL_KEY 0x42 diff --git a/tests/monero_client/monero_types.py b/tests/monero_client/monero_types.py index 42697ec..8dcd258 100644 --- a/tests/monero_client/monero_types.py +++ b/tests/monero_client/monero_types.py @@ -45,6 +45,7 @@ class InsType(enum.IntEnum): INS_DERIVE_PUBLIC_KEY = 0x36 INS_DERIVE_SECRET_KEY = 0x38 INS_GEN_KEY_IMAGE = 0x3A + INS_DERIVE_VIEW_TAG = 0x3B INS_SECRET_KEY_ADD = 0x3C INS_GENERATE_KEYPAIR = 0x40 INS_SECRET_SCAL_MUL_KEY = 0x42 diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 18ab9b5..7e47bb4 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -77,3 +77,15 @@ def test_gen_key_derivation(monero): ) assert expected == monero.xor_cipher(_d_in, b"\x55") # decrypt _d_in + +def test_derive_view_tag(monero): + expected_view_tag: bytes = bytes.fromhex("76") + + derivation: bytes = bytes.fromhex("0fc47054f355ced4d67de73bfa12e4c7" + "8ff19089548fffa7d07a674741860f97") + + output_index = 0 + + view_tag: bytes = monero.derive_view_tag(derivation, output_index) + + assert expected_view_tag == view_tag From eb7fee39e63674175c72eb05f409ea8273b9151f Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Wed, 13 Jul 2022 13:27:35 +0200 Subject: [PATCH 02/11] Lint --- src/monero_api.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/monero_api.h b/src/monero_api.h index 5247d7d..0ce728d 100644 --- a/src/monero_api.h +++ b/src/monero_api.h @@ -170,8 +170,7 @@ void monero_derive_public_key(unsigned char *x, unsigned char *drv_data, unsigne unsigned char *ec_pub); void monero_secret_key_to_public_key(unsigned char *ec_pub, unsigned char *ec_priv); void monero_generate_key_image(unsigned char *img, unsigned char *P, unsigned char *x); -void monero_derive_view_tag(unsigned char *view_tag, unsigned char *drv_data, - unsigned int out_idx); +void monero_derive_view_tag(unsigned char *view_tag, unsigned char *drv_data, unsigned int out_idx); void monero_derive_subaddress_public_key(unsigned char *x, unsigned char *pub, unsigned char *drv_data, unsigned int index); From 2ec65d615c49878f032f7c5677c60c0dfbb557b3 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Mon, 18 Jul 2022 14:20:02 +0200 Subject: [PATCH 03/11] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2e46d19..ccc3eaa 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ endif APPVERSION_M=1 APPVERSION_N=7 -APPVERSION_P=8 +APPVERSION_P=9 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) SPECVERSION="1.0" From dfafbb7925c89f51e88c707a4eb49eae1dd9876c Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Tue, 19 Jul 2022 16:41:50 +0200 Subject: [PATCH 04/11] Fix signature test when no view_tag is required --- tests/monero_client/monero_cmd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/monero_client/monero_cmd.py b/tests/monero_client/monero_cmd.py index 41230f6..b81ee61 100644 --- a/tests/monero_client/monero_cmd.py +++ b/tests/monero_client/monero_cmd.py @@ -174,6 +174,7 @@ def gen_txout_keys(self, b"\x01" if is_change_addr else b"\x00", b"\x01" if is_subaddress else b"\x00", b"\x00" * 33, # additional_txkeys + b"\x00" * 33, # use_view_tags )) self.device.send(cla=PROTOCOL_VERSION, From fd5491704d048f9ea6a506dd703babe8d62ff3c0 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Tue, 19 Jul 2022 16:43:04 +0200 Subject: [PATCH 05/11] Add complete test for view_tags --- tests/monero_client/monero_crypto_cmd.py | 24 +++++++ tests/test_crypto.py | 83 +++++++++++++++++++++--- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/tests/monero_client/monero_crypto_cmd.py b/tests/monero_client/monero_crypto_cmd.py index 33cd512..617acd9 100644 --- a/tests/monero_client/monero_crypto_cmd.py +++ b/tests/monero_client/monero_crypto_cmd.py @@ -259,3 +259,27 @@ def gen_key_derivation(self, pub_key: bytes, _priv_key: bytes) -> bytes: assert len(response) == 32 return _d_in + + def derive_view_tag(self, derivation: bytes, output_index: bytes) -> int: + ins: InsType = InsType.INS_DERIVE_VIEW_TAG + + payload: bytes = b"".join([ + derivation, + output_index, + ]) + + self.device.send(cla=PROTOCOL_VERSION, + ins=ins, + p1=0, + p2=0, + option=0, + payload=payload) + + sw, response = self.device.recv() # type: int, bytes + + if not sw & 0x9000: + raise DeviceError(sw, ins) + + assert len(response) == 1 + + return int.from_bytes(response, "big") diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 7e47bb4..a3d1a3d 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -78,14 +78,79 @@ def test_gen_key_derivation(monero): assert expected == monero.xor_cipher(_d_in, b"\x55") # decrypt _d_in -def test_derive_view_tag(monero): - expected_view_tag: bytes = bytes.fromhex("76") - - derivation: bytes = bytes.fromhex("0fc47054f355ced4d67de73bfa12e4c7" - "8ff19089548fffa7d07a674741860f97") - output_index = 0 +class Derivation_Test: + _derivation: bytes + _output_index: bytes + _expected_view_tag: int + + def __init__(self, derivation, output_index, expected_view_tag): + self._derivation = bytes.fromhex(derivation) + self._output_index = (output_index).to_bytes(4, byteorder='big') + self._expected_view_tag = expected_view_tag + + def do_test(self, monero): + assert self._expected_view_tag == monero.derive_view_tag(self._derivation, self._output_index) + +DERIVATION_TESTS = [ + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 0, 0x76), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 1, 0xd6), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 2, 0x87), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 3, 0x1b), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 12, 0xd6), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 13, 0xe9), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 14, 0x12), + Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 15, 0x26), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 0, 0x70), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 1, 0x81), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 2, 0xa0), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 3, 0xec), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 12, 0x22), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 13, 0x0a), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 14, 0x87), + Derivation_Test("a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc", 15, 0x76), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 0, 0x93), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 1, 0x67), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 2, 0x9d), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 3, 0x2d), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 12, 0x63), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 13, 0xcf), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 14, 0xef), + Derivation_Test("7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8", 15, 0x10), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 0, 0x90), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 1, 0x5a), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 2, 0xde), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 3, 0x21), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 12, 0x57), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 13, 0x52), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 14, 0x6f), + Derivation_Test("fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0", 15, 0xeb), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 0, 0xc6), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 1, 0x60), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 2, 0xf0), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 3, 0x71), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 12, 0x0e), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 13, 0x42), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 14, 0xb2), + Derivation_Test("ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d", 15, 0x61), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 0, 0x4c), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 1, 0x9b), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 2, 0x64), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 3, 0xff), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 12, 0xe3), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 13, 0x24), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 14, 0xea), + Derivation_Test("25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a", 15, 0x3b), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 0, 0x74), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 1, 0x77), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 2, 0xa9), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 3, 0x44), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 12, 0x75), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 13, 0x05), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 14, 0xca), + Derivation_Test("8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8", 15, 0x00), +] - view_tag: bytes = monero.derive_view_tag(derivation, output_index) - - assert expected_view_tag == view_tag +def test_derive_view_tag(monero): + for test in DERIVATION_TESTS: + test.do_test(monero) From 62fdc2883e9ed980ff008036a6aa7dc56cfcf947 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Tue, 19 Jul 2022 16:43:55 +0200 Subject: [PATCH 06/11] Fix view tag derivation test by sending encrypted derivation --- tests/test_crypto.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_crypto.py b/tests/test_crypto.py index a3d1a3d..5ec3cf6 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -90,7 +90,8 @@ def __init__(self, derivation, output_index, expected_view_tag): self._expected_view_tag = expected_view_tag def do_test(self, monero): - assert self._expected_view_tag == monero.derive_view_tag(self._derivation, self._output_index) + encrypted_derivation = monero.xor_cipher(self._derivation, b"\x55") # encrypt with dummy key 0x55 + assert self._expected_view_tag == monero.derive_view_tag(encrypted_derivation, self._output_index) DERIVATION_TESTS = [ Derivation_Test("0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97", 0, 0x76), From 4fe19ac7aeb2eb9b59c98d756f4f9e97fc123826 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Fri, 22 Jul 2022 16:18:32 +0200 Subject: [PATCH 07/11] Review from Selsta --- Makefile | 4 ++-- src/monero_init.c | 2 +- tests/test_sig.py | 11 +++++++---- tests/test_version.py | 31 +++++-------------------------- 4 files changed, 15 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index ccc3eaa..b2eca0e 100644 --- a/Makefile +++ b/Makefile @@ -39,8 +39,8 @@ endif #DEFINES += MONERO_BETA APPVERSION_M=1 -APPVERSION_N=7 -APPVERSION_P=9 +APPVERSION_N=8 +APPVERSION_P=0 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) SPECVERSION="1.0" diff --git a/src/monero_init.c b/src/monero_init.c index ad637f3..7ef66fa 100644 --- a/src/monero_init.c +++ b/src/monero_init.c @@ -177,7 +177,7 @@ void monero_install(unsigned char netId) { /* ----------------------------------------------------------------------- */ /* --- Reset --- */ /* ----------------------------------------------------------------------- */ -const char* const monero_supported_client[] = {"0.17.0.", "0.17.1.", "0.17.2.", "0.17.3."}; +const char* const monero_supported_client[] = {"0.18.0."}; #define MONERO_SUPPORTED_CLIENT_SIZE \ (sizeof(monero_supported_client) / sizeof(monero_supported_client[0])) diff --git a/tests/test_sig.py b/tests/test_sig.py index 0313da9..45fcb05 100644 --- a/tests/test_sig.py +++ b/tests/test_sig.py @@ -1,6 +1,7 @@ import pytest from monero_client.monero_types import SigType, Keys +from monero_client.exception import ClientNotSupported @pytest.mark.incremental @@ -47,10 +48,12 @@ def state(): @staticmethod def test_set_sig(monero): - major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.17.0.0" - ) # type: int, int, int - assert (major, minor) == (1, 7) # version of the Monero app + # Send a reset, ignore the version check and version related errors. + # They belong in a dedicated test + with pytest.raises(ClientNotSupported) as excinfo: + monero.reset_and_get_version( + monero_client_version=b"0.0.0.0" + ) sig_mode: SigType = monero.set_signature_mode(sig_type=SigType.REAL) assert sig_mode == SigType.REAL diff --git a/tests/test_version.py b/tests/test_version.py index 0f921c1..7f24e0a 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,38 +1,17 @@ import pytest # pylint: disable=wildcard-import, unused-wildcard-import -from monero_client.exception import * +from monero_client.exception import ClientNotSupported def test_version(monero): major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.17.0.0" + monero_client_version=b"0.18.0.0" ) # type: int, int, int - assert (major, minor) == (1, 7) # version of the Monero app + assert (major, minor) == (1, 8) # version of the Monero app - # another compatible version of the Monero client - major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.17.1.0" - ) # type: int, int, int - - assert (major, minor) == (1, 7) # version of the Monero app - - # another compatible version of the Monero client - major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.17.2.0" - ) # type: int, int, int - - assert (major, minor) == (1, 7) # version of the Monero app - - # another compatible version of the Monero client - major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.17.3.0" - ) # type: int, int, int - - assert (major, minor) == (1, 7) # version of the Monero app - -@pytest.mark.xfail(raises=ClientNotSupported) def test_old_client_version(monero): # should raise ClientNotSupported[0x6a30] - monero.reset_and_get_version(b"0.15.0.0") + with pytest.raises(ClientNotSupported) as excinfo: + monero.reset_and_get_version(b"0.17.0.0") From c5b95f11c985cbf78085e1624416e483344980b9 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Fri, 22 Jul 2022 16:26:23 +0200 Subject: [PATCH 08/11] Rationalize Lint CI run --- .github/workflows/lint-workflow.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-workflow.yml b/.github/workflows/lint-workflow.yml index 67a2024..3a64caf 100644 --- a/.github/workflows/lint-workflow.yml +++ b/.github/workflows/lint-workflow.yml @@ -1,6 +1,14 @@ name: Code style check -on: [push, pull_request] +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop jobs: job_lint: From 1c7bb4b0db0052dc7d0609474af40d95be47b48d Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Fri, 22 Jul 2022 16:31:42 +0200 Subject: [PATCH 09/11] Following suggestion from Selsta, support client v0.18. instead of v0.18.0. --- src/monero_init.c | 2 +- tests/test_version.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/monero_init.c b/src/monero_init.c index 7ef66fa..d521d47 100644 --- a/src/monero_init.c +++ b/src/monero_init.c @@ -177,7 +177,7 @@ void monero_install(unsigned char netId) { /* ----------------------------------------------------------------------- */ /* --- Reset --- */ /* ----------------------------------------------------------------------- */ -const char* const monero_supported_client[] = {"0.18.0."}; +const char* const monero_supported_client[] = {"0.18."}; #define MONERO_SUPPORTED_CLIENT_SIZE \ (sizeof(monero_supported_client) / sizeof(monero_supported_client[0])) diff --git a/tests/test_version.py b/tests/test_version.py index 7f24e0a..4a05775 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -5,13 +5,23 @@ def test_version(monero): + # Monero does not have any consensus changes with minor point upgrades, we support 0.18. + major, minor, patch = monero.reset_and_get_version( monero_client_version=b"0.18.0.0" ) # type: int, int, int assert (major, minor) == (1, 8) # version of the Monero app + major, minor, patch = monero.reset_and_get_version( + monero_client_version=b"0.18.9.0" + ) # type: int, int, int + + assert (major, minor) == (1, 8) # version of the Monero app + def test_old_client_version(monero): # should raise ClientNotSupported[0x6a30] with pytest.raises(ClientNotSupported) as excinfo: monero.reset_and_get_version(b"0.17.0.0") + with pytest.raises(ClientNotSupported) as excinfo: + monero.reset_and_get_version(b"0.19.0.0") From 1629a486f478c0952c9d5f12f8edcb0908cda5c3 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Mon, 25 Jul 2022 10:58:37 +0200 Subject: [PATCH 10/11] Add an explicit mechanism for the Monero app to refuse some client versions --- src/monero_init.c | 39 ++++++++++++++++++++++++--------------- tests/test_sig.py | 9 +++------ tests/test_version.py | 43 ++++++++++++++++++++++++++++--------------- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/monero_init.c b/src/monero_init.c index d521d47..72c0d54 100644 --- a/src/monero_init.c +++ b/src/monero_init.c @@ -177,33 +177,42 @@ void monero_install(unsigned char netId) { /* ----------------------------------------------------------------------- */ /* --- Reset --- */ /* ----------------------------------------------------------------------- */ -const char* const monero_supported_client[] = {"0.18."}; -#define MONERO_SUPPORTED_CLIENT_SIZE \ - (sizeof(monero_supported_client) / sizeof(monero_supported_client[0])) +// Accept the following versions and their derivates +const char* const supported_clients[] = {"0.18."}; +#define MONERO_SUPPORTED_CLIENT_SIZE (sizeof(supported_clients) / sizeof(supported_clients[0])) + +// Explicitly refuse the following versions +const char* const refused_clients[] = {"0.18.0.0."}; +#define MONERO_REFUSED_CLIENT_SIZE (sizeof(refused_clients) / sizeof(refused_clients[0])) int monero_apdu_reset() { unsigned int client_version_len; char client_version[16]; + memset(client_version, '\0', 16); client_version_len = G_monero_vstate.io_length - G_monero_vstate.io_offset; if (client_version_len > 14) { THROW(SW_CLIENT_NOT_SUPPORTED + 1); } monero_io_fetch((unsigned char*)&client_version[0], client_version_len); + // Add '.' suffix to avoid 'X.1' prefixing 'X.10' client_version[client_version_len] = '.'; - client_version_len++; - client_version[client_version_len] = 0; - unsigned int i = 0; - while (i < MONERO_SUPPORTED_CLIENT_SIZE) { - unsigned int monero_supported_client_len = strlen((char*)PIC(monero_supported_client[i])); - if ((monero_supported_client_len <= client_version_len) && - (os_memcmp((char*)PIC(monero_supported_client[i]), client_version, - monero_supported_client_len) == 0)) { - break; + // Check if version is explicitly refused + for (uint32_t i = 0; i < MONERO_REFUSED_CLIENT_SIZE; ++i) { + if (strcmp(PIC(refused_clients[i]), client_version) == 0) { + THROW(SW_CLIENT_NOT_SUPPORTED); } - i++; } - if (i == MONERO_SUPPORTED_CLIENT_SIZE) { - THROW(SW_CLIENT_NOT_SUPPORTED); + // Check if version is supported + uint32_t i; + for (i = 0; i < MONERO_SUPPORTED_CLIENT_SIZE + 1; ++i) { + // Use strncmp to allow supported version prefixing client version + unsigned int supported_clients_len = strlen((char*)PIC(supported_clients[i])); + if (strncmp(PIC(supported_clients[i]), client_version, supported_clients_len) == 0) { + break; + } + if (i == MONERO_SUPPORTED_CLIENT_SIZE) { + THROW(SW_CLIENT_NOT_SUPPORTED); + } } monero_io_discard(0); diff --git a/tests/test_sig.py b/tests/test_sig.py index 45fcb05..2429b2e 100644 --- a/tests/test_sig.py +++ b/tests/test_sig.py @@ -48,12 +48,9 @@ def state(): @staticmethod def test_set_sig(monero): - # Send a reset, ignore the version check and version related errors. - # They belong in a dedicated test - with pytest.raises(ClientNotSupported) as excinfo: - monero.reset_and_get_version( - monero_client_version=b"0.0.0.0" - ) + monero.reset_and_get_version( + monero_client_version=b"0.18" + ) sig_mode: SigType = monero.set_signature_mode(sig_type=SigType.REAL) assert sig_mode == SigType.REAL diff --git a/tests/test_version.py b/tests/test_version.py index 4a05775..1997248 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -3,25 +3,38 @@ # pylint: disable=wildcard-import, unused-wildcard-import from monero_client.exception import ClientNotSupported - -def test_version(monero): - # Monero does not have any consensus changes with minor point upgrades, we support 0.18. - +def check_accepted_version(monero, valid_version: bytes): major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.18.0.0" + monero_client_version=valid_version ) # type: int, int, int + assert (major, minor, patch) == (1, 8, 0) # version of the Monero app - assert (major, minor) == (1, 8) # version of the Monero app +def check_refused_version(monero, invalid_version: bytes): + with pytest.raises(ClientNotSupported) as excinfo: + monero.reset_and_get_version(invalid_version) - major, minor, patch = monero.reset_and_get_version( - monero_client_version=b"0.18.9.0" - ) # type: int, int, int - assert (major, minor) == (1, 8) # version of the Monero app +def test_version(monero): + # Monero does not have any consensus changes with minor point upgrades, we support the entire 0.18. range, except 0.18.0.0 + check_accepted_version(monero, b"0.18") + check_accepted_version(monero, b"0.18.0.1") + check_accepted_version(monero, b"0.18.0.10") + check_accepted_version(monero, b"0.18.1.1") + check_accepted_version(monero, b"0.18.9.0") + check_accepted_version(monero, b"0.18.18.0") def test_old_client_version(monero): - # should raise ClientNotSupported[0x6a30] - with pytest.raises(ClientNotSupported) as excinfo: - monero.reset_and_get_version(b"0.17.0.0") - with pytest.raises(ClientNotSupported) as excinfo: - monero.reset_and_get_version(b"0.19.0.0") + # Not supported anymore + check_refused_version(monero, b"0.0.0.0") + check_refused_version(monero, b"0.17.0.0") + check_refused_version(monero, b"0.17.18.0") + # Explicitly disabled + check_refused_version(monero, b"0.18.0.0") + # Not yet supported + check_refused_version(monero, b"0.19.0.0") + check_refused_version(monero, b"1.0.0.0") + # Regex shenanigans + check_refused_version(monero, b"") + check_refused_version(monero, b"0") + check_refused_version(monero, b"0.0.") + check_refused_version(monero, b"0.180") From 163e5d2ba8f7212bf3ac73f6c2fc60c7d0a624fb Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Tue, 26 Jul 2022 09:12:23 +0200 Subject: [PATCH 11/11] Separate function for version checking --- src/monero_init.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/monero_init.c b/src/monero_init.c index 72c0d54..bf2dd34 100644 --- a/src/monero_init.c +++ b/src/monero_init.c @@ -185,6 +185,24 @@ const char* const supported_clients[] = {"0.18."}; const char* const refused_clients[] = {"0.18.0.0."}; #define MONERO_REFUSED_CLIENT_SIZE (sizeof(refused_clients) / sizeof(refused_clients[0])) +static bool is_client_version_valid(const char* client_version) { + // Check if version is explicitly refused + for (uint32_t i = 0; i < MONERO_REFUSED_CLIENT_SIZE; ++i) { + if (strcmp(PIC(refused_clients[i]), client_version) == 0) { + return false; + } + } + // Check if version is supported + for (uint32_t i = 0; i < MONERO_SUPPORTED_CLIENT_SIZE; ++i) { + // Use strncmp to allow supported version prefixing client version + unsigned int supported_clients_len = strlen(PIC(supported_clients[i])); + if (strncmp(PIC(supported_clients[i]), client_version, supported_clients_len) == 0) { + return true; + } + } + return false; +} + int monero_apdu_reset() { unsigned int client_version_len; char client_version[16]; @@ -196,23 +214,9 @@ int monero_apdu_reset() { monero_io_fetch((unsigned char*)&client_version[0], client_version_len); // Add '.' suffix to avoid 'X.1' prefixing 'X.10' client_version[client_version_len] = '.'; - // Check if version is explicitly refused - for (uint32_t i = 0; i < MONERO_REFUSED_CLIENT_SIZE; ++i) { - if (strcmp(PIC(refused_clients[i]), client_version) == 0) { - THROW(SW_CLIENT_NOT_SUPPORTED); - } - } - // Check if version is supported - uint32_t i; - for (i = 0; i < MONERO_SUPPORTED_CLIENT_SIZE + 1; ++i) { - // Use strncmp to allow supported version prefixing client version - unsigned int supported_clients_len = strlen((char*)PIC(supported_clients[i])); - if (strncmp(PIC(supported_clients[i]), client_version, supported_clients_len) == 0) { - break; - } - if (i == MONERO_SUPPORTED_CLIENT_SIZE) { - THROW(SW_CLIENT_NOT_SUPPORTED); - } + + if (!is_client_version_valid(client_version)) { + THROW(SW_CLIENT_NOT_SUPPORTED); } monero_io_discard(0);