diff --git a/common/protob/messages-solana.proto b/common/protob/messages-solana.proto index 4ed0a2dcdf..5068fdaeb9 100644 --- a/common/protob/messages-solana.proto +++ b/common/protob/messages-solana.proto @@ -8,14 +8,14 @@ option java_outer_classname = "TrezorMessageSolana"; /** * Message version for Solana SignMessage */ -enum SolanaMessageVersion { +enum SolanaOffChainMessageVersion { MESSAGE_VERSION_0 = 0; } /** * Message format for Solana SignMessage */ -enum SolanaMessageFormat { +enum SolanaOffChainMessageFormat { V0_RESTRICTED_ASCII = 0; V0_LIMITED_UTF8 = 1; } @@ -57,24 +57,35 @@ message SolanaSignedTx { } /** - * Request: ask device to sign a message + * Request: ask device to sign a off-chain message * @start - * @next SolanaSignedMessage + * @next SolanaMessageSignature * @next Failure */ -message SolanaSignMessage { +message SolanaSignOffChainMessage { repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node required bytes message = 2; // the message to sign - optional SolanaMessageVersion message_version = 3[default = MESSAGE_VERSION_0]; - optional SolanaMessageFormat message_format = 4[default = V0_RESTRICTED_ASCII]; + optional SolanaOffChainMessageVersion message_version = 3[default = MESSAGE_VERSION_0]; + optional SolanaOffChainMessageFormat message_format = 4[default = V0_RESTRICTED_ASCII]; optional bytes application_domain = 5; // application domain must be 32 bytes } /** - * Response: signature for message + * Request: ask device to sign arbitrary message except valid solana transaction + * @start + * @next SolanaMessageSignature + * @next Failure + */ +message SolanaSignUnsafeMessage { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required bytes message = 2; // the message to sign +} + +/** + * Response: signature for message signing * @end */ -message SolanaSignedMessage { +message SolanaMessageSignature { required bytes signature = 1; // the signature of the message - required bytes public_key = 2; // the public key of the signer + optional bytes public_key = 2; // the public key of the signer } diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 28b07b93f3..278653fe97 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -448,8 +448,9 @@ enum MessageType { MessageType_SolanaAddress = 10101 [(wire_out) = true]; MessageType_SolanaSignTx = 10102 [(wire_in) = true]; MessageType_SolanaSignedTx = 10103 [(wire_out) = true]; - MessageType_SolanaSignMessage = 10104 [(wire_in) = true]; - MessageType_SolanaSignedMessage = 10105 [(wire_out) = true]; + MessageType_SolanaSignOffChainMessage = 10104 [(wire_in) = true]; + MessageType_SolanaMessageSignature = 10105 [(wire_out) = true]; + MessageType_SolanaSignUnsafeMessage = 10106 [(wire_in) = true]; // Cosmos MessageType_CosmosGetAddress = 10800 [(wire_in) = true]; diff --git a/core/src/all_modules.py b/core/src/all_modules.py index e06d7dd0a4..82a78b5ad2 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -129,10 +129,10 @@ import trezor.enums.SafetyCheckLevel trezor.enums.SdProtectOperationType import trezor.enums.SdProtectOperationType -trezor.enums.SolanaMessageFormat -import trezor.enums.SolanaMessageFormat -trezor.enums.SolanaMessageVersion -import trezor.enums.SolanaMessageVersion +trezor.enums.SolanaOffChainMessageFormat +import trezor.enums.SolanaOffChainMessageFormat +trezor.enums.SolanaOffChainMessageVersion +import trezor.enums.SolanaOffChainMessageVersion trezor.enums.TonWalletVersion import trezor.enums.TonWalletVersion trezor.enums.TonWorkChain @@ -799,10 +799,12 @@ import apps.solana.message apps.solana.publickey import apps.solana.publickey -apps.solana.sign_message -import apps.solana.sign_message +apps.solana.sign_offchain_message +import apps.solana.sign_offchain_message apps.solana.sign_tx import apps.solana.sign_tx +apps.solana.sign_unsafe_message +import apps.solana.sign_unsafe_message apps.solana.spl._layouts import apps.solana.spl._layouts apps.solana.spl._layouts.token_instructions diff --git a/core/src/apps/solana/sign_message.py b/core/src/apps/solana/sign_offchain_message.py similarity index 79% rename from core/src/apps/solana/sign_message.py rename to core/src/apps/solana/sign_offchain_message.py index e60e53ba71..a4664b26b3 100644 --- a/core/src/apps/solana/sign_message.py +++ b/core/src/apps/solana/sign_offchain_message.py @@ -3,9 +3,9 @@ from trezor import wire from trezor.crypto import base58 from trezor.crypto.curve import ed25519 -from trezor.enums import SolanaMessageFormat, SolanaMessageVersion +from trezor.enums import SolanaOffChainMessageFormat, SolanaOffChainMessageVersion from trezor.lvglui.scrs import lv -from trezor.messages import SolanaSignedMessage +from trezor.messages import SolanaMessageSignature from trezor.ui.layouts.lvgl import confirm_sol_message from trezor.utils import BufferWriter @@ -17,17 +17,17 @@ from .publickey import PublicKey if TYPE_CHECKING: - from trezor.messages import SolanaSignMessage + from trezor.messages import SolanaSignOffChainMessage from apps.common.keychain import Keychain # The signing domain _SIGN_DOMAIN = b"\xffsolana offchain" # The header version, currently only version 0 is introduced in proposal[https://github.com/solana-labs/solana/blob/master/docs/src/proposals/off-chain-message-signing.md] -_ALLOWED_HEADER_VERSIONS = (SolanaMessageVersion.MESSAGE_VERSION_0,) +_ALLOWED_HEADER_VERSIONS = (SolanaOffChainMessageVersion.MESSAGE_VERSION_0,) # The allowed message formats _ALLOWED_MESSAGE_FORMATS = ( - SolanaMessageFormat.V0_RESTRICTED_ASCII, - SolanaMessageFormat.V0_LIMITED_UTF8, + SolanaOffChainMessageFormat.V0_RESTRICTED_ASCII, + SolanaOffChainMessageFormat.V0_LIMITED_UTF8, ) # The number of signers _SIGNER_COUNT = 1 @@ -50,9 +50,9 @@ @auto_keychain(__name__) -async def sign_message( - ctx: wire.Context, msg: SolanaSignMessage, keychain: Keychain -) -> SolanaSignedMessage: +async def sign_offchain_message( + ctx: wire.Context, msg: SolanaSignOffChainMessage, keychain: Keychain +) -> SolanaMessageSignature: # sanitize message sanitize_message(msg) # path validation @@ -75,18 +75,20 @@ async def sign_message( # prepare the message message_to_sign = prepare_message(msg, signer_pub_key_bytes) signature = ed25519.sign(node.private_key(), message_to_sign) - return SolanaSignedMessage(signature=signature, public_key=signer_pub_key_bytes) + return SolanaMessageSignature(signature=signature, public_key=signer_pub_key_bytes) -def prepare_message(msg: SolanaSignMessage, signer_pub_key_bytes: bytes) -> bytes: +def prepare_message( + msg: SolanaSignOffChainMessage, signer_pub_key_bytes: bytes +) -> bytes: """Prepare the message to be signed.""" - + message = msg.message + message_length = len(message) buffer = bytearray( - len(msg.message) + message_length + (_PREAMBLE_LENGTH if msg.application_domain else _PREAMBLE_LENGTH_LEDGER) ) bw = BufferWriter(buffer) - message_length = len(msg.message) writers.write_bytes_fixed(bw, _SIGN_DOMAIN, 16) writers.write_uint8(bw, msg.message_version) if msg.application_domain: @@ -102,8 +104,9 @@ def prepare_message(msg: SolanaSignMessage, signer_pub_key_bytes: bytes) -> byte return bytes(bw.buffer) -def sanitize_message(msg: SolanaSignMessage): +def sanitize_message(msg: SolanaSignOffChainMessage): """Sanitize the message.""" + message = msg.message if ( msg.application_domain and len(msg.application_domain) != _APPLICATION_DOMAIN_LENGTH @@ -111,23 +114,23 @@ def sanitize_message(msg: SolanaSignMessage): raise wire.DataError( f"Application domain must be 32 bytes, got {len(msg.application_domain)}" ) - if len(msg.message) > ( + if len(message) > ( _MAX_MESSAGE_LENGTH if msg.application_domain else _MAX_MESSAGE_LENGTH_LEDGER ): raise wire.DataError( - f"Message is too long, maximum length is {_MAX_MESSAGE_LENGTH} bytes, got {len(msg.message)}" + f"Message is too long, maximum length is {_MAX_MESSAGE_LENGTH} bytes, got {len(message)}" ) if msg.message_version not in _ALLOWED_HEADER_VERSIONS: raise wire.DataError(f"Message version must be 0, got {msg.message_version}") if msg.message_format not in _ALLOWED_MESSAGE_FORMATS: raise wire.DataError(f"Message format must be 0 or 1, got {msg.message_format}") - elif msg.message_format == SolanaMessageFormat.V0_RESTRICTED_ASCII: - if any(b < 0x20 or b > 0x7E for b in msg.message): + elif msg.message_format == SolanaOffChainMessageFormat.V0_RESTRICTED_ASCII: + if any(b < 0x20 or b > 0x7E for b in message): raise wire.DataError( "Message format 0 must contain only printable characters" ) - elif msg.message_format == SolanaMessageFormat.V0_LIMITED_UTF8: + elif msg.message_format == SolanaOffChainMessageFormat.V0_LIMITED_UTF8: try: - msg.message.decode("utf-8") + message.decode("utf-8") except UnicodeDecodeError: raise wire.DataError("Message format 1 must be a valid UTF-8 string") diff --git a/core/src/apps/solana/sign_unsafe_message.py b/core/src/apps/solana/sign_unsafe_message.py new file mode 100644 index 0000000000..4298a76d02 --- /dev/null +++ b/core/src/apps/solana/sign_unsafe_message.py @@ -0,0 +1,58 @@ +from typing import TYPE_CHECKING + +from trezor import wire +from trezor.crypto.curve import ed25519 +from trezor.lvglui.scrs import lv +from trezor.messages import SolanaMessageSignature +from trezor.ui.layouts.lvgl import confirm_sol_message + +from apps.common import paths, seed +from apps.common.helpers import validate_message +from apps.common.keychain import auto_keychain +from apps.common.signverify import decode_message + +from . import ICON, PRIMARY_COLOR +from .message import Message +from .publickey import PublicKey + +if TYPE_CHECKING: + from trezor.messages import SolanaSignUnsafeMessage + from apps.common.keychain import Keychain + + +@auto_keychain(__name__) +async def sign_unsafe_message( + ctx: wire.Context, msg: SolanaSignUnsafeMessage, keychain: Keychain +) -> SolanaMessageSignature: + # sanitize message + message = msg.message + sanitize_message(message) + + # path validation + await paths.validate_path(ctx, keychain, msg.address_n) + + node = keychain.derive(msg.address_n) + + signer_pub_key_bytes = seed.remove_ed25519_prefix(node.public_key()) + ctx.primary_color, ctx.icon_path = lv.color_hex(PRIMARY_COLOR), ICON + decoded_message = decode_message(message) + address = str(PublicKey(signer_pub_key_bytes)) + + # display the decoded message to confirm + await confirm_sol_message(ctx, address, None, decoded_message, is_unsafe=True) + + signature = ed25519.sign(node.private_key(), message) + return SolanaMessageSignature(signature=signature) + + +def sanitize_message(message: bytes) -> None: + validate_message(message) + try: + _ = Message.deserialize(message) + raise wire.DataError("Valid transaction message format is not allowed") + except BaseException as e: + if __debug__: + import sys + + sys.print_exception(e) # type: ignore["print_exception" is not a known member of module] + return diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index c7f1b4a18c..0b3b62a8db 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -258,8 +258,10 @@ def find_message_handler_module(msg_type: int) -> str: return "apps.solana.get_address" if msg_type == MessageType.SolanaSignTx: return "apps.solana.sign_tx" - if msg_type == MessageType.SolanaSignMessage: - return "apps.solana.sign_message" + if msg_type == MessageType.SolanaSignUnsafeMessage: + return "apps.solana.sign_unsafe_message" + if msg_type == MessageType.SolanaSignOffChainMessage: + return "apps.solana.sign_offchain_message" # starcoin if msg_type == MessageType.StarcoinGetAddress: diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index a2bbfbc2e3..8c64055a46 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -306,8 +306,9 @@ SolanaAddress = 10101 SolanaSignTx = 10102 SolanaSignedTx = 10103 - SolanaSignMessage = 10104 - SolanaSignedMessage = 10105 + SolanaSignOffChainMessage = 10104 + SolanaMessageSignature = 10105 + SolanaSignUnsafeMessage = 10106 CosmosGetAddress = 10800 CosmosAddress = 10801 CosmosSignTx = 10802 diff --git a/core/src/trezor/enums/SolanaMessageFormat.py b/core/src/trezor/enums/SolanaOffChainMessageFormat.py similarity index 100% rename from core/src/trezor/enums/SolanaMessageFormat.py rename to core/src/trezor/enums/SolanaOffChainMessageFormat.py diff --git a/core/src/trezor/enums/SolanaMessageVersion.py b/core/src/trezor/enums/SolanaOffChainMessageVersion.py similarity index 100% rename from core/src/trezor/enums/SolanaMessageVersion.py rename to core/src/trezor/enums/SolanaOffChainMessageVersion.py diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index ed4eb2e51d..65c8def91a 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -324,8 +324,9 @@ class MessageType(IntEnum): SolanaAddress = 10101 SolanaSignTx = 10102 SolanaSignedTx = 10103 - SolanaSignMessage = 10104 - SolanaSignedMessage = 10105 + SolanaSignOffChainMessage = 10104 + SolanaMessageSignature = 10105 + SolanaSignUnsafeMessage = 10106 CosmosGetAddress = 10800 CosmosAddress = 10801 CosmosSignTx = 10802 @@ -709,10 +710,10 @@ class NEMImportanceTransferMode(IntEnum): ImportanceTransfer_Activate = 1 ImportanceTransfer_Deactivate = 2 - class SolanaMessageVersion(IntEnum): + class SolanaOffChainMessageVersion(IntEnum): MESSAGE_VERSION_0 = 0 - class SolanaMessageFormat(IntEnum): + class SolanaOffChainMessageFormat(IntEnum): V0_RESTRICTED_ASCII = 0 V0_LIMITED_UTF8 = 1 diff --git a/core/src/trezor/lvglui/i18n/keys.py b/core/src/trezor/lvglui/i18n/keys.py index 8b696166ae..0aa916922a 100644 --- a/core/src/trezor/lvglui/i18n/keys.py +++ b/core/src/trezor/lvglui/i18n/keys.py @@ -1883,4 +1883,17 @@ POWER_ON_LOW_BATTERY_DESC = 860 # Auto Lock/Shutdown ITEM__AUTO_LOCK_AND_SHUTDOWN = 861 +# Solana Raw Signing +SECURITY__SOLANA_RAW_SIGNING_TITLE = 862 +# Allows signing raw Solana messages without processing or validation. This ma +# y expose you to phishing, blind signing, and unauthorized approvals. Use wit +# h caution. +SECURITY__SOLANA_RAW_SIGNING_DESC = 863 +# Enable Solana Raw Signing? +SECURITY__SOLANA_RAW_SIGNING_ENABLE_TITLE = 864 +# This may expose you to phishing, blind signing, and unauthorized transaction +# s. Enable only if you fully understand the risks. +SECURITY__SOLANA_RAW_SIGNING_ENABLE_DESC = 865 +# Risk of phishing & blind signing. Proceed only if you trust the source. +SECURITY__SOLANA_RAW_SIGNING_TX_WARNING = 866 # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/de.py b/core/src/trezor/lvglui/i18n/locales/de.py index 8f31b23d36..770b1e1155 100644 --- a/core/src/trezor/lvglui/i18n/locales/de.py +++ b/core/src/trezor/lvglui/i18n/locales/de.py @@ -862,5 +862,10 @@ "Schwache Batterie", "Ausschalten", "Automatische Sperre/Abschaltung", + "Solana Raw Signing", + "Ermöglicht das Signieren von rohen Solana-Nachrichten ohne Verarbeitung oder Validierung. Dies kann Sie Phishing, blindem Signieren und unbefugten Genehmigungen aussetzen. Verwenden Sie es mit Vorsicht.", + "Solana Raw Signing aktivieren?", + "Dies kann Sie Phishing, blindem Signieren und unautorisierten Transaktionen aussetzen. Aktivieren Sie dies nur, wenn Sie die Risiken vollständig verstehen.", + "Risiko von Phishing & blindem Signieren. Fahren Sie nur fort, wenn Sie der Quelle vertrauen.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/en.py b/core/src/trezor/lvglui/i18n/locales/en.py index 9ce2c64ccd..c98e5534ff 100644 --- a/core/src/trezor/lvglui/i18n/locales/en.py +++ b/core/src/trezor/lvglui/i18n/locales/en.py @@ -862,5 +862,10 @@ "Low Battery", "Powering off", "Auto Lock/Shutdown", + "Solana Raw Signing", + "Allows signing raw Solana messages without processing or validation. This may expose you to phishing, blind signing, and unauthorized approvals. Use with caution.", + "Enable Solana Raw Signing?", + "This may expose you to phishing, blind signing, and unauthorized transactions. Enable only if you fully understand the risks.", + "Risk of phishing & blind signing. Proceed only if you trust the source.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/es.py b/core/src/trezor/lvglui/i18n/locales/es.py index 10a2f46fd2..b5e828aa44 100644 --- a/core/src/trezor/lvglui/i18n/locales/es.py +++ b/core/src/trezor/lvglui/i18n/locales/es.py @@ -862,5 +862,10 @@ "Batería baja", "Apagando", "Bloqueo/Apagado Automático", + "Firma en bruto de Solana", + "Permite firmar mensajes sin procesar de Solana sin procesamiento ni validación. Esto puede exponerte a phishing, firmas ciegas y aprobaciones no autorizadas. Úsalo con precaución.", + "¿Habilitar la firma sin procesar de Solana?", + "Esto puede exponerte a phishing, firmas ciegas y transacciones no autorizadas. Activa solo si comprendes completamente los riesgos.", + "Riesgo de phishing y firma a ciegas. Proceda solo si confía en la fuente.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/fr.py b/core/src/trezor/lvglui/i18n/locales/fr.py index 0fd2e2d49e..f9a371b302 100644 --- a/core/src/trezor/lvglui/i18n/locales/fr.py +++ b/core/src/trezor/lvglui/i18n/locales/fr.py @@ -862,5 +862,10 @@ "Batterie faible", "Mise hors tension", "Verrouillage/Arrêt automatique", + "Signature brute Solana", + "Permet de signer des messages Solana bruts sans traitement ni validation. Cela peut vous exposer à des tentatives de phishing, à des signatures aveugles et à des approbations non autorisées. Utilisez avec prudence.", + "Activer la signature brute Solana ?", + "Cela peut vous exposer à des tentatives de phishing, à des signatures à l'aveugle et à des transactions non autorisées. Activez uniquement si vous comprenez pleinement les risques.", + "Risque de phishing et de signature à l'aveugle. Ne continuez que si vous faites confiance à la source.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/it.py b/core/src/trezor/lvglui/i18n/locales/it.py index f474058851..dd0edb27d8 100644 --- a/core/src/trezor/lvglui/i18n/locales/it.py +++ b/core/src/trezor/lvglui/i18n/locales/it.py @@ -862,5 +862,10 @@ "Batteria scarica", "Spegnimento", "Blocco automatico/spegnimento", + "Firma Raw di Solana", + "Permette di firmare messaggi Solana grezzi senza elaborazione o validazione. Questo potrebbe esporvi a phishing, firme alla cieca e approvazioni non autorizzate. Usare con cautela.", + "Abilitare Solana Raw Signing?", + "Questo potrebbe esporvi a phishing, firme alla cieca e transazioni non autorizzate. Abilitate solo se comprendete pienamente i rischi.", + "Rischio di phishing e firma alla cieca. Procedi solo se ti fidi della fonte.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/ja.py b/core/src/trezor/lvglui/i18n/locales/ja.py index 68a6fb36b9..42506a0014 100644 --- a/core/src/trezor/lvglui/i18n/locales/ja.py +++ b/core/src/trezor/lvglui/i18n/locales/ja.py @@ -862,5 +862,10 @@ "バッテリー残量低下", "電源オフ", "自動ロック/シャットダウン", + "Solana ローサイニング", + "生の Solana メッセージを処理や検証なしで署名することができます。これにより、フィッシング、ブラインドサイン、無許可の承認にさらされる可能性があります。注意して使用してください。", + "SolanaのRaw Signingを有効にしますか?", + "これにより、フィッシング、盲目的な署名、無許可の取引にさらされる可能性があります。リスクを十分に理解している場合のみ有効にしてください。", + "フィッシングや盲目的な署名のリスクがあります。信頼できる情報源である場合のみ進めてください。", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/ko.py b/core/src/trezor/lvglui/i18n/locales/ko.py index abad07f751..26d8a7ddd8 100644 --- a/core/src/trezor/lvglui/i18n/locales/ko.py +++ b/core/src/trezor/lvglui/i18n/locales/ko.py @@ -862,5 +862,10 @@ "배터리 부족", "전원 끄기", "자동 잠금/종료", + "Solana Raw Signing", + "처리나 검증 없이 원시 Solana 메시지를 서명할 수 있습니다. 이는 피싱, 맹목적 서명, 무단 승인에 노출될 수 있습니다. 주의해서 사용하세요.", + "Solana 원시 서명을 활성화하시겠습니까?", + "이것은 피싱, 맹목적 서명, 그리고 무단 거래에 노출될 수 있습니다. 위험을 완전히 이해한 경우에만 활성화하십시오.", + "피싱 및 맹목적 서명의 위험. 출처를 신뢰할 경우에만 진행하세요.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/pt_br.py b/core/src/trezor/lvglui/i18n/locales/pt_br.py index b2a8ae4d8f..9b2bc4924c 100644 --- a/core/src/trezor/lvglui/i18n/locales/pt_br.py +++ b/core/src/trezor/lvglui/i18n/locales/pt_br.py @@ -862,5 +862,10 @@ "Bateria fraca", "Desligando", "Bloqueio/Desligamento Automático", + "Assinatura Bruta Solana", + "Permite assinar mensagens brutas do Solana sem processamento ou validação. Isso pode expô-lo a phishing, assinaturas cegas e aprovações não autorizadas. Use com cautela.", + "Ativar Assinatura Bruta Solana?", + "Isso pode expor você a phishing, assinaturas cegas e transações não autorizadas. Ative apenas se você compreender totalmente os riscos.", + "Risco de phishing e assinatura cega. Prossiga apenas se confiar na fonte.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/ru.py b/core/src/trezor/lvglui/i18n/locales/ru.py index eb5d21fb88..f3378493b1 100644 --- a/core/src/trezor/lvglui/i18n/locales/ru.py +++ b/core/src/trezor/lvglui/i18n/locales/ru.py @@ -862,5 +862,10 @@ "Низкий заряд батареи", "Выключение питания", "Автоматическая блокировка/выключение", + "Solana Raw Signing", + "Позволяет подписывать необработанные сообщения Solana без обработки или проверки. Это может подвергнуть вас фишингу, слепому подписанию и несанкционированным одобрениям. Используйте с осторожностью.", + "Включить Raw Signing для Solana?", + "Это может подвергнуть вас фишингу, слепой подписи и несанкционированным транзакциям. Включайте только в том случае, если вы полностью понимаете риски.", + "Риск фишинга и слепого подписания. Продолжайте только если доверяете источнику.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/zh_cn.py b/core/src/trezor/lvglui/i18n/locales/zh_cn.py index c44c8eb471..f9f1cfcbe7 100644 --- a/core/src/trezor/lvglui/i18n/locales/zh_cn.py +++ b/core/src/trezor/lvglui/i18n/locales/zh_cn.py @@ -862,5 +862,10 @@ "电池电量低", "正在关机", "自动锁定/关机", + "Solana 原始消息签名", + "允许签署原始 Solana 消息而不进行处理或验证。这可能会让您面临网络钓鱼、盲签和未经授权的批准。请谨慎使用。", + "启用 Solana 原始消息签名?", + "这可能会让你面临钓鱼、盲签和未经授权的交易风险。只有在完全了解风险的情况下才启用。", + "存在钓鱼和盲签的风险 。 只有在你信任来源的情况下才继续 。", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/zh_hk.py b/core/src/trezor/lvglui/i18n/locales/zh_hk.py index 56ee47a86d..76812b1a6a 100644 --- a/core/src/trezor/lvglui/i18n/locales/zh_hk.py +++ b/core/src/trezor/lvglui/i18n/locales/zh_hk.py @@ -862,5 +862,10 @@ "電池電量低", "正在關機", "自動鎖定/關機", + "Solana 原始訊息簽署", + "允許簽署原始 Solana 訊息而不進行處理或驗證。這可能會讓你面臨網絡釣魚、盲簽和未經授權的批准。使用時請謹慎。", + "啟用 Solana 原始訊息簽名?", + "這可能會令你面臨釣魚攻擊、盲簽和未經授權的交易風險。只有在你完全了解風險的情況下才啟用。", + "有釣魚及盲簽風險。 只有在信任來源的情況下才繼續。", ] # fmt: on diff --git a/core/src/trezor/lvglui/scrs/template.py b/core/src/trezor/lvglui/scrs/template.py index f791be3f67..31eb7b2daa 100644 --- a/core/src/trezor/lvglui/scrs/template.py +++ b/core/src/trezor/lvglui/scrs/template.py @@ -556,8 +556,9 @@ def __init__( verify: bool = False, item_other: int | str | None = None, item_addr_title: str | None = None, - is_standard: bool = True, item_other_title: str | None = None, + is_standard: bool = True, + warning_banner_text: str | None = None, ): super().__init__( title, @@ -580,7 +581,8 @@ def __init__( self.warning_banner = Banner( self.content_area, 2, - _(i18n_keys.CONTENT__NON_STANDARD_MESSAGE_SIGNATURE), + warning_banner_text + or _(i18n_keys.CONTENT__NON_STANDARD_MESSAGE_SIGNATURE), ) self.warning_banner.align_to(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 40) self.item_message = CardItem( diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 0588c11561..13dc0a4e0a 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -59,8 +59,8 @@ def __getattr__(name: str) -> Any: from trezor.enums import ResourceType # noqa: F401 from trezor.enums import SafetyCheckLevel # noqa: F401 from trezor.enums import SdProtectOperationType # noqa: F401 - from trezor.enums import SolanaMessageFormat # noqa: F401 - from trezor.enums import SolanaMessageVersion # noqa: F401 + from trezor.enums import SolanaOffChainMessageFormat # noqa: F401 + from trezor.enums import SolanaOffChainMessageVersion # noqa: F401 from trezor.enums import StellarAssetType # noqa: F401 from trezor.enums import StellarMemoType # noqa: F401 from trezor.enums import StellarSignerType # noqa: F401 @@ -7851,11 +7851,11 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignedTx"]: return isinstance(msg, cls) - class SolanaSignMessage(protobuf.MessageType): + class SolanaSignOffChainMessage(protobuf.MessageType): address_n: "list[int]" message: "bytes" - message_version: "SolanaMessageVersion" - message_format: "SolanaMessageFormat" + message_version: "SolanaOffChainMessageVersion" + message_format: "SolanaOffChainMessageFormat" application_domain: "bytes | None" def __init__( @@ -7863,30 +7863,46 @@ def __init__( *, message: "bytes", address_n: "list[int] | None" = None, - message_version: "SolanaMessageVersion | None" = None, - message_format: "SolanaMessageFormat | None" = None, + message_version: "SolanaOffChainMessageVersion | None" = None, + message_format: "SolanaOffChainMessageFormat | None" = None, application_domain: "bytes | None" = None, ) -> None: pass @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignMessage"]: + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignOffChainMessage"]: return isinstance(msg, cls) - class SolanaSignedMessage(protobuf.MessageType): + class SolanaSignUnsafeMessage(protobuf.MessageType): + address_n: "list[int]" + message: "bytes" + + def __init__( + self, + *, + message: "bytes", + address_n: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignUnsafeMessage"]: + return isinstance(msg, cls) + + class SolanaMessageSignature(protobuf.MessageType): signature: "bytes" - public_key: "bytes" + public_key: "bytes | None" def __init__( self, *, signature: "bytes", - public_key: "bytes", + public_key: "bytes | None" = None, ) -> None: pass @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignedMessage"]: + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaMessageSignature"]: return isinstance(msg, cls) class StarcoinGetAddress(protobuf.MessageType): diff --git a/core/src/trezor/ui/layouts/lvgl/__init__.py b/core/src/trezor/ui/layouts/lvgl/__init__.py index 7c43520db4..01c275cb57 100644 --- a/core/src/trezor/ui/layouts/lvgl/__init__.py +++ b/core/src/trezor/ui/layouts/lvgl/__init__.py @@ -1340,7 +1340,11 @@ async def confirm_sol_memo( async def confirm_sol_message( - ctx: wire.GenericContext, address: str, app_domain_fd: str | None, message: str + ctx: wire.GenericContext, + address: str, + app_domain_fd: str | None, + message: str, + is_unsafe: bool = False, ) -> None: from trezor.lvglui.scrs.template import Message @@ -1353,6 +1357,10 @@ async def confirm_sol_message( False, item_other=app_domain_fd, item_other_title="Application Domain:" if app_domain_fd else None, + is_standard=not is_unsafe, + warning_banner_text=_(i18n_keys.SECURITY__SOLANA_RAW_SIGNING_TX_WARNING) + if is_unsafe + else None, ) await raise_if_cancelled( interact(ctx, screen, "confirm_sol_message", ButtonRequestType.ProtectCall) diff --git a/python/src/trezorlib/cli/sol.py b/python/src/trezorlib/cli/sol.py index 0c6167f5b0..2b21b35756 100644 --- a/python/src/trezorlib/cli/sol.py +++ b/python/src/trezorlib/cli/sol.py @@ -33,11 +33,11 @@ PATH_RAW_TX = "Base58 encoded transaction" MESSAGE_VERSIONS = { - "v0": messages.SolanaMessageVersion.MESSAGE_VERSION_0, + "v0": messages.SolanaOffChainMessageVersion.MESSAGE_VERSION_0, } MESSAGE_FORMATS = { - "ascii": messages.SolanaMessageFormat.V0_RESTRICTED_ASCII, - "utf8": messages.SolanaMessageFormat.V0_LIMITED_UTF8, + "ascii": messages.SolanaOffChainMessageFormat.V0_RESTRICTED_ASCII, + "utf8": messages.SolanaOffChainMessageFormat.V0_LIMITED_UTF8, } @click.group(name="sol") @@ -70,6 +70,7 @@ def sign_tx(client: "TrezorClient", address: str, raw_tx: str) -> str: @click.option("-v", "--message-version", type=ChoiceType(MESSAGE_VERSIONS), default="v0") @click.option("-f", "--message-format", type=ChoiceType(MESSAGE_FORMATS), default="ascii") @click.option("-d", "--application-domain", default=None, help="32 bytes hex encoded application domain or None") +@click.option("-u", "--unsafe", is_flag=True, help="Use unsafe message signing protocol") @click.argument("message") @with_client def sign_message(client: "TrezorClient", @@ -77,12 +78,16 @@ def sign_message(client: "TrezorClient", message: str, message_version: str, message_format: str, - application_domain: Optional[str] + application_domain: Optional[str], + unsafe: bool ): """Sign Solana message.""" address_n = tools.parse_path(address) - rep = solana.sign_message(client, address_n, tools.prepare_message_bytes(message), message_version, message_format, application_domain) + if unsafe: + rep = solana.sign_unsafe_message(client, address_n, tools.prepare_message_bytes(message)) + else: + rep = solana.sign_offchain_message(client, address_n, tools.prepare_message_bytes(message), message_version, message_format, application_domain) return { - "public_key": f"0x{rep.public_key.hex()}", + "public_key": f"0x{rep.public_key.hex()}" if not unsafe else None, "signature": f"0x{rep.signature.hex()}", } diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index d5ecddb569..1473bebab6 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -332,8 +332,9 @@ class MessageType(IntEnum): SolanaAddress = 10101 SolanaSignTx = 10102 SolanaSignedTx = 10103 - SolanaSignMessage = 10104 - SolanaSignedMessage = 10105 + SolanaSignOffChainMessage = 10104 + SolanaMessageSignature = 10105 + SolanaSignUnsafeMessage = 10106 CosmosGetAddress = 10800 CosmosAddress = 10801 CosmosSignTx = 10802 @@ -764,11 +765,11 @@ class NEMImportanceTransferMode(IntEnum): ImportanceTransfer_Deactivate = 2 -class SolanaMessageVersion(IntEnum): +class SolanaOffChainMessageVersion(IntEnum): MESSAGE_VERSION_0 = 0 -class SolanaMessageFormat(IntEnum): +class SolanaOffChainMessageFormat(IntEnum): V0_RESTRICTED_ASCII = 0 V0_LIMITED_UTF8 = 1 @@ -9845,13 +9846,13 @@ def __init__( self.signature = signature -class SolanaSignMessage(protobuf.MessageType): +class SolanaSignOffChainMessage(protobuf.MessageType): MESSAGE_WIRE_TYPE = 10104 FIELDS = { 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), 2: protobuf.Field("message", "bytes", repeated=False, required=True), - 3: protobuf.Field("message_version", "SolanaMessageVersion", repeated=False, required=False), - 4: protobuf.Field("message_format", "SolanaMessageFormat", repeated=False, required=False), + 3: protobuf.Field("message_version", "SolanaOffChainMessageVersion", repeated=False, required=False), + 4: protobuf.Field("message_format", "SolanaOffChainMessageFormat", repeated=False, required=False), 5: protobuf.Field("application_domain", "bytes", repeated=False, required=False), } @@ -9860,8 +9861,8 @@ def __init__( *, message: "bytes", address_n: Optional[Sequence["int"]] = None, - message_version: Optional["SolanaMessageVersion"] = SolanaMessageVersion.MESSAGE_VERSION_0, - message_format: Optional["SolanaMessageFormat"] = SolanaMessageFormat.V0_RESTRICTED_ASCII, + message_version: Optional["SolanaOffChainMessageVersion"] = SolanaOffChainMessageVersion.MESSAGE_VERSION_0, + message_format: Optional["SolanaOffChainMessageFormat"] = SolanaOffChainMessageFormat.V0_RESTRICTED_ASCII, application_domain: Optional["bytes"] = None, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] @@ -9871,18 +9872,35 @@ def __init__( self.application_domain = application_domain -class SolanaSignedMessage(protobuf.MessageType): +class SolanaSignUnsafeMessage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 10106 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), + 2: protobuf.Field("message", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + message: "bytes", + address_n: Optional[Sequence["int"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.message = message + + +class SolanaMessageSignature(protobuf.MessageType): MESSAGE_WIRE_TYPE = 10105 FIELDS = { 1: protobuf.Field("signature", "bytes", repeated=False, required=True), - 2: protobuf.Field("public_key", "bytes", repeated=False, required=True), + 2: protobuf.Field("public_key", "bytes", repeated=False, required=False), } def __init__( self, *, signature: "bytes", - public_key: "bytes", + public_key: Optional["bytes"] = None, ) -> None: self.signature = signature self.public_key = public_key diff --git a/python/src/trezorlib/solana.py b/python/src/trezorlib/solana.py index fe216ec180..9224f5c509 100644 --- a/python/src/trezorlib/solana.py +++ b/python/src/trezorlib/solana.py @@ -29,16 +29,16 @@ def sign_tx( ) return client.call(msg) -@expect(messages.SolanaSignedMessage) -def sign_message( +@expect(messages.SolanaMessageSignature) +def sign_offchain_message( client: "TrezorClient", n: "Address", message: bytes, - message_version: messages.SolanaMessageVersion, - message_format: messages.SolanaMessageFormat, + message_version: messages.SolanaOffChainMessageVersion, + message_format: messages.SolanaOffChainMessageFormat, application_domain: Optional[str] = None ): - msg = messages.SolanaSignMessage( + msg = messages.SolanaSignOffChainMessage( message=message, address_n=n, message_version=message_version, @@ -46,3 +46,15 @@ def sign_message( application_domain=bytes.fromhex(application_domain) if application_domain else None, ) return client.call(msg) + +@expect(messages.SolanaMessageSignature) +def sign_unsafe_message( + client: "TrezorClient", + n: "Address", + message: bytes, +): + msg = messages.SolanaSignUnsafeMessage( + message=message, + address_n=n, + ) + return client.call(msg)