zbyh%@*TMPUh-lMm(2T41qS4%MZ@G_oVc^6u(e^K7+))4kz-k|TfN^{}dKBdXs2s%F zqt@ooIBI+z?1P4;2_x0`T5q>wbEGLWx}@0`%2ZZB?a17mG@`x86`*V>^BS~QyUikf zas|jfv^$r+sm|^%SAZk`@2vaJqg$>~kNUf)RfT38?lOPT4*&oF008dr3v^`?fmkS) QN&o-=07*qoM6N<$g1|W1y8r+H literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanox/test_sign_message_simple/00000.png b/tests/snapshots/nanox/test_sign_message_simple/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..a95fdd26f372c9b936555276daf0234a6d63a0dc GIT binary patch literal 399 zcmV;A0dW3_P)uO23*$P?WUkY3nc%rVW2M~wBM9*|mG%teu@a*(=7*QeH>W^Ccd+}6 Cld?oMg^w`sP-12pwC>QSk?e60;D?4u6-LlWQ8=v* ~TaD^jSA-^6>reV#!|$k`ka=R?s((-BT;(>s z{rrctS{{SC;DVT(3%o`b?=KU~TC`u%@`H=$rxKx*q75PsS%J<&2Gf2rZ@OdsQ{8gX PA&{`AtDnm{r-UW|Wpbv? literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanox/test_sign_message_simple/00002.png b/tests/snapshots/nanox/test_sign_message_simple/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..6df9c9df488d0cf8e3ff56ebe9be01413918d2a2 GIT binary patch literal 461 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|^i$>EaktG3V{Az`Vl>JPnCq zZ`JPn_qel^e}$k?f`?Sjl|T1`-6aK__c(}wbS1nyzxwf%pnn3%Z>HKDJYCJ;|NXsf z!-SwNL%GxOHKjG@wjbRp6lk#~C-c6wLc7|r8$DAv!Xkz2okYKH>but}`QLiM%Z}Ef z^eM#w(|nEvzCYCSnPtk! FfSoydkEUAyNKu>ZJM-rQo?T z&xB?#Tks+B&8bCUUR9B BSUuQpVc;vbMDUV zN}uh$cy?CJT=`0_Q@fMi2gW?yBRKme3-9ZW7uvJ8o_rj1dZ)>(hI=z_efY74C$D(s z^;ToUwYNj~)=f783UmGam-V^OY5m_#0rA)4s+0d;z7Q=X`0;J3++OSH+nMY?x$pSy z_clj1@c(W{--+c7Pan5bNEYn$i`=}kOZ`9Nf`4moI~!&c&o@4_v_^mP?cdIe vD4cuHn_ckM5kA4=SnQY FSd4J{-YHcY(QO5;J{<~e9q=7F?O4)!WO}6UUdH! zS+Hz=(4})vCM2-VD4nt3W%LdvkyzeG%km#`1t_mu*zR}#jNB{EKZnW}g5-=Bym-_% z`=7%C>zxJL|9msNSMXnbQRayy^)nr>EU7olTHqoc^vG(B%bo(Id*|kR{84!`z2x6n zZ`;PkbrPvd6U}A>Y0chs_tyHmLi6kw6c{>JvYiQ=+MTFTx8d*9^V|R0+_75oZt9or zLetinU!`YMvFyLCb8XxElhO0{v2rTkNR$jMW7~4}*8a52+MC^J%KMbxPN-JRb2RNP zEsp0ebAPS3=e2G53#Z$XZAI6Yd=Y K000000Kjcts{{C_lv4Nm z7~{>6oa9%4QR-_(W%yYgz%^owMoF)k6`)N2_@};FJ|0;mBFqZVGM)H^YXRyxZe0V{ z0tBmlX2Q4t&tO~t>z$`?EkK=p>%GXe0EKn-!(&duc|p{tgj-`e;k`P5Yxu4MzZH-@ zrWpqSLVWLL7IxCBll?jpov1D`9FEjDOE6MVU8Nq>yL=`10Jn*%Z9#8NUfmwGVB zbyh%@*TMPUh-lMm(2T41qS4%MZ@G_oVc^6u(e^K7+))4kz-k|TfN^{}dKBdXs2s%F zqt@ooIBI+z?1P4;2_x0`T5q>wbEGLWx}@0`%2ZZB?a17mG@`x86`*V>^BS~QyUikf zas|jfv^$r+sm|^%SAZk`@2vaJqg$>~kNUf)RfT38?lOPT4*&oF008dr3v^`?fmkS) QN&o-=07*qoM6N<$g1|W1y8r+H literal 0 HcmV?d00001 diff --git a/tests/test_sign_cmd.py b/tests/test_sign_cmd.py index 0c7dd6d..492e3bf 100644 --- a/tests/test_sign_cmd.py +++ b/tests/test_sign_cmd.py @@ -64,7 +64,7 @@ def test_sign_tx_simple(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_simple_ecdsa(firmware, backend, navigator, test_name): # Use the app interface instead of raw interface @@ -120,7 +120,7 @@ def test_sign_tx_simple_ecdsa(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_p2sh(firmware, backend, navigator, test_name): @@ -177,7 +177,7 @@ def test_sign_tx_p2sh(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_simple_sendint(firmware, backend, navigator, test_name): # Use the app interface instead of raw interface @@ -233,7 +233,7 @@ def test_sign_tx_simple_sendint(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_simple_sendmaxu64(firmware, backend, navigator, test_name): # Use the app interface instead of raw interface @@ -289,7 +289,7 @@ def test_sign_tx_simple_sendmaxu64(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_simple_change_idx1(firmware, backend, navigator, test_name): # Use the app interface instead of raw interface @@ -358,7 +358,7 @@ def test_sign_tx_simple_change_idx1(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_with_change(firmware, backend, navigator, test_name): # Use the app interface instead of raw interface @@ -423,7 +423,7 @@ def test_sign_tx_with_change(firmware, backend, navigator, test_name): response = client.get_async_response().data _, _, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(0) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) def test_sign_tx_with_invalid_change(backend): backend.raise_policy = RaisePolicy.RAISE_NOTHING @@ -666,7 +666,7 @@ def test_sign_tx_max(firmware, backend, navigator, test_name): response = client.get_async_response().data has_more, input_index, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(input_index) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) while has_more > 0: idx = idx + 1 @@ -677,7 +677,7 @@ def test_sign_tx_max(firmware, backend, navigator, test_name): response = client.get_next_signature().data has_more, input_index, _, der_sig, _, sighash = unpack_sign_tx_response(response) assert transaction.get_sighash(input_index) == sighash - assert check_signature_validity(public_key, der_sig, sighash, transaction) + assert check_signature_validity(public_key, der_sig, sighash) # Transaction signature refused test # The test will ask for a transaction signature that will be refused on screen diff --git a/tests/test_sign_personal_message_cmd.py b/tests/test_sign_personal_message_cmd.py new file mode 100644 index 0000000..34b53fd --- /dev/null +++ b/tests/test_sign_personal_message_cmd.py @@ -0,0 +1,111 @@ +from application_client.kaspa_command_sender import KaspaCommandSender, Errors, InsType, P1, P2 +from application_client.kaspa_message import PersonalMessage +from application_client.kaspa_response_unpacker import unpack_get_public_key_response, unpack_sign_message_response +from ragger.backend import RaisePolicy +from ragger.navigator import NavInsID +from utils import ROOT_SCREENSHOT_PATH, check_signature_validity + +# In this tests we check the behavior of the device when asked to sign a transaction + + +# In this test se send to the device a transaction to sign and validate it on screen +# We will ensure that the displayed information is correct by using screenshots comparison +def test_sign_message_simple(firmware, backend, navigator, test_name): + # Use the app interface instead of raw interface + client = KaspaCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/111111'/0'/1/5" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + address_type = 1 + address_index = 5 + message = "Hello Kaspa!" + + message_data = PersonalMessage(address_type, address_index, message) + + # Send the sign device instruction. + # As it requires on-screen validation, the function is asynchronous. + # It will yield the result when the navigation is done + with client.sign_message(message_data=message_data): + # Validate the on-screen request by performing the navigation appropriate for this device + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name) + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, der_sig, _, message_hash = unpack_sign_message_response(response) + + assert message_hash == message_data.to_hash() + assert check_signature_validity(public_key, der_sig, message_hash) + +def test_sign_message_kanji(firmware, backend, navigator, test_name): + # Use the app interface instead of raw interface + client = KaspaCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/111111'/0'/1/3" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + address_type = 1 + address_index = 3 + message = "こんにちは世界" + + message_data = PersonalMessage(address_type, address_index, message) + + # Send the sign device instruction. + # As it requires on-screen validation, the function is asynchronous. + # It will yield the result when the navigation is done + with client.sign_message(message_data=message_data): + # Validate the on-screen request by performing the navigation appropriate for this device + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name) + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, der_sig, _, message_hash = unpack_sign_message_response(response) + + assert message_hash == message_data.to_hash() + assert check_signature_validity(public_key, der_sig, message_hash) + +def test_sign_message_too_long(firmware, backend, navigator, test_name): + backend.raise_policy = RaisePolicy.RAISE_NOTHING + # Use the app interface instead of raw interface + client = KaspaCommandSender(backend) + + address_type = 1 + address_index = 4 + message = '''Lorem ipsum dolor sit amet. Aut omnis amet id voluptatem eligendi sit accusantium dolorem 33 corrupti necessitatibus hic consequatur quod et maiores alias non molestias suscipit? Est voluptatem magni qui odit eius est eveniet cupiditate id eius quae''' + + message_data = PersonalMessage(address_type, address_index, message) + + last_response = client.send_raw_apdu(InsType.SIGN_MESSAGE, p1=P1.P1_INPUTS, p2=P2.P2_LAST, data=message_data.serialize()) + + assert last_response.status == Errors.SW_MESSAGE_TOO_LONG + \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index 76b26f9..8a6b511 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,12 +2,10 @@ from secp256k1 import PublicKey -from application_client.kaspa_transaction import Transaction - ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() # Check if a signature of a given message is valid -def check_signature_validity(public_key: bytes, signature: bytes, sighash: bytes, transaction: Transaction) -> bool: +def check_signature_validity(public_key: bytes, signature: bytes, sighash: bytes) -> bool: pkey = PublicKey(public_key, True) return pkey.schnorr_verify(sighash, signature, None, True) From bb1d5ad73cae19d17f77db2e1fa92e0a7d716ecc Mon Sep 17 00:00:00 2001 From: coderofstuff <114628839+coderofstuff@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:00:40 -0600 Subject: [PATCH 4/9] Implement for Stax --- src/common/bip32.c | 95 -------------- src/common/bip32.h | 70 ---------- src/ui/nbgl_display_message.c | 122 ++++++++++++++++++ .../stax/test_sign_message_kanji/00000.png | Bin 0 -> 7884 bytes .../stax/test_sign_message_kanji/00001.png | Bin 0 -> 9114 bytes .../stax/test_sign_message_kanji/00002.png | Bin 0 -> 9406 bytes .../stax/test_sign_message_kanji/00003.png | Bin 0 -> 5084 bytes .../stax/test_sign_message_kanji/00004.png | Bin 0 -> 10839 bytes .../stax/test_sign_message_simple/00000.png | Bin 0 -> 7884 bytes .../stax/test_sign_message_simple/00001.png | Bin 0 -> 10706 bytes .../stax/test_sign_message_simple/00002.png | Bin 0 -> 9406 bytes .../stax/test_sign_message_simple/00003.png | Bin 0 -> 5084 bytes .../stax/test_sign_message_simple/00004.png | Bin 0 -> 10839 bytes 13 files changed, 122 insertions(+), 165 deletions(-) delete mode 100644 src/common/bip32.c delete mode 100644 src/common/bip32.h create mode 100644 src/ui/nbgl_display_message.c create mode 100644 tests/snapshots/stax/test_sign_message_kanji/00000.png create mode 100644 tests/snapshots/stax/test_sign_message_kanji/00001.png create mode 100644 tests/snapshots/stax/test_sign_message_kanji/00002.png create mode 100644 tests/snapshots/stax/test_sign_message_kanji/00003.png create mode 100644 tests/snapshots/stax/test_sign_message_kanji/00004.png create mode 100644 tests/snapshots/stax/test_sign_message_simple/00000.png create mode 100644 tests/snapshots/stax/test_sign_message_simple/00001.png create mode 100644 tests/snapshots/stax/test_sign_message_simple/00002.png create mode 100644 tests/snapshots/stax/test_sign_message_simple/00003.png create mode 100644 tests/snapshots/stax/test_sign_message_simple/00004.png diff --git a/src/common/bip32.c b/src/common/bip32.c deleted file mode 100644 index 18a4af7..0000000 --- a/src/common/bip32.c +++ /dev/null @@ -1,95 +0,0 @@ -/***************************************************************************** - * MIT License - * - * Copyright (c) 2023 coderofstuff - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - *****************************************************************************/ - -#include// snprintf -#include // memset, strlen -#include // size_t -#include // uint*_t -#include // bool - -#include "bip32.h" -#include "read.h" - -bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len) { - if (out_len == 0 || out_len > MAX_BIP32_PATH) { - return false; - } - - size_t offset = 0; - - for (size_t i = 0; i < out_len; i++) { - if (offset > in_len) { - return false; - } - out[i] = read_u32_be(in, offset); - offset += 4; - } - - return true; -} - -bool bip32_path_format(const uint32_t *bip32_path, - size_t bip32_path_len, - char *out, - size_t out_len) { - if (bip32_path_len == 0 || bip32_path_len > MAX_BIP32_PATH) { - return false; - } - - size_t offset = 0; - - for (uint16_t i = 0; i < bip32_path_len; i++) { - size_t written; - - snprintf(out + offset, out_len - offset, "%d", bip32_path[i] & 0x7FFFFFFFu); - written = strlen(out + offset); - if (written == 0 || written >= out_len - offset) { - memset(out, 0, out_len); - return false; - } - offset += written; - - if ((bip32_path[i] & 0x80000000u) != 0) { - snprintf(out + offset, out_len - offset, "'"); - written = strlen(out + offset); - if (written == 0 || written >= out_len - offset) { - memset(out, 0, out_len); - return false; - } - offset += written; - } - - if (i != bip32_path_len - 1) { - snprintf(out + offset, out_len - offset, "/"); - written = strlen(out + offset); - if (written == 0 || written >= out_len - offset) { - memset(out, 0, out_len); - return false; - } - offset += written; - } - } - - return true; -} diff --git a/src/common/bip32.h b/src/common/bip32.h deleted file mode 100644 index c306956..0000000 --- a/src/common/bip32.h +++ /dev/null @@ -1,70 +0,0 @@ -/***************************************************************************** - * MIT License - * - * Copyright (c) 2023 coderofstuff - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - *****************************************************************************/ -#pragma once - -#include // size_t -#include // uint*_t -#include // bool - -/** - * Maximum length of BIP32 path allowed. - */ -#define MAX_BIP32_PATH 5 - -/** - * Read BIP32 path from byte buffer. - * - * @param[in] in - * Pointer to input byte buffer. - * @param[in] in_len - * Length of input byte buffer. - * @param[out] out - * Pointer to output 32-bit integer buffer. - * @param[in] out_len - * Number of BIP32 paths read in the output buffer. - * - * @return true if success, false otherwise. - * - */ -bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len); - -/** - * Format BIP32 path as string. - * - * @param[in] bip32_path - * Pointer to 32-bit integer input buffer. - * @param[in] bip32_path_len - * Maximum number of BIP32 paths in the input buffer. - * @param[out] out string - * Pointer to output string. - * @param[in] out_len - * Length of the output string. - * - * @return true if success, false otherwise. - * - */ -bool bip32_path_format(const uint32_t *bip32_path, - size_t bip32_path_len, - char *out, - size_t out_len); diff --git a/src/ui/nbgl_display_message.c b/src/ui/nbgl_display_message.c new file mode 100644 index 0000000..c32a6c8 --- /dev/null +++ b/src/ui/nbgl_display_message.c @@ -0,0 +1,122 @@ +/***************************************************************************** + * MIT License + * + * Copyright (c) 2023 coderofstuff + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ +#ifdef HAVE_NBGL + +#pragma GCC diagnostic ignored "-Wformat-invalid-specifier" // snprintf +#pragma GCC diagnostic ignored "-Wformat-extra-args" // snprintf + +#include // bool +#include // memset + +#include "os.h" +#include "glyphs.h" +#include "nbgl_use_case.h" + +#include "display.h" +#include "constants.h" +#include "../globals.h" +#include "io.h" +#include "../sw.h" +#include "../address.h" +#include "action/validate.h" +#include "../types.h" +#include "../transaction/types.h" +#include "bip32.h" +#include "../common/format.h" +#include "../menu.h" + +static char g_message[MAX_MESSAGE_LEN]; +static char g_bip32_path[60]; + +static nbgl_layoutTagValue_t pairs[2]; +static nbgl_layoutTagValueList_t pairList; +static nbgl_pageInfoLongPress_t infoLongPress; + +static void confirm_message_rejection(void) { + // display a status page and go back to main + validate_message(false); + nbgl_useCaseStatus("Message signing\ncancelled", false, ui_menu_main); +} + +static void confirm_message_approval(void) { + // display a success status page and go back to main + validate_message(true); + nbgl_useCaseStatus("MESSAGE\nSIGNED", true, ui_menu_main); +} + +static void review_message_choice(bool confirm) { + if (confirm) { + confirm_message_approval(); + } else { + confirm_message_rejection(); + } +} + +static void continue_message_review(void) { + // Fill pairs + pairs[0].item = "BIP32 Path"; + pairs[0].value = g_bip32_path; + pairs[1].item = "Message"; + pairs[1].value = g_message; + + // Setup list + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 2; + pairList.pairs = pairs; + + // Info long press + infoLongPress.icon = &C_stax_app_kaspa_64px; + infoLongPress.text = "Sign message"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject message", review_message_choice); +} + +int ui_display_message() { + if (G_context.req_type != CONFIRM_MESSAGE || G_context.state != STATE_NONE) { + G_context.state = STATE_NONE; + return io_send_sw(SW_BAD_STATE); + } + + memset(g_bip32_path, 0, sizeof(g_bip32_path)); + if (!bip32_path_format(G_context.bip32_path, + G_context.bip32_path_len, + g_bip32_path, + sizeof(g_bip32_path))) { + return io_send_sw(SW_DISPLAY_BIP32_PATH_FAIL); + } + + memset(g_message, 0, sizeof(g_message)); + snprintf(g_message, sizeof(g_message), "%.*s", G_context.msg_info.message_len, G_context.msg_info.message); + + nbgl_useCaseReviewStart(&C_stax_app_kaspa_64px, + "Sign Message", + NULL, + "Cancel", + continue_message_review, + confirm_message_rejection); + return 0; +} + +#endif diff --git a/tests/snapshots/stax/test_sign_message_kanji/00000.png b/tests/snapshots/stax/test_sign_message_kanji/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..ef8c0f1575e871ea832d4dd46cf0af81c2a00c36 GIT binary patch literal 7884 zcmeHMdpy%?{O?pJl<27F;!;OZ*`ZUebL%EWNRDe5a*5iIxopf%DNd;*<+8a&EF+1S zHrvjUq{R-yVrC&`hRLk4+4lR^@Av2L&)@I&`u%bKdba2JzMkj#JkRIz{=7f$=k|Gb z=gk{;ZCtZv&E~UbPF+~D=BEd1)~qd8T?_PpJ=$v5tkKUqd+PWfDaCV~G5_&T%7w9y z3D9*zT1WQ3)hg5qQ#-#l|Ihu$facLLm&5xDwWbj9>3L|&W9!4;z8IBclOuDFZCOci zw|;9N5EJou_0LQhS~B|6p9yN(TQWm_3RV5L`S%F^^@9H<5Z*ZgaZ(Y3sje4rV7{!x z{0KgE9imjn$umD9KO(Q|DIT9Q>PpE60&6(u7oDQ+$iyIn-} pYBhDzUo+6pKbLqvZ=GPlZw; b#wscI@?2 zN3DK}Io4$D)Ubod`h<^d4zSt4*h?FCggLZ)5VgsKRjC#J>dT z@H;PWvi=gfu^y|zNeyG&g4gY zw>3x)*$Se7= ^dKl?9Y5vZU6iTD+oP31)Mc+ceFst7R;)hYItvn?XD`@3Zh>l z2SaqqT?Z1O51fAW3uiDGIzf&M5qwv(7-?VP?hynfus53)oik$1awk*Y`|kG(*SQB- zw1b!y@Gm1hx6vmT`_BvF!r`3Ix{tXa#g;lDtZC>KG*uz4#qTPv_y&wlht}0fM6tyq znQ`m0H$93#z;kIePX@>87G~OmBX5P@=m_;X{WNE1DW-&s_`@W%GZbV9Izk)&m9Ku- z(3rhjy&2eTD5rk8(0x1)tg5Z!ias-5{KSXdl9Ah+>JXFA8}h*HYy*W<%e%}|Z`QzN z`I;GY-)-0e8a;Xyk?^b;IHhG@Gc%-B-hY1P>{l`nBVJgDQ!Mc0lJ|uDeq4^5hI%s; zn;F0h7tBmn?(rM>7}E@nYJMcT7;iz7&cm#ao6xU0lu;2D290Ae?w=(bPR#RTQtKrf zmfh-mjp8oVY>5jFW2598qt~=zLQu)VVy$|XK%S1q`vA_DnT^xMJ2$jm@Xt3hGc#OQ zJ9IT^Dg22qL*?g9!2I>Kf5X8Xp#(Q71pHA`Q |2;CGHBNDSNuUw( zI0Dh0(7y$fF(2-q@9mwacp?GOeQF1=Xc*6@?1Fh)#ig4a%D~}UUjy_?PgX}`Or|n2 zeZM<+p8MN$G2}06c*+{Vos4;)vq) nkL?&{WSSiV0)fP~YNq{EeN&zD&CGe9?-z!Pr-qugv#xwokbw~+ zVIBIu7U(|dQWGA3!Q}q6e@gQDp3Y8nHobGhdF35<{{{5!Y0}6SRC_|9+L4||53JHY zj{FmE(zDmE4eC&)9EhwE72vnW9@+l|>g2gvHQ^syO5G;C+hEPQ_IgApA%I0u(ut7j z>w%{lT90{dlgZP`!Bpdm2fd>;raz{A@AdQc1};e= y4G2y}ev2u64vrU%^bVUO+rNJTmAaJBDa=fm;^1OKbESChp`7i)!rPesAyJ3h0y9 zY|0q2OZ5OK55!n}N+JgrMEK`VBFTz!4vNqnna~5|jd_oKV-tm1rNA aQByXgqHLhm;>P0J|W2HWfryPdCV6($6FWQ z%oz)MlSlOT@XyDMZU)gC@%T#TRw7{=Q+C_giaVEt?FPKQ;=rAXLU$bhvM=#4y|F92 zuPk*wi3Ep4_*b*f6~J|Tye$^rP1^Lqaz@@)HOH`%nuhulIW5pqUbHd8<{4q=DiK+Y zE63xpu@5QN YH-BJC2#_Mnnlb)IHqPG>vmO?)N{Idw5Sxzg?Yi%coD)nC7IZ)TfS* zu55l{=x5x`6V3DZ)5Evo4Qazdi6PZf#$@D%l fcw%*v&C_vfO_zE{aikr z$Q1&C4+##IR$1RYfxZRx_U3d0=nLa+xES>Etn&S(fU^ZIFS$Y)3ANnUD!mPy)SEv; z?(sDni3M-3y|yz}!QB`@U)NLkX$~GM-spp*ski7+Co4D5X7q)<{&8rF*>tLbnvb%( zvz->Lw4#;Y4f;jHgv$vk<;h8;u)KOb=XLS!JIzqz^@_~Hd;DUp( R64SOmuuaH$qz&sL|pF(%08-1YLaYBkzq+l9gW# z2jx!sIPJF*kFZ{d)9)qjOkVK}Q6b@?jq}cWdr!D-t^f38+5yM7&PqhVIqy `d?OGD#<1fVkWy!7KWB8R0a6t7#!I-a6w_0D&H9ZLHU5um?7g60RnWgr`iM3n znx;EEe+CveAv=tyXr;Rfy^|TYe6QOS{lhsKE|M!>5T&C=0wpMT3GtcHcD@0tOv*V> z5)id8aB*kxk*icL06{bfE*jGvDOz4$2GYOvnb+{V7AU|3)8*3Ufae<^hwg->-IV?0 zQJnDX-r@ae1$;Qh=(c}8)DmmONF9)G&{sZb6Xsm?_HG&iLfhQw5WNw*|6bYJ*?*Eq zC=od8dLeM(D|Xv)-zhB%brLuXvA`D{m)v0_XU0BFoQb9(hw}zv9_y!bpO>}@D0|`S z1+9w0u+;}{lFGCmHFYVqESbdVt=#Lz9I^6?UM>7pytMR+10zj@@pO0Z0{IY;R(-Mt zKG%(D=ipHN!Q4%>x|#+rPwp||bYg+a0cl5R?BI&ta%Og)m|8l5dj++XRhqCo 9jD3gu~&EY7&eyG#y97(S0XY>v!SYJ(7Bi
ExeIyl1gg8a}9KwfT&RCy%V5EN$fI_iXrFN>_UHL7mSda zl2X_mDJOjCE1U*s1>jT81n}zPxDKOOu$VtzTh>}j-Kz^=rI+*tqY %EeI6W}e|Nzi2wv>s+8$r=jO>Jq*b;Z(M^~7*I@3xOR~YhD|K_ zkd) K zt#%@6>kr&Xf2@%#BpT1#bGJy>3!vYbvugz&??qpIm+`ocCE&xs0ME`liwS#5cRbrb z+; -Y?=bkqut&~Bm+?%+FwHgkUy1Yb+ac&j70Hy zJ1EbWe!9OTgx9~P!krz_igU3aOOzB@RKA1wAtf=ogK2ZI$~%P5H8e({7)DTbQk0^t z>qf@WvhVH~?r-E*)|+GC*ym_x`!Z!*VM*WR5WqACDl9HqY2O@Bp`34ZVw7bW&wItG z3o^uxl%qxGHW)|p4GQc<#Xf239wSj9E-5P1botB+J3HxEDeVN*VV%bar$}q`c(V4w z)0Ik6_>rRJC(4_4@ySs)6Rn`^YE8tF bMrlIK3P#CloQOm+p3t>gyM>gdwa(!RI6l@%NE z;fIYNuCB{dgjcc`po(PeZuzb=jG*O!D&sv+ v^IxUlrCA_L=&gUtGk8Hf6}+uJ)j;@aDtJRbop zJff7x|2bhs$}jgLUdxY23@RgDGiHbswB}cHpZ(ZOrq{0VL *)iQZ%O8_RWB! zx+Au`&uosC`@;MHU^XXZA9)yCZPRuQva2z%6Q}9?UHdqQk)LE?9f6Bl)-}Foy8(*} z01gXQe65%K^xy)LtUvM@MsQWx$W1%%Oh$ZPEaq?^p!$$F^~z7Lw*#vkyc701u!kq2 zVKZx-ommNPMtO?agKqYXj?NzSdHG>aUtdrz!m9H}VY`Ryyh9pR(i}V{O8SRP<|F EAwj1 zr*}{%bGvo2JFdMx2O%y6-=`@5(K9m`DvKwOhjLXOo0^-n*?7H?)e6A(yui*cl462= zwmD(?0|_Z%U%DINW6LHsC~^1W2w__R ?55D|ov zN3rLOl2^(HB>Vi5^I|Og)|-#_@7O2Lq)I0o_#1LGw)Wkc(BuDVO9>N{NEXkk_l?XY z{8ggdr3 ^l z26u5c;yh=eh^ Z?5bEcP-tSacE2GskMg=w9HLRTmotVLo !I!Q>FRuaRGq3qTq*VXFEwMW^(%bkUi#WXTI%wo@K`qqgxS}s&6`zROJtQay3BT zSWzY5{zCmf{PX21kMmhJL2tPWm&n1XITI92H9!4>N6-^x{D`1u@Ng^5tMjfy`_QiX zql@MB`S-(15Ka8!D6-eS^h4D*CCMq7)Of>aTc!Ss PVC7i0{7)&IA4XDxEI>L{ QoBQ0% #<}ER7OM0Un0|<7Y+(4l?Fancr9)#PC`#UPqWmyc&n%Im)Ai6|$K@LB- zq~|t39r4cxK&&II7uC#cfr6*Jw{?v9YFfAfe@b~yo_8Tl^@5|ZmYk^~+rNEV05wLG zNTyJd7~&+7J+fr}A4b^AX$U3Il-@Y2eDx^8rZso|Gb_yjjX>1k@o0IaWLYW&w+8&s zf{U-@N7yXio=XCxiEOIvDMLTVNZ89WFL-tgkDw?L$5cDp+ KRc;L^qqw9HhcbitKSNL|H^d!@oFaD|FSZ#d+!08?Vg!N@!*eM#b~ca} zNt|yJ7Sm^ZYby@?Pz{6+G4kX#?&f(wG!#mJx#Y5$;X{7hJ0Ym}pok4jay+2MIh6T& zTNE-AvmOxRy&Qi9zT#NO3Jik%wmB&yNp$19uFnsTXA&O*cBH#e_5reME9~C1(}zY~ zT{6 |%x6&Jr7xb4WM z8UdK>xvV%17u(g1eSY9y&;q {gvi`D)iIW zq2Tk6EnhEP-UTVlnMy+iWdd?7u%OZv%2oh6+eH=b^F3|_;Yz)ZS5R4xS^!}qykU3k zK#V0ev7K0#eCA4AH;4`h1Aqa{nKX(XeHSG|5v{&xR|d#Se0UrzL$NUvcAoiLFQAXg zb9=@bGuyp?<~)|^lu|WnNevgXnmI68c2nQLpdeAw3dl|&C5Iq)Lr^+~CM=Piqxd?O zHb4vx)T G#*8)NTrziZ&K;7 zuKJ;|t?O$Y+}7FA_0zq_#4Gyg*1yvmSJEh4Aqk)nY2@JK{bU$tr{^kA^<_D3O@s0; z18^Bzy1Ga26`0q*$+vas>SlDbmFlU6KM$W%yMB26Ce@R`&;Q+KLb>jU`SvTS%910% QEA^VQPVT4bPlVj~578&LjQ{`u literal 0 HcmV?d00001 diff --git a/tests/snapshots/stax/test_sign_message_kanji/00001.png b/tests/snapshots/stax/test_sign_message_kanji/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..ea783c27f478b70ba41fbc8002b66fed8f65ac68 GIT binary patch literal 9114 zcmeI2do zb#(JJ`tVJ+@a&Oiaf1>XqwaVmqFRiHX12BMy9W-m$(+OibO+_R7T@h}UzR(N~3b z>>9uCW|88v2eJ=L9Y9=aJaBr~zKfT;4`}R?x_If?ft&*y!h34YzOdgw2W#BABDQm? zrL}+0bg_!3rnlYBpFAiQ8zY+M91(l?`!0F0qyHPHsGclthZ1hA@PCc<1%@2DqIAaJ z=+Ebq>*TWIkL3TG*{OaUKV D(cn zi%SH=1XNHQlv6CHa}A>bW{mF5BVlukr%8J0T+{R=bE<1Ck5pPZK!{ig&us|u;i&p~ zfs)6L>PR+FAMd2!y0wOMaS6)F^q;1>GgksfwUfPry}jPPoUS;XT!`gGlJg{ a0ciOu&K+5jL%;qT)7O*ME zI5(|8!XuL<6?(V9>sLB1T4$C7r})^QbIuXAOG|T#tDBeA-vk|lR=IyMP0ua 4*YJburGdD68@6bKQk!RjKDlA(m0RqL!X)wglBl61atY6g1 zy)YV^!XcxS*M?xN?~&dN`=LVZ@)8zd&g)oJBOTbp`|A1tp`tX{N17gLQS0!m#z &Y(I6`>bBb=yp)GTL@#=l ze5rMCc5yMzqQKZsouac`S36OpGPto-+0tN5#gr6NPwYgcA`ORUCVGbSuBRHUX_eNv zici{3MCbdn!sN5N602do{m*BN>#qblYiDPPCB$%Cnrsq1f0)ZsTS7lyYCeUNWpefQ zgb>+B=dzxW>RjD7l3;&fc|A&pcBCR9H_sVtT#P?Odd xQ-D;7Bs0 z^5A6t@Q6v+tdopj46l!^+y6#1R`bOf?m6vTwo4n?Zw?Ys^(>G@1Cf56N2&x4hpmEg zI(g?=ah>w%nW^ypT47%$rfj_CE<$&$DxpxjpBKR}bgiE1+V!O8VwBrbU$DQkyTWUj z^wk(U>9WnrsV@AYU^8ys7DTb%T*5A!WuE1?-j2$s?qDy++@YoI@4&VOwv;ocRV^3< zSf)QS;4e7QD+?N~v}2OFh}inmcSJ9|q0ieccfawOr{V6~UVUnaN)vC*SeY2pA7p3` zaIhddu6XXSKqZJj+$S#mKc1;-n@`52_>%;zaQCiD^}N%14G;_2OFkRwhqq-LzphQM z=cbUw<#gsr{9&{xtx($*L!UV7Sq$@9EjwqzTVL9%AE8LbE}>)Yjk*1i$>BqiPKMc( zmfpAgoT*`(KRLXhuycEF8+VbXiL^9YTVJnR8xtJ0!`!)b0f_4U-w(G0#t(HJbYRZd z$oq_+_Suwwy;e`zo+H;a(I@Wf{=mA3Ij$5oo>$GFtZrf(_j;R;%gz5WAAWviJKUlm zsh|_eTmSS}%{;ZBpnx%#W8P)MTyH1p-+8P@$J#QMhvp&XQatTXm)6q3On!%NC1p z7mAVhX5M7a%dOd_4CU6!N_|VCUPE2_4}|%KC0WJF$s>~vU&UTNh-+d@k5`sxCks*N z^La?>XCQNTfNx%`+*95&oF^+=zR(hi8si+ds=PL@J{5Ye7Nh2i5cq9vZ2WliA $ u1?lPTudx&^MnO@GCEE7sh QB2ow1lUGfjZQ9B z>O1!8*pd8!ruzk+n*+<#+Do49t9qy |qm^|@i*)i?(=#}3 zhRn(_eSO5Hc$7whG)PaOp;yk!9m1>=^wgZ`nbfripW_*(b9?-7PL>S|V7U%uba8J@ zi}3wmsVa* e`r2E>1Az;P>kDO9K9j#BE5rfgo;xO^>ezv1R-(gVcFszPl$ z#@}|NhcRK~C39R;MuzO_yaaV$7y~a}R{HB|ykch|QZUBs4-z3*U$@n!2mF?k?iVYm z*kV$5_t(#LMl$!#F@RsnCXhLu6cp^fdH%RZ{KV?_fWhX4k+I7VCTsdnjW5fqmZz>M zTU%DiQ(^v##QC8k+w*7 77DyxEWjw+mAd^6$Y4rVCtm)u-U{q#dpf!Z2JP&^se-0T+{>~;Ge`;zoKvAo$O zmSG2yVxhc$`^H6c_K*09-Sm{g?8QZ7*dlcBiQ-Q8j6LS5DUM*ztvPbk Yn z*cjtq9kGPH4Duh HQIYOyUlCYcAYW=jeYm4H}-w2 z6(+d67CB$@c`qFouGT}sz8YLOX1_aAbf$ubM0&oV7lY{Kyw#C0x$N$$C%H*I9d6l* zX@cSY#(KnjLLzjl($>>mf62|9ktg4O60T}ZeR)q8KWNg?jO26p6gYN)%jGgFeW@tn z53MJHFzHb*5UUBu1sfzEHi4$|5bFy#N6wyQd9} DN`b zHMRoo5>Wxn_y{xGIQqa@LZMs<9>Y?{P`5YBvyWy3zb4kE7{>^%K3O2&6(u&ES~$Hy z)Ld5m%!<+Q88NH )>3ZPyCMX9j5=PZ{ zR=Ab{VTZ=8Eh6^So3Ca{$=G7nWExkHFwW(rd8%kbh5a6`a`{Pt$iSDsvt8v}#vaR5 zAkT5RG{|1%{Q)oikLFtdzKGZPn3#?JfHZYe2Q#>fpC@Ntq7c$4qXMhJ7{#v1rA36Y z4+YFv4nO~RxN%`fN`^frxL=pr>2{S7P8}}jy4yR`=W{M`1&Q?=R!VGy1gNzFiSc}1 zdK-5-Jm*Ys-5Ps!B>lBR34NkTf1 sxjoj)lJ-r+Q~sILXHfUEp4qg6 z#vn E!M!GmVTJY=n==icdXHMm4VX|4pETU)jt|mB^go@)UFQQk$`Z`>J|kJ zo>+Y#CU$`D0p#pg-oxRqcQpQ0vddMu4Z;N9$uz1gPQvCC-&6T21!Eh7AK)qS?q5q) zdN=TxZ3_oCqp&EkYl!x)SWVkrL$HfW(Ie-`p#fQ%jN=HF2WTy|UACCRR%f 8XAOGpta4x{l4{eE^SjLnZUqu$A%49bBU=bEOkZVKx;JHi;w;$_;&D@YCj z#*CoJHI$ZWCu1~@8s8yd*&)V+7xo_sFq2%cMYlPM@6CFA9}lvTsGm5$|KV?4Gl#6v)*rUsuF^(t(&mbolDfx2uEs1o>Y0IL zD&AG-(s6!wcF8{&d~9Cg)@Z~^q!$MbK+in>xz=%CIiPR7#uJC;5{N@%M)7iXM0Q2m z3$JLVV0wiro+6`vaNzWMpg*7V=acp7#y|2E-FphwWsH~MOwOtEzNhH8=_?OF;j{Ty zyrLO~xg7yy&X7wv77A5 iLO*P-Iukpodr|aS;zMEVb3Q2@MhR`Rl zr&7Vt =zK3Mh{I=f5WfR?JXKiTWm=#8}=d( `=xe^63i z^(FopgT?}Co3@=3v3j!dpo06?2r=QU2uQ~6O#gm35x=|W<;Qb;{7)zNwsG%$x+tF; zGVS 9Dh=HgD>P##8bag$r28XU?UB{ Y+bKNAR__n0F@H-y_?e za!tFIfI5h5q!k+_mVjuHvzhazoEwDu6WFkvVt)TE6Nh0lKwMFBJH|HxJu*AA1LQ7p zWZ14CI&R4PIy%qF>D#w@@XINQ Xi&etK7 2#6H98-3U#<2E zo82fPyY;k=FbswHdC-xz<5m3IU7l5vH|wHhjb|es4E^#@@}PTEZtcT8BOlgIxcuu0 z>0eD`|6cd+cmD4f_;(EaZ;XNM=qL%)i_%gOmh1uQS~5-d{k?$d`cqJxNG=qjM10as z+g<$_KxD850o6Z|Bl=<-GN%~06>Krg(2Z#8s8TF+^Lt>y _fNSc>>qvUmP v`xm>W`}j1x~LkOe~GV2{ARk%0r${IQ{|lE#iW_%fJW;TbrWG^R!BJ)&z_rY zJ{^=Z6k_L2{ZbKJ<0+#6;7cNVeG-k)h|ZS`7z o4Z@hoZZCGwcYYu{6`BOTQOH1MW$JE+B-Spe^QzFBLC>_^t zKT$0S3;B|37SXXBJnwJjzf-jBz)D%_e=7B{Y6u`HCQTEvv`Qv)vzMP1>rwOuWY1Ks z#sd(}%CUHMq81Wkq1mfeeLjc{3Rxdhw=dKYvOW9u#4!>}@uIt#DUZLzoL(-I+Bq zhld6~IGMP`=(9igJXi@c{X1fA-YoNyG!KT+V9l@kdkFN7d8(#$PPXH|&R_6gs&5z| z&t`yIsv0mN67K_Rg7CCOR|1)m lNgT&tw=1!Vclu#qGn?g%J-)& zCb>XGFIWl}xcYoa`>-+=g!mGAtHgWhDE;R{YQS3xJW~Fi@*!W?Zr8(+8J#u8=^YUt zrJil`;!Q4*=r1TOoRIrwBDKMGirLC>uZj+RyqmRF*2V*0A(IVI-S&Xnx?X3xHDh!C zNgsJD194^S`urY)?i?p$$KFous3p(OEVw-&{yZzjOa(SR?YNKY!<3vWei{4$IFZsa z=lW@idS-dVmCyNLGaijHRVHD*Dw*(J0BlfnG~gwPp|^gL&xVO=WlJU9a$>dC-f!yx z21)~j4qV8XGn_F9ORj(Kl2bgIk-~x#`R{&s1sfMIJb%NUGFCe~zNMxIr`$ka-{yMN zMU|-f?M&I;Du-T9BRB`;Y?%2ocnI%igDn8zd2 (J|QgVcCI-;X-vMreSvB{&o$kjapV4s z#MB}2fA*MyYRf{!w{E2RNmWVq)RW~KH+!1Ik3VYszdyMJ+R3G*$S5PR5dykUyM1Yk z( d2zi5F;1+oc2IZ;e;1Qk65iB~K9* ?bwSNavQorL&XtMs*CqOPj0n z(NCQ--)llVGVPf>%SbY(?H>b9jG0M%$O`n+1=(U4(|3T&2WT?^{T|({zTk*DZ$88? zr`RA<9Hk)+jYcu`!3+=`02KgmWdo!xJ#rtLFh_#>_<~|Rpkry7Kr&w&etJZ(Hx o2-+EVR5EvQ?Qs%7RL$~jG5bU_7E_cW;rjZk$vRQw zx4 P2$m>@>YKS$%SG1t@ma_;jMc6 z$h7Eu*)j$95bE&iSwk>`F>N@=Uyl`B23lnFiLuBEqVp*E5o=Gm|24@>vkO~A7kYYh z=)FgjWXc=;sk=+O=kM@gg_8j{k64`vxdOC0hN7X!rw1CO<>ro26XRR9H0Guyx~KeX zF?S5BnxZ5#j#p)#Ip_`mDLy{NqJYEGYx!YrS >=n0K61y z5lZxO>m3qyb#(!B`ueG^CBVLcu_>2dHWSM6dRh zvMuIY&yvzvel}2dhdLj&+7xatEippRwA`C5os@iLbTuYPZzJ^PW2!o0SA0z>G(G|_ z#fBwf)THNoIp!2I?K<5FZaS+ZwUb!O2)xxuK{ATl;{Tkdh>CU^YrD9nSL+EQdQtCq zWcquM97AtbFRgn6QG)aR+BX |(nz%n*(Jt{? z7=<{SpwIKXKs%uUm0x$sIp|KYe$IZaCW+elpp{Sd1gIy-ynHLX8k!hCA$Xtl(ATG^ zPo-~x^XXBo-P>%(8Xl;K#MT;69z%Vkm_CPO+gJB9YRHWq|ihB;>{&0mUjL7gk04 z!Pwi|LNi+o;8??sr9n&6_?A>oX18G)L|ZveZE`=PvP{Y9PxQ@sPM%KN&bNEYC+a|d z@dhkd{W3>&w?1#J mIhO*K`A`~+XN^ZU4A1Kt?o6?A`si6< z9a=~VP;8fzeC}mYkk;s#Ez> Rz2S$%=r*6JVhZiO7^^ z9O4H&nDKW6$1p8L394Dwv-s>5knnvneire|8Nh&gFmm6@NBizviu@Tk`JXp_5v&h| z7;hthVjhm0pg0|BD3>lT4nk_mCOs+}hj{rmr*epQs?lyP{(1T)?+vQ5?W>=%R9jU@ zBy5b4^6q~1`BVHRN805OAb}YX(A$nf*r_%6UlwJ5NTJk%^Cf ztb%cFM{v!CaFaOPyST{|Aa9;ob+fFP-CycsG>8M#kcfw*sQ%}+G?yl;YhP*ied{Pt zHeWZ)Aj+ZSxn*8ka+=RsS#N<(h^lMoWv9b0{m-2@i7T}8UG>)7H!Pu`4F98V;{Kv< zeOa~_m3(NX%k1g4nG@EejiSAm*+WneUOv2?K5}qZ|JhUG^n%5a0$DkO6TOBJOS#sJ z6Je~%s#pA0NBIX2iZ|8ckJL&7Crd8QB+7heN}7WY@owgd9V#vkqL(##KBZ7GZ(8-| zUg1&&ooX(N1+V-cHPgzH0HFl$R)X9e!3E{ycTM)hNX2CAOy$-x2eTC+DDM$H_liW| zlv2I*u!Q)Q f}d~Lg3D0Vr#knr}8DkmYx=kb{)z_ zcs}ZqS+1Wo^b(xeccNwX_XNv_p8#w#hiO4k)hKuF%`;REq?aWOH#KXqFWd`n#a)P3 zsPY+B-ZS`EBIU7iGJw?F++seA5B&+%IZ=|I)VR^V<&jSNg^9>74)PXJcq9;g%#=@p z<5{&OfoC3#u1p<5K>_BDLw`fG$LK5(S#y!hV)bvDZsNQZ#c)wSMx%Pi{oa|CkotON zRSjE7_=_|`law%g?A(fvK%*D9NknS8{{s^Ie^NMY%hriG6fcmj-v<73BW7#ma0Pqm H<{$q7Rc`{1 literal 0 HcmV?d00001 diff --git a/tests/snapshots/stax/test_sign_message_kanji/00002.png b/tests/snapshots/stax/test_sign_message_kanji/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..c428ef3e4d8837bb678787564b274d523cc35969 GIT binary patch literal 9406 zcmeI2XH-+&w)aC5kSY(-1ymG)(4 n0*Zo4 zlNtm z5D3JgcjvYV2t@T91fqV$Kn=90-0P|Zfkfi +866w3eV(mAZfu= z2EC_WS_Y3fUeStC=|ATvqb*_UxEgZY#rX8gy2?IVbG>oz<6m-&7mv%__-JFOA5T%D zOQb-c6bOupJ+7Bp_)HAlO=fDQ$2_!LAXx@5$e!IC^g$R2daCvB??7a&D=8^a>bdl| zVQ0E`+zsMPmA49yff!MRju%o78r4#cj*GeJww)HvnU|e^4Czus-jM(qgn7YUn1Z-= z-}hds2Z8a>7>Fg6`Md#jatrnL*=R1@5AMqI+Mmxb_h1A%sceG_x+dQ%)JYpjfP~r| zS!#DAJ2!L1cj#e_d|W|MpoTVc7JO7sG~C&EurO6S5d!`u;_1j$VhkFzd^>? %?;Ii;dZC+y}u);Uf=*6Fqy z46*&3t-y~X-gV!WBR~FBWKZmUPg}ei#Cm ~P<=KHc*QV0QD+irHVtn)3rYu|458*4?k7Ib!zah7oqWdKsSW zb30QdtG4HQxPUgvaoLBMR)l-%vwGlODr%-=y$r`8SFx4$_nx%VKJ74?VN%z%BtfZu zL&dyQ#y?T0^8T}HbFT-$-_TC WEwSaWg-^}}t6{+X>b23~ zE};)d;qw;;UaK=eGTGhrYmqfIXrn+Zvu7C;r5hbg&(&kZ0NiKTala}$wPTDC3Y`i@ zVTt6_HXD?sJc^vuFDy^ZB+d}Klew`zcV;+RRl?M{IrUY(x?|Q>uq!QBR>917@j(-E z9X^I>Np);B2o_e{{#`(UJ=G}WTKPkE+9lLNwuxSb+MkZ1@-36HaJo1?u45N>DdtLO z1Z#dxjdKfqwaq&n*kbhV?ak)si>3A07=>&D87O_>B98KLC0tC^+P#RBx$QD=3Bxk* z>dI3)VE*2S*@WFkc7Sq72ex#4Xq=RrVH*1M>P5i-Yck}>f^Air&T|qeV=6EJF$OD; zDv$kS3S#n}0CtVQb3tBItjjf)ztL}}%Fk!ZWH-+PGsKo&+ajZk5VB+g{5RbR`EDFB z(9BNp5Ghl_yI5VBCQBl%yEz6*4w2ze+h+_eB9S0WkTs@!;fdKBJ%;~(9P@wo+})Q2OOC{9TRj!Cfbs4bA%P;5f1~#NY#QT=@-%mu-=$_Q~m0i z8o*F@LOs>Cl+*OCQG7tnV jEYzHtXX!|q#0sI1f>G>jBW32TB(ncz=Jh4{?=wn;z1V-HLJ3I!Eq$Dh_EBe z;?;Gp4c(zQEp}$Bx1Mqr@tugM*Wke~O_Pvueot^i2~?j>Ya1DEgK4!XM|ceQ?VcH! z)NTgLIZX_lEP4!^Lxj{Rse%u>s)+?&`&2O|`sHrnx@;uB5NmS&RGhG@<|9ssOVe7& z2elxTqRrO3+v@|W0Z7vFVV>k|W5*tZ-TqN{cd!P~3)jzI6dugz(>~X?G#yos-|L(d z_Y)vriQq|qI~9@<>R~rw?B?2~*RBgy4=!I1+KcHGPqP1RFy$S^N^5FcUr#zunTeTd z*TRu~+8^OO#L8zy=Lo_0T6 U&07=^NHXDOEf ze_J+1Pd#{?vk%uaoG85}m7A>b!V04xs$&aVmwbzeP$`ni9bEQzo4kJL&c%|-Qt*Dp z4jZLt_#lEikTB$2^3sln3*v{uA~3Z@oP_&G>B=)&u;6!59sQParj4izd##*BoDk!g zkKQGG&A8TY^t3Fr(=@3h(~|Rk0_o4<(7jEgjr*c=8bz~AGugs`ap75gIx@T8>D*|* z)LLf*i|7CKcFZwtX{K%pkw0rolS%|NzYG3Q_+B~9LFf-}ZQkY@^HbT+%6ry@@S9(6 z!qlCkXFU;k9b@^gaTDC;D-mytlnajPRoTspUb~I7qf?b;_yuO-M_dT1H &hw z33Qx~wA@#bO{ kkV?*!*BMhb6gS%C zF)F|=FH>j7iG>EL6#Y_D%552LcCzqRECgQfS0?WniG9fvB)7#8{^5pI$bv7j98ouS zS#7hZ^_!ge+B>R4q73t1H K1#eS>RdN&vC@gCtq~s_7mUPoTz-ISbzx=T zW2keB#F61Afhy}`@~fn8k+dNwY~G8lecfP~9VUpPG0%oM0;6W;2>Xk__B;3>F9hF| z9jBU&FM8-aY0Y%)7cV&e(O8M&>3HzZ5qZ`%GMAL?cx)QXP>F+RyzdZ~&3&mbzEu}v z&dP~uKi`^~FHN4OyEhkn>8^D-0#67=%@Hz0+|- c*%Dn<*oH%^jW>q#8oEx1R@>yJM=U$h$Lk_aeMf0|lZ^Ywf}p43TA z%|4~u2#9GTs{yMD&t+?80z6+DRmOOTwYe;`ma{T&8os9rvKBg){XF%#IBs{*nUL;j zOMJUlSj8WrA=P9Er+KcIithN_UuTc0SDC1h3oH)9*!bOylBBN9J-Xr1elDd6OyKt( z@cb5em?`2G9Xr_ca+14EF>NU?YHPnM0q{D&PWvj) +m1{FtQjwbDB*b*eGe%;Wod6K0nhgi-1?)J7A^wy&9j%uSEmFG-W;lrZ{XYd`XbP z?Bm<7Tt|u46=7k%>7(YMsiBrNAL;5+XRkRErOoekg0{a0$E;jnZ#ieqCXX)NOSz)O z9vFOHY5Kz3T%@0 8IfzQW%Q infJ$2C*(Iq7GH`*tJ5$COh z@(BL<(lnH2p;h6P-T^NPJ0ci46BcmKp-xdFErhV-$`O^2&tO%1EStV2VG+4y77sL5 z*}+Y$F^3v#6z|*c#+OFVr+!#dgio1=JR{Zr1n2ng65v12TK;zp|4vDGe673*ObL_) zeQ(s%uNG#|ux18@rd+-F??h&&+7J|)4G^-#!FeKCDa%oemTOzRUu1GyTw+w3nu*;< z$4GsCQYACFmzwsGOpQu=SKG`bB hn-FzfufR4#;TgDe2cv3T&pL^|A-u-&}Z%czo-ZpD! zF(Xm~UEXt-W$%w)xcE0kEFmjQNf3fi+J7@Ijm^Otv)Sa}q`%=~ $0feK3Xq5Rd%4u1Uw&!cda>GZ+($lg;ud^UBM!Gz0eHvO JaCsd;(+r*f&$f)ujH@*H*?Eb5L5~=y;D74Hi+}227z p> zJ5;g4U-_A1Vf`Cd%Zw@~E-hQc%@Le?mfGhC6Ib3Jp<{)vzzgZv_%um-b!wx{;0O9Q z(waxUB%iwLyrfm7DS@52uZgF>WdZ=<_Q14Cx!VRnSi;;2qB_Cm4SN_$mEF- 1?Vv`?_gVpl z>i7oZNstQ<&JZ!4j)#z*ntUN2jd)}w6Af~lD#U=v@SQ77al@N+KqA5I802CFg(84W z^GJIZb)T4JJ*hUjhsJlyr03Dc3FrB}<|1P5RJF#hN7wxkw_9DW1cZ4{Kz5=TV{+-5 zTJo;byB#w|{@ew=AIectwh4P)!$H{n=>#?doa|T$3+drfUf%bK^pKVBT^_AH?|Ktg zhZi0o%hTq<3!em8`zRKoi#OFql?qdN`xQ)1<%1E1n=fr(X|g`G{QwQX1av+0G|^BY zH%|1*ej}1E8???5RC0$-l}kF9ae*jR BolhWt#)E*f+u>61~_*K{a-!L|;AOm E;dFX!uF7v+a#J-UT{+typp$@Jv=EiC+$LS>LN7Xcj07srRZv3f6oX0vvPlu zn(G_&%%RzCw`W9ChNI@K3i rP*dO z EaO$uKEzu^GR#y6_1umh>Y~MM3`&G+R&yiS-t3eXoJWn47w& 6z@L+y;CihyBcl+F4xO$P17W<6E#%m~ByG+xCFD=m=EbT1u71@?9 zuy1f78U;GiWS)j|L}4)L>weOW8@7$oikP4`*YqbICgx&C>s>baVJ=oUkg?pwOF4T9 z9s%&`-QyNZUl$CIc`*HUZ>V=bM*8}EYwr5oN}fdEgKm4obk_IqpgY`wkeF} z$0g2w6KVG@ua`w;1xfA2f7%I^k4|5A2o@M{j6om0?H-b#HjwO=1~`CC&9L)CM{k)* zfpuH(m0Iv%;*@;d%cA?^6Z-Xv$f5f{N Vn%6`ejCh z832vR2?=JgKS*PT=l76U&o>WWOS+rhs(#_i!HccbG5+wU77igR1OuT6X|+++A|tng z4MYfvr>M5Y2gFkxH;LA4^eKzLU4X1~uc7F_=khCW+B$!ZYnwe4w4{U)xh{3Q!IC^4 zFXC@a4n_?#)Q$MJRp#b5O4B5uI%l8VQ0q=9(kE!^!n#L#3l9`>)a@m_INT4nPWFX8 z kdlj_Qb#FQB-eji@4%tib@Ava8Ar-NVS5vOvOz(5AOgX-og5OiA7B#r{ z`+0>MPXd}qK{qJ &!Kqgl zeK2`xSV2=~&4VEbBQs^%bWwsjnQz+1<(@&qF;;XkPdvFLyt<*zvD>w>%1)~~S~Jfz z3Vr{GLf5w%P0isDX?q0-{OE5=PSf~}lz&Rs+HD1O3u){*ARH%4f2#XO{5=&u=I6$; z>y$W)d#R@(uFl<+GqhYtl^@sfUUXNHj} F$kiA+2Ga42rS?NN zS}x)72zw88<8nZPz#5Y4TOrv%iR(wcZ c?@s;kCr(9HTnO&ikbt0LbIKIubY5M z$g2}{2OL4xE*8^K^L_%OBr8l&_p%63uZc4O(6YSYD)+hP>AjXZP^gNj$|-wW@0$5S zvO3w>& @~ZD%yCfyYAGambn~}s z>K#C2xv?z=n*D_R47o4JLJOoSC*c3Q%ZR4Z`Wl1@T+aY%>N zYy;qRFPNZ-whd;@9;roqJ*q%q!Lr{MmOs&_F-ybJeJyFoz9>e)9~*(s@_0Fvn(G?M zV-8dYB+i{ulLhzG+dMr?G<$rgCzrXKGh1GtyIGX@F$g=-3k(%{CZ;tdrneL8z0wTK z6E |NB4z@HW|KCTB|H`oX{2}W5 _Xd1KcvO&pkVr^81%}UYe}V_BExm1OgSN(5S15%k9R*0D _eP6wZ8x8MA>Z zQ;xERpT8SqUCF=ph1X{virhi5C%hKqHib?(H~Rrb2AE!~Fp_QL(?@yuqgxg^hqvrj z50nGe_zbPfv*57GEu+z$UUS;%Uw65Fh9NY+b@GmPY|AZSgb*GhwNu67Y#~}?JpwtR zTRPnJPL9*Sk}N~4a$1mUn$=ygRvb~)r*vb2bvY*Pvt&tw4ovcH$k&aYVXw!DeHQ_M zeR!v!0=}1(C`R(Ne!yK-v#|)PO6O^&gs*x2U1)Xtp@Ye|OFYQ?2pm%2(7qIA=!ynP zq_)%YI#ptL57@%icV60QruE>Y<4B=&T~yCBNlk9X-#shE7DH=lk&}8QQ$#lY1{pHIEOjhd6As<DKexv!99 zH?1z1zTXTKcK;qVq4r5K4^N^K+MgfOHv9;tvju4B2<3E}I#R~(PeH_`eme!5J7VV{ z#;Y^CF_*BFR#yain64@hfok!4?wZPOvd!>-E0cmDrN^rG5wiX2%-R!Qr`cu`UH_=Y zy0DuM@-ToSKE%N_m{Xo?tBt1kY}`$JfnOL%4a96ZjtQm(MqfqhY`rH0^Sh$*mu~ns zj=aN%k2LjX0FM^l-PX>VV7GdQyhCIt*2>&o-n2O%F#^Ww&7;G4AjV5)x<|aytT3dm z(rYTjE1q}h*OZOkbjp53{posvdSx(W1C)}0RqCtyH_9**Boa1oJL}A<>SYFOKQO!B zTU~f-_9xaiUyWT8RM+)^?_FeJyOxt!-1D*@0^!(f$uDb6elyD`|@l ze)D0U?Kd`AdD3g2)XHpsR+LK{F>kl~NmoZUqX(WNHaYbMr(VWdZEfnq>%==aoln)6 zi%8R9!Q~l2|5S|F;CKrT-? 5o=($Ho-G5Hk^_Ss$=;cZcgVEcE>n7h%?)Ux2 e{=buQ!j`#sB{7j}e--#&5=akXbQ^IC7W-fRMAsky literal 0 HcmV?d00001 diff --git a/tests/snapshots/stax/test_sign_message_kanji/00003.png b/tests/snapshots/stax/test_sign_message_kanji/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..c1a8fd4eb09009370b79a8e7d92355e84bad3426 GIT binary patch literal 5084 zcmeHLZB&wJ-nXVQQ?sYesN #mc*DSArvuuui#U|>JL*E_Gt=5P& z=+V(%u7+oJBM Uv;!_`kypQ`bxH~!zi!MqfOTGoi#syg2YC@|D4RJFHuA2$jy4?(di=`_?+YS~ zpNlJQJfzFH>uu7bg;)N0IGo08=d_-fANKZS#rD!8Mj^=+=C*8xIu)06EorsdekG|} zuGi~5U6)j=i&)5|qsV<#@ovo{7v^t$Gs^NeFb#T+b652i8K6exRAb+7!?49X9`C|F zUxSFD?jo4+a}SwuP3CnX4a@xU5)@`mO=%ecS4bkBW7s$>7NX1hDLKe70f^IEMmwxg z4{i&)_yjpSH+Ov4tW+vl$f~*>J9h9Gv(>^hp;vE+ePahhl>dJD?L&13nM|J5=K8s9 z<#H36#OX?HUS2mi=E>uc@IGVK2cP9f5-+1C^aevLfgSsB9-p5Uv{1Vrm!H4$Xe%;5 zdlg(iIb};`#5FOK?@MobQdnyg6C8;l4Jn#; Qx}L+M?*Lt|RxpBi4sFOI}Z&|=Ao0YFJv+-P6){o?WY zK;zj}YK9P<3B-}T%G|C8QX_wSVkqu$0$0}i96PMNs4Eak>>~-GCwjBdvUr^6 )fz zTOv=9c_Jbz`W<+%15?+#ySqJ^dEWkLKNoUwjRxae6RKvaF6*dOyW#FJ&f+b5or-(p zd_^XTu8Gd-gU%^3V5`)U;y|iXqn+j{#IrmVh;wC>f?5fs=!+ubX`RPKEGCml)U{)| zCgK!jT_mCF5_z#c^%q)Q5^jz!i@Qxkqg102b@9THgESY;uY=WfsiD9U_#{1YBz4m; za1H>7Gq?s3kabZdhvAneU|li0);r`82rwOap4MbEk$L2nE9e>3cwmhPC(CQE>K(t< zcD}L>#;LD{kD(Fbw-uqZ!(pjiN!*T#0hT6iT;-9~e#-Q-_6Bn)b0M?f^SjlP5oYbP zEwYf333A!>Zn+L?6u``7mMJYK2uGEw)#sZk>wM6&? %(7Am zwHh)uu!Y_7iwKI^DW4}lix@5kP9sHkDl0vl*YhQhze5T1LhGUDd^@$ykl_#$o}4+@ zewP-|(nf8levvy@acEPcMd_=1j0gKEeIEY@|B+N&oo^vQ@CS46YD-riR5I!++&>_8 zybdwDZh7yjolg~HE;|&V5jfmS9}p_zfp_nuzD=QYsX5vctQBH z`~glZuYx87_HhWUS54upNXr$3`I0fA@)Yn+K!~rEO+@IN#{tg$X?H2xk=LK&hc5dt zSTN?}DP9z;=$h4LL;T0gfH++p%r~v+7h11(^Cq^N7bIWR@|itX4D<5}4Tepl(OL*@ zTpKHMtdv85nJ+(23Qw`wWnY9{A(+gDfW36Q5U2?O+rfy77~cJs(w3L@SVZZQq7mj) z6bf}eM{zEJa4%1We`JUA8MEUW&g14heobqw45guY$_!d=d$b`jo)Pom867L!xqm7o z7*(0vGU5k?x`->H>I!S5Z!$xZzCIXWqP|X!eHcwVX^#|j0RYH(o&$HGkKakYIvZNg z@%O9kK#F>kvvSw>s?} >YkaIAqr-s z-s;ZC97oZGeTUu4;fzuI!tznmBcYTLO*bT%Z=F(t;55;Vvz2@hISX&wUisG(vA&(pO1?v-edAzkENnBWDa`^akG254dJU=HkM_!tCs9t7{?si;aO; zEVe M!(#NFLJ AaduK8Lq`aD%g?tmy&QFd8m9 z^X(4NdGMG|J#Ho_>WKmnL~7EX)0?-e$!+B`IaxC=6Z%~PB$t$<#KDjzDy7sn#Wng4 z_A`AX8(hzl2NAG}AlMDHZCbm|ZGFe58^ih7m4} zp2~^FVYSri*6v2{g7UO)D?mzAWR9T4SU_@8-!rIfYiomf`Om(7{DXFNMesK5VrTWr z#l!hM@kg2p_J#~l9HWwLvJr4QRS0cBOWQ-}+StfV@Sq6XZHAIt77x0PZzCRC3@*^= z&xu$z-9u^}%)A`wQr(+e^!(SHxcPH4m31TeBlxccoLC A&y 6SO&)G4R|+-s*}58l>c{g;@YVQkH* zA3Kty?|+g>QX!O6#im`h+-qB75>*h4X)PSF8`T^-6fUdS4|JZ@dDWb(Q7vpid-j#} zA1e5~4t5oi_1_o82eV7}!2LhujBgf@EcMBhq2fjXtd^Nx0ST-KYI;_-wNRm)%*Mvt z&|prc#NOdRgbJ0qY-D4;ZbX9#3k!3_a-AAM
sie| zOzoG*^UYaOiDZ ylpJeH==X=FV%CB){+6}TyP76c)*~#DYx12wpIHF%g zamN8I|G mKnN|A`?2;O``&TKIOB|Q&p!9hz5nv%d%w5LIp0~HXTCAedwh|Rn~{o& z>LU1w<})fP>UUIBG==muz&}d*9aU6Rclg1Y8pgr7ggHW1rDQw8XUgyAMemHB53ht? zGC*k3dp;Bj-Dj|f`fy$73B$7}RGgcBU|{eiwlpRbCN7}bhbp=^B89ED8@qK)>-`0) zhP_j@=TuZH2|YB0=OfSEzevOMikps;N{${x<;n)3nz#<9`cuP}+93M>c)&iYoQdj} ziC+15pQFEO`iCBx^b-9C0d>-(qnmIl@~m&A{-Gj1TL713MaNnAgG9dNe5FC-0QK_) zuqosE64zdcR89&3CDu$2x}SAbEA3&k3gQRwsoeZ!ljhbhoLD#&-}=U;LV4_Q(k*dK zy+61)C4*#IeVtt`1*%;Q`)Hgzgk2oNnk%(zsY!B(Ms|TA5Dio2?eXnw6AK2TN(E=m z$a6#AdBq1*cTR?G!e7l8zBm0noOL$5ShVJ|vJ9{hr^SYn-29!`-pi1lb 2iL)&E0|J#k`XP$6rJ0btu_;w)%{ zucZY)7y75z!-j5Fnf=yE`{&1J(*GsH`IDcc?&TGBKncI3Y )+MFP_o=K%u{A zt&d+XW@R3wga21E@FvM@UP+hwk;l!s$(2A1iufWJ^FK|b2MKC1*)Im;8UD7I9ucwn z6mpw0&85GM=M}3ZL 5gCjDZPE{4W)cuD^IoVOCRhyaBsQ~(5oy;P~Hg3~{ zY-1-Jr_hW#w$w7)Yuw+oJBv2yI3*?4xMPVf%#6TDKCLYB$loLLjUA?P#=qSHbS9l8 z*Y`uZ6zW&b11KZoO|-dV`t2c##3eRJPxY`^gIcC3zy2`wGRlP~+;yeTx#X!qAB{h} z%EGJGV}`gt^Zo@OH@S7~IAVpz%z$E?Mh#$%L!mF+JPm~a!z+clhZqSajQ_~hH)fz= zG8p2rQM&0l+t# C*yw-p z8vWNV;{WS2oRC&&?pM`=5{kc)Iz$;!q(z%JB1MDkwcSd{JhU^ZcxA5ibN!Jre=_f* zYYqtX6|L`?2COl5nS$VJu^MbT){-E8-3QEYG~wi!lHoYh(zvZsCmK%(q>xArXu`Mp z+~!~iV9A}idgYU@X^u0-JVW*CIGl5n#U;y? U*{5NS(Ql49&wG6egOPG@%JCZ!XBwFr8xkm2@G9_+n8oyNFw? zXLn^~-@?1HRpZjAKv{-`kbJZBrN)wn_`ctLk{?`n&JWbT)!3Az2L $6xl2WpzsxROzE+b (9sQe*W9XqWo6~* zwjswoqlspdChqo6k1|MCQqlMMXqaeU+x<}dT3AzQ;&mlyW4G9ZKDp x8ap(Wvj3CJ9+)(E+=T(!rH&ffo=`f zPr6QaACiugf4g0RU_4WjiYhBVR!)jEKVlU)M-S2w9;{a@Db2(%mYlurJ#TR>7M!AD z^)|;{lKb*Lti7V2@hf2zmR^mir`^%K$b?F%a5}WP=%9Gz(JkQosk*(`t>9Lx*|9Z1 zU5df+wNN(5wcE^nc=D{_r&&T^Vrop1Co4PTIxx6};}7h ;I)#OwDdkgN} zTN}OkhdV3wGb{e5fe$ZmeJ$ J1zng?T+(ll5;6iLR&$N0q)(+(a>@H)llz`Y? z$=lFP#QW^Q3U;rnWWU~wZDbs*cgJgHI2ERvlKGFn^FdVd*!TejevNq$!rd^=V3NM% z+BEBl(6g+w7UP}^)v{-c>WR9lWq1lEFE(qC?CrmNbDv_!{{HaMs%PAF_^%LJ9xE-w zC#W`Ns};-}wOZP{%zX^FKO -ap_yO1Y)*vFqN@?e$ zUq=v}f;$+!QR2gE)EPy<6jIi=F0L1?8EwHLR?fpP-E*Fo)2W&6r?S<$6!6}7^jYAR zmf>UDA`R^2w)y@&*qxJ?nzltV7i8|n-yCC1T=9Q=*X4ume8Hqxl0A3y%ZIi_2@XiX z>5G&?p+7uj8Mb;tXJdCPi&$xxl7+zAV?!wi+Tgn?nyGBb=QK8pPpacc88q^)kd?c- z M4e}*Zd_s zB7!w8`Y58)x&Va>;$R78(e3s6;Ftxjx=#P(?n@0@SYENH&J_@S)Zo(g%r@0AaQ8|@ z<0oAm!&Uf`{hw=GqQNH^n>5XN4krWl-fqU9$)hya{nfQe`YPMFEG*MBB>$?GosHky z;uX8Zb$_KiJ&}f|Y;=Yka-LSrymWs-J9ky;K!c#c#V=%Nr*&QQ_23pGEF(ir5)#YD zlu}l4;7gbm;?T@4V^tXA &o7o!RC9yf}( zBb#IsBOM>Vq0@|0OtL)f3dzTX*j<~NhC*!oqz755`y1Ic*kqliS{^)52BfhT>4? 4)bxcsOrQ R?AFsi>; r0 z?Q@ Z{_;SnY_LERWc)AwG<^=nh3rkTw>(6V<1iNXshGZ_bjA}V0{nwX{;_aPN? z4Yu_?3U*7BI;hjS{js_b*tdPIb{Zev9i)^QU;M*eKG7{;N4DvBE$hJp1y=~uxcu1h zy!{{1u LG;tZI8M+9_u>6QfE`(;m3&4i9aPJ!F;< zh8K&j1&{Y@u$C|}5MHu}-)@(2L!h&oC!w95xo>2X>_d%>=azh>0(f(X6w2`lv@>wW zp=KI?NF#kQT9x&6=MiuN+9QxyAA@A!E=>~?Ekk3IB}zDQP9`95I%P<4wk5L__BggT zCtz6iBdm;v@A(taFB}6e<&|ilPQ!xu6eCQm@Qwei?k!hV>w9~(beb3N6#LA=k$=(4 zGEo2D$m5IF2T x(mD-s!l_@LjR=QMtW%o74bTO3Y_8@K)*uk>LO z{#!Ar`0EjbLo3}8$Q#1HlRENSodm(pOwx!HyK8zz2B29q%e5q(xcVO7s8t}*81vqQ zsq2oHKaKU;Zl;aBHC_aQ=NqS&;zGtGeV$Bb?u1QFr*!)!-VHo+*@H;|j)S|sfO|N; z$Je;|P|FZVC+IY=)@XqzCMMhVdm@|O&OA(#wMTO?3i$^cZPNrr+clfS<1dqD#FJpW zm+WEHs(1=Z+aa76@LZ%0;B@ =f-Z^&r*3^s)*+W n6H$l z2YFA6je3iif?DQ0N7nG xyvrXg>ZZXdpM5~MN)o4Vv_nV(+&pNf*TWN$a~sTy^x@56{k$ (wfUcY3t|XBQFe{66fsix`ygt%G k>VKd7Q*iyD-j*QutXgBf z0I{Xc$>4qCetb`*0@vb@RNXXd0_osq%ToW;<2gcLl}%i_(V!YshXHSXO9YI4KkK~k zs2+kv3YptJD94thP7_G2IAA5UK;=wkm#BcSTgObNLqXGibIr76QZ#~c+_d-wB>EDB zb(+suP%K@2tn<^jahK$3i-%y k#BG01qo@O!RBy6GJVB=981s)ursy_c6pCmeVBzAgdp+) =Nbm hRt!=A%ba||M6BQOzGEHdteb3+cm$4J%BTh^m{z$n1)!p5lLK6bG-3usEilEeU zD!zJ kNbppuFp} z9+yp1e;U_k*D&K>mxep=MBE*ePQsE7KrlwYB(1y-t7Ub)#6IeKzq_Q2Havg^2a+X@ zT8N%IZO`7j{A^MS>h9`gPS{bQ!ArIN5w2EHH(f=fkXcybn7j1Hjal~MM2eUVs=LLT z7|M6mUfRXE@a+@Y5-b$_ojcPl#`-iqVj0zU+Ic!_SC3rLO?x?Z@ADtq$^n}gl*ZK= zvC+W}HJ1=%Kh)~j#z~t0`EwcuUiU9r{FwUDApY@S{F7%6u&w<^^ 7_b% z#(u!L8{uNi4_7TJiPJy~bC A<)vqghOlOf?I*h-!`QHea@A2oATnB z9EvpTM5>^qJ3~G)fWmoAu`7ZHejdA>K46y@nU_$B4;OjnJQd8PnIDEeKS1~dJDk4X zP`$b#nxhK&IbU~3^(pCwiW8f`XGzSG-nem6O7qrFRie;(t9RTXAsEQa=B;K( z)hlPWow*G|WF-pu%Par6xrgg&SHlPLTHr|L8?PApyt^@&)7XyiJ8&o`I01-ahD6c& zy=yRJH}m7QYi8!@ODL2+8=Wslpni|!%Z2 #I{>9TMsrEZeO_=~1N9n#7)+kq|l3VHf&md(>KjRdNm oyz