feat(solana): add off-chain message signing
What changed, and why it matters
This commit adds a new Trezor feature: signing and verifying Solana off-chain messages. It is a feature implementation, not a fix for a known vulnerability. The code introduces new message types, parsing logic, user confirmation screens, and client helpers. There is no direct evidence in the commit of a security bug, but any new signing path increases the attack surface and must be reviewed carefully for parsing mistakes or user-interface spoofing.
Treat this as a security-sensitive feature addition. Conduct a focused review of the OCMS parser for length/offset/underflow bugs, confirm that user prompts are unspoofable and clearly distinguish sign vs. verify, verify that the derived key must match one of the declared signers, and ensure the Ed25519 signing is performed over the exact raw bytes the user confirmed. Fuzz the parser and run static analysis on the new modules before release.
Security signals we found
New signing code path added for Solana off-chain messages
Parsing of attacker-controlled binary message/envelope format introduced
User confirmation flow added for off-chain message signing and verification
Deviation from Solana OCMS v0 spec: all formats are clear-signed instead of blind-signed
No vendor disclosure of a security issue in commit message or changelog
Evidence from the diff
The commit implements Solana Off-Chain Message Signing (OCMS) v0. It adds protobuf messages SolanaSignMessage, SolanaMessageSignature, and SolanaVerifyMessage; firmware modules offchain_message.py, sign_message.py, verify_message.py; layout confirmation helpers; and corresponding trezorlib/Rust client code. The firmware parses the OCMS domain-prefixed message, validates version/format/length/signer count, derives the requested key, confirms the message on device, and produces an Ed25519 signature. Verification parses an envelope of signatures plus the message and checks Ed25519 signatures before showing success. The implementation intentionally deviates from the v0 spec by clear-signing all formats rather than blind-signing UTF-8 short/long messages.
Changed components
core/src/apps/solana/offchain_message.pycore/src/apps/solana/sign_message.pycore/src/apps/solana/verify_message.pycore/src/apps/solana/layout.pycommon/protob/messages-solana.protopython/src/trezorlib/solana.pypython/src/trezorlib/cli/solana.pyInspect captured patch +1439 / −83
diff --git a/common/protob/messages-solana.proto b/common/protob/messages-solana.proto
index 0eef808e..5e26ad97 100644
--- a/common/protob/messages-solana.proto
+++ b/common/protob/messages-solana.proto
@@ -85,3 +85,34 @@ message SolanaSignTx {
message SolanaTxSignature {
required bytes signature = 1; // tx signature
}
+
+/**
+ * Request: Ask device to sign a Solana off-chain message
+ * @start
+ * @next SolanaMessageSignature
+ * @next Failure
+ */
+message SolanaSignMessage {
+ repeated uint32 address_n = 1; // BIP-32 path to derive the key to sign with
+ required bytes message = 2; // message to be signed, in OCMS format
+ optional bool chunkify = 3; // display the address in chunks of 4 characters
+}
+
+/**
+ * Response: Contains the message signature
+ * @end
+ */
+message SolanaMessageSignature {
+ required bytes signature = 1; // message signature
+}
+
+/**
+ * Request: Ask device to verify a Solana off-chain message signature(s)
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message SolanaVerifyMessage {
+ required bytes envelope = 1; // OCMS envelope containing the signatures and the message
+ optional bool chunkify = 2; // display the address in chunks of 4 characters
+}
diff --git a/common/protob/messages.proto b/common/protob/messages.proto
index f8083336..15e636a2 100644
--- a/common/protob/messages.proto
+++ b/common/protob/messages.proto
@@ -340,6 +340,9 @@ enum MessageType {
MessageType_SolanaAddress = 903 [(wire_out) = true];
MessageType_SolanaSignTx = 904 [(wire_in) = true];
MessageType_SolanaTxSignature = 905 [(wire_out) = true];
+ MessageType_SolanaSignMessage = 906 [(wire_in) = true];
+ MessageType_SolanaMessageSignature = 907 [(wire_out) = true];
+ MessageType_SolanaVerifyMessage = 908 [(wire_in) = true];
// THP
MessageType_ThpCreateNewSession = 1000 [(wire_in) = true, (bitcoin_only) = true];
diff --git a/core/.changelog.d/6759.added b/core/.changelog.d/6759.added
new file mode 100644
index 00000000..9f96b869
--- /dev/null
+++ b/core/.changelog.d/6759.added
@@ -0,0 +1 @@
+Support for Solana off-chain message signing (OCMS) v0.
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index cf16a648..cd1d855d 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -1405,6 +1405,7 @@ static void _librust_qstrs(void) {
MP_QSTR_ripple__destination_tag_missing;
MP_QSTR_ripple__destination_tag_template;
MP_QSTR_solana__account_index;
+ MP_QSTR_solana__app_domain;
MP_QSTR_solana__associated_token_account;
MP_QSTR_solana__base_fee;
MP_QSTR_solana__claim;
@@ -1420,6 +1421,7 @@ static void _librust_qstrs(void) {
MP_QSTR_solana__max_fees_rent;
MP_QSTR_solana__max_rent_fee;
MP_QSTR_solana__multiple_signers;
+ MP_QSTR_solana__offchain_is_multisig;
MP_QSTR_solana__priority_fee;
MP_QSTR_solana__stake;
MP_QSTR_solana__stake_account;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index d5b2cf34..c43ccc94 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1623,6 +1623,10 @@ pub enum TranslatedString {
words__account_deletion = 1228, // "Account deletion"
words__tap_to_wake = 1229, // "Tap to wake"
words__made_in = 1230, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "Made in"}
+ #[cfg(feature = "universal_fw")]
+ solana__app_domain = 1231, // "Application domain"
+ #[cfg(feature = "universal_fw")]
+ solana__offchain_is_multisig = 1232, // "The following message requires multiple signers."
}
impl TranslatedString {
@@ -2861,6 +2865,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -4097,6 +4103,8 @@ impl TranslatedString {
19852,
19863,
19863,
+ 19881,
+ 19929,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -5332,6 +5340,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -6568,6 +6578,8 @@ impl TranslatedString {
19852,
19863,
19863,
+ 19881,
+ 19929,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -7803,6 +7815,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -9039,6 +9053,8 @@ impl TranslatedString {
19852,
19863,
19863,
+ 19881,
+ 19929,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -10274,6 +10290,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -11510,6 +11528,8 @@ impl TranslatedString {
19852,
19863,
19863,
+ 19881,
+ 19929,
];
} else if #[cfg(feature = "layout_caesar")] {
@@ -12746,6 +12766,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -13982,6 +14004,8 @@ impl TranslatedString {
17786,
17797,
17797,
+ 17815,
+ 17863,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -15217,6 +15241,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -16453,6 +16479,8 @@ impl TranslatedString {
17786,
17797,
17797,
+ 17815,
+ 17863,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -17688,6 +17716,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -18924,6 +18954,8 @@ impl TranslatedString {
17786,
17797,
17797,
+ 17815,
+ 17863,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -20159,6 +20191,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -21395,6 +21429,8 @@ impl TranslatedString {
17786,
17797,
17797,
+ 17815,
+ 17863,
];
} else if #[cfg(feature = "layout_delizia")] {
@@ -22631,6 +22667,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -23867,6 +23905,8 @@ impl TranslatedString {
19206,
19217,
19217,
+ 19235,
+ 19283,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -25102,6 +25142,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -26338,6 +26380,8 @@ impl TranslatedString {
19206,
19217,
19217,
+ 19235,
+ 19283,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -27573,6 +27617,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -28809,6 +28855,8 @@ impl TranslatedString {
19206,
19217,
19217,
+ 19235,
+ 19283,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -30044,6 +30092,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -31280,6 +31330,8 @@ impl TranslatedString {
19206,
19217,
19217,
+ 19235,
+ 19283,
];
} else if #[cfg(feature = "layout_eckhart")] {
@@ -32516,6 +32568,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"Made in",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -33752,6 +33806,8 @@ impl TranslatedString {
20553,
20564,
20571,
+ 20589,
+ 20637,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -34987,6 +35043,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"Made in",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -36223,6 +36281,8 @@ impl TranslatedString {
20553,
20564,
20571,
+ 20589,
+ 20637,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -37458,6 +37518,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"Made in",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -38694,6 +38756,8 @@ impl TranslatedString {
20553,
20564,
20571,
+ 20589,
+ 20637,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -39929,6 +39993,8 @@ impl TranslatedString {
"Account deletion",
"Tap to wake",
"Made in",
+ "Application domain",
+ "The following message requires multiple signers.",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -41165,6 +41231,8 @@ impl TranslatedString {
20553,
20564,
20571,
+ 20589,
+ 20637,
];
}
@@ -42384,6 +42452,8 @@ impl TranslatedString {
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_solana__account_index, Self::solana__account_index),
#[cfg(feature = "universal_fw")]
+ (Qstr::MP_QSTR_solana__app_domain, Self::solana__app_domain),
+ #[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_solana__associated_token_account, Self::solana__associated_token_account),
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_solana__base_fee, Self::solana__base_fee),
@@ -42414,6 +42484,8 @@ impl TranslatedString {
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_solana__multiple_signers, Self::solana__multiple_signers),
#[cfg(feature = "universal_fw")]
+ (Qstr::MP_QSTR_solana__offchain_is_multisig, Self::solana__offchain_is_multisig),
+ #[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_solana__priority_fee, Self::solana__priority_fee),
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_solana__stake, Self::solana__stake),
diff --git a/core/embed/upymod/qstrdefsport.h b/core/embed/upymod/qstrdefsport.h
index 6116d1ba..374a539b 100644
--- a/core/embed/upymod/qstrdefsport.h
+++ b/core/embed/upymod/qstrdefsport.h
@@ -648,7 +648,9 @@ Q(apps.solana.format)
Q(apps.solana.get_address)
Q(apps.solana.get_public_key)
Q(apps.solana.layout)
+Q(apps.solana.offchain_message)
Q(apps.solana.predefined_transaction)
+Q(apps.solana.sign_message)
Q(apps.solana.sign_tx)
Q(apps.solana.token_account)
Q(apps.solana.transaction)
@@ -656,6 +658,7 @@ Q(apps.solana.transaction.instruction)
Q(apps.solana.transaction.instructions)
Q(apps.solana.transaction.parse)
Q(apps.solana.types)
+Q(apps.solana.verify_message)
Q(apps.stellar)
Q(apps.stellar.consts)
Q(apps.stellar.get_address)
@@ -753,6 +756,7 @@ Q(nem)
Q(nem_mosaics)
Q(network_ids)
Q(networks)
+Q(offchain_message)
Q(offloading_keys)
Q(operations)
Q(ordinary_signer)
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index fe72c83a..d035c23d 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -878,6 +878,7 @@ class TR:
sn__action: str = "Allow connected device to get serial number of your Trezor Safe 7?"
sn__title: str = "Serial number"
solana__account_index: str = "Account index"
+ solana__app_domain: str = "Application domain"
solana__associated_token_account: str = "Associated token account"
solana__base_fee: str = "Base fee"
solana__claim: str = "Claim"
@@ -893,6 +894,7 @@ class TR:
solana__max_fees_rent: str = "Max fees and rent"
solana__max_rent_fee: str = "Max rent fee"
solana__multiple_signers: str = "Multiple signers"
+ solana__offchain_is_multisig: str = "The following message requires multiple signers."
solana__priority_fee: str = "Priority fee"
solana__stake: str = "Stake"
solana__stake_account: str = "Stake account"
diff --git a/core/src/apps/solana/README.md b/core/src/apps/solana/README.md
index 39ce449d..219a35b9 100644
--- a/core/src/apps/solana/README.md
+++ b/core/src/apps/solana/README.md
@@ -194,3 +194,8 @@ Assuming that all the information presented to the user is confirmed and accepte
The presence of multisig instructions within the Token program adds an additional layer of complexity to the Solana message signing app. Multisig instructions involve owner accounts that are multisignature accounts, which means they require multiple signatures to authorize a transaction. In the context of Solana, this impacts the determination of the transaction fee since the fee is calculated based on the number of required signatures.
+## Off-Chain Message Signing
+
+We support signing and verifying off-chain messages as defined by the [Solana off-chain message signing spec v0](https://docs.anza.xyz/proposals/off-chain-message-signing). The firmware expects messages already formatted according to the spec (i.e., prefixed with `\xffsolana offchain`); [trezorlib](../../../../python/src/trezorlib/solana.py) provides utilities to do so. For verification, the firmware expects the message wrapped in an [envelope](https://docs.anza.xyz/proposals/off-chain-message-signing#envelope) (signatures concatenated with the formatted message). Also see [messages-solana.proto](../../../../common/protob/messages-solana.proto).
+
+The spec requires that HW wallets clear-sign format 0 (ASCII), blind-sign format 1 (short UTF-8), and reject format 2 (long UTF-8) messages. We deviate from this and clear-sign all formats, since Trezor can display longer messages including non-ASCII characters, making the restriction arbitrary. This limitation is also removed in the newer [v1 spec](https://github.com/solana-foundation/SRFCs/discussions/3).
diff --git a/core/src/apps/solana/constants.py b/core/src/apps/solana/constants.py
index 9bafad79..768a4e7e 100644
--- a/core/src/apps/solana/constants.py
+++ b/core/src/apps/solana/constants.py
@@ -1,6 +1,7 @@
from micropython import const
ADDRESS_SIZE = const(32)
+SIGNATURE_SIZE = const(64)
SOLANA_BASE_FEE_LAMPORTS = const(5000)
SOLANA_COMPUTE_UNIT_LIMIT = const(200000)
diff --git a/core/src/apps/solana/layout.py b/core/src/apps/solana/layout.py
index dc7f411f..ea735b3a 100644
--- a/core/src/apps/solana/layout.py
+++ b/core/src/apps/solana/layout.py
@@ -25,6 +25,7 @@ if TYPE_CHECKING:
from trezor.ui.layouts import PropertyType, StrPropertyType
from .definitions import Definitions
+ from .offchain_message import OffchainMessage
from .transaction import Fee
from .transaction.instructions import Instruction, SystemProgramTransferInstruction
from .types import AddressReference
@@ -610,3 +611,59 @@ async def confirm_payment_request(
fee_str,
fee_items,
)
+
+
+async def confirm_offchain_signverify(
+ offchain_message: OffchainMessage,
+ verify: bool,
+ signer_index: int = 0,
+ signer_path: list[int] | None = None,
+ chunkify: bool = False,
+) -> None:
+ from trezor.ui.layouts import confirm_address, confirm_signverify
+
+ from .offchain_message import OffchainMessageV0
+
+ if isinstance(offchain_message, OffchainMessageV0):
+ await confirm_address(
+ TR.solana__app_domain,
+ base58.encode(offchain_message.app),
+ verb=TR.buttons__continue,
+ chunkify=chunkify,
+ )
+
+ if len(offchain_message.signers) > 1:
+ await confirm_metadata(
+ "confirm_multisig",
+ TR.solana__confirm_multisig,
+ TR.solana__offchain_is_multisig,
+ br_code=ButtonRequestType.Other,
+ )
+
+ # TODO: consider using confirm_properties for multisig
+ for i, signer in enumerate(offchain_message.signers):
+ if i != signer_index:
+ await confirm_address(
+ TR.address__title_cosigner,
+ base58.encode(signer),
+ verb=TR.buttons__continue,
+ chunkify=chunkify,
+ )
+
+ signer_address = base58.encode(offchain_message.signers[signer_index])
+
+ if signer_path is not None:
+ signer_path_str = address_n_to_str(signer_path)
+ signer_account = _format_path(signer_path)
+ else:
+ signer_path_str = None
+ signer_account = None
+
+ await confirm_signverify(
+ offchain_message.message,
+ signer_address,
+ verify=verify,
+ path=signer_path_str,
+ account=signer_account,
+ chunkify=chunkify,
+ )
diff --git a/core/src/apps/solana/offchain_message.py b/core/src/apps/solana/offchain_message.py
new file mode 100644
index 00000000..d1cc4ac6
--- /dev/null
+++ b/core/src/apps/solana/offchain_message.py
@@ -0,0 +1,143 @@
+from micropython import const
+from typing import TYPE_CHECKING, Sequence
+
+from trezor.utils import BufferReader
+from trezor.wire import DataError
+
+if TYPE_CHECKING:
+ from buffer_types import AnyBytes
+ from enum import IntEnum
+
+ from typing_extensions import Self
+else:
+ IntEnum = object
+
+
+class OffchainMessage:
+ SIGNING_DOMAIN = b"\xffsolana offchain"
+
+ def __init__(self, signers: Sequence[AnyBytes], message: str) -> None:
+ self.signers = signers
+ self.message = message
+
+ @classmethod
+ def _from_reader(cls, reader: BufferReader) -> "OffchainMessage":
+ if reader.read_memoryview(len(cls.SIGNING_DOMAIN)) != cls.SIGNING_DOMAIN:
+ raise DataError("Invalid signing domain")
+
+ version = reader.get()
+ if version == 0:
+ return OffchainMessageV0._from_reader(reader)
+ raise DataError(f"Unsupported version: {version}")
+
+ @classmethod
+ def from_bytes(cls, data: AnyBytes) -> "OffchainMessage":
+ try:
+ return cls._from_reader(BufferReader(data))
+ except EOFError:
+ raise DataError("Message too short")
+
+
+class OffchainMessageV0(OffchainMessage):
+
+ APP_DOMAIN_LEN = const(32)
+ MAX_COMBINED_MSG_LEN_SHORT = const(1232)
+ MAX_COMBINED_MSG_LEN = const(65535)
+
+ def __init__(
+ self, signers: Sequence[AnyBytes], message: str, app: AnyBytes, format: int
+ ) -> None:
+ super().__init__(signers, message)
+ self.app = app
+ self.format = format
+
+ class MessageFormat(IntEnum):
+ ASCII_SHORT = const(0)
+ UTF8_SHORT = const(1)
+ UTF8_LONG = const(2)
+
+ @classmethod
+ def max_len(cls, format: int) -> int:
+ if format in (cls.ASCII_SHORT, cls.UTF8_SHORT):
+ return OffchainMessageV0.MAX_COMBINED_MSG_LEN_SHORT
+ if format == cls.UTF8_LONG:
+ return OffchainMessageV0.MAX_COMBINED_MSG_LEN
+ raise DataError(f"Invalid message format: {format}")
+
+ @classmethod
+ def _from_reader(cls, reader: BufferReader) -> Self:
+ from apps.common.readers import read_uint16_le
+ from apps.common.signverify import is_printable_ascii
+
+ from .constants import ADDRESS_SIZE
+
+ app = reader.read_memoryview(cls.APP_DOMAIN_LEN)
+
+ format = reader.get()
+ combined_len = len(reader.buffer)
+ if combined_len > cls.MessageFormat.max_len(format):
+ raise DataError("Message is too long")
+
+ n_signers = reader.get()
+ if n_signers == 0:
+ raise DataError("At least one signer is required")
+ signers = [reader.read_memoryview(ADDRESS_SIZE) for _ in range(n_signers)]
+
+ message_len = read_uint16_le(reader)
+ if message_len == 0:
+ raise DataError("Message cannot be empty")
+ message_bytes = reader.read_memoryview(message_len)
+ if reader.remaining_count() != 0:
+ raise DataError("Invalid message length")
+ if format == cls.MessageFormat.ASCII_SHORT and not is_printable_ascii(
+ message_bytes
+ ):
+ raise DataError("Invalid message format")
+ try:
+ message = str(message_bytes, "utf-8")
+ except UnicodeError:
+ raise DataError("Invalid message encoding")
+
+ return cls(signers, message, app, format)
+
+
+class Envelope:
+ def __init__(
+ self,
+ signatures: Sequence[AnyBytes],
+ message_bytes: AnyBytes,
+ ) -> None:
+ self.signatures = signatures
+ self.message = OffchainMessage.from_bytes(message_bytes)
+ # we keep the raw bytes around for signature verification
+ self._message_bytes = message_bytes
+
+ if len(self.signatures) != len(self.message.signers):
+ raise DataError("Number of signatures must match number of signers")
+
+ @classmethod
+ def _from_reader(cls, reader: BufferReader) -> Self:
+ from .constants import SIGNATURE_SIZE
+
+ n_signatures = reader.get()
+ signatures = [
+ reader.read_memoryview(SIGNATURE_SIZE) for _ in range(n_signatures)
+ ]
+
+ message_bytes = reader.read_memoryview()
+
+ return cls(signatures, message_bytes)
+
+ @classmethod
+ def from_bytes(cls, data: AnyBytes) -> Self:
+ try:
+ return cls._from_reader(BufferReader(data))
+ except EOFError:
+ raise DataError("Envelope too short")
+
+ def verify(self) -> None:
+ from trezor.crypto.curve import ed25519
+
+ for signature, signer in zip(self.signatures, self.message.signers):
+ if not ed25519.verify(signer, signature, self._message_bytes):
+ raise DataError("Invalid signature")
diff --git a/core/src/apps/solana/sign_message.py b/core/src/apps/solana/sign_message.py
new file mode 100644
index 00000000..228ebb81
--- /dev/null
+++ b/core/src/apps/solana/sign_message.py
@@ -0,0 +1,44 @@
+from typing import TYPE_CHECKING
+
+from apps.common.keychain import with_slip44_keychain
+
+from . import CURVE, PATTERNS, SLIP44_ID
+
+if TYPE_CHECKING:
+ from trezor.messages import SolanaMessageSignature, SolanaSignMessage
+
+ from apps.common.keychain import Keychain
+
+
+@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE)
+async def sign_message(
+ msg: SolanaSignMessage, keychain: Keychain
+) -> SolanaMessageSignature:
+ from trezor.crypto.curve import ed25519
+ from trezor.messages import SolanaMessageSignature
+ from trezor.wire import DataError
+
+ from apps.common import seed
+
+ from .layout import confirm_offchain_signverify
+ from .offchain_message import OffchainMessage
+
+ offchain_message = OffchainMessage.from_bytes(msg.message)
+
+ node = keychain.derive(msg.address_n)
+ public_key = seed.remove_ed25519_prefix(node.public_key())
+
+ if public_key not in offchain_message.signers:
+ raise DataError("Requested key not among signers")
+ signer_index = offchain_message.signers.index(public_key)
+
+ await confirm_offchain_signverify(
+ offchain_message,
+ verify=False,
+ signer_index=signer_index,
+ signer_path=msg.address_n,
+ chunkify=bool(msg.chunkify),
+ )
+
+ signature = ed25519.sign(node.private_key(), msg.message)
+ return SolanaMessageSignature(signature=signature)
diff --git a/core/src/apps/solana/verify_message.py b/core/src/apps/solana/verify_message.py
new file mode 100644
index 00000000..9f35fa2c
--- /dev/null
+++ b/core/src/apps/solana/verify_message.py
@@ -0,0 +1,25 @@
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from trezor.messages import SolanaVerifyMessage, Success
+
+
+async def verify_message(msg: SolanaVerifyMessage) -> Success:
+ from trezor import TR
+ from trezor.messages import Success
+ from trezor.ui.layouts import show_success
+
+ from .layout import confirm_offchain_signverify
+ from .offchain_message import Envelope
+
+ envelope = Envelope.from_bytes(msg.envelope)
+
+ envelope.verify()
+
+ await confirm_offchain_signverify(
+ envelope.message, verify=True, chunkify=bool(msg.chunkify)
+ )
+
+ # TODO: add a generic (not specific to any coin) valid signature message
+ await show_success("verify_message", TR.ethereum__valid_signature)
+ return Success(message="Signature ok")
diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py
index 44c1f3ca..35a3b300 100644
--- a/core/src/apps/workflow_handlers.py
+++ b/core/src/apps/workflow_handlers.py
@@ -235,6 +235,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.SolanaVerifyMessage:
+ return "apps.solana.verify_message"
raise ValueError
diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py
index 2e5ced09..bd08cd65 100644
--- a/core/src/trezor/enums/MessageType.py
+++ b/core/src/trezor/enums/MessageType.py
@@ -279,6 +279,9 @@ if not utils.BITCOIN_ONLY:
SolanaAddress = 903
SolanaSignTx = 904
SolanaTxSignature = 905
+ SolanaSignMessage = 906
+ SolanaMessageSignature = 907
+ SolanaVerifyMessage = 908
NostrGetPubkey = 2001
NostrPubkey = 2002
NostrSignEvent = 2003
diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py
index 97bf4cbe..0df2d151 100644
--- a/core/src/trezor/enums/__init__.py
+++ b/core/src/trezor/enums/__init__.py
@@ -686,6 +686,9 @@ if TYPE_CHECKING:
SolanaAddress = 903
SolanaSignTx = 904
SolanaTxSignature = 905
+ SolanaSignMessage = 906
+ SolanaMessageSignature = 907
+ SolanaVerifyMessage = 908
ThpCreateNewSession = 1000
ThpCredentialRequest = 1016
ThpCredentialResponse = 1017
diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py
index 0871e11d..d44bb1fa 100644
--- a/core/src/trezor/messages.py
+++ b/core/src/trezor/messages.py
@@ -6056,6 +6056,54 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxSignature"]:
return isinstance(msg, cls)
+ class SolanaSignMessage(protobuf.MessageType):
+ address_n: "list[int]"
+ message: "AnyBytes"
+ chunkify: "bool | None"
+
+ def __init__(
+ self,
+ *,
+ message: "AnyBytes",
+ address_n: "list[int] | None" = None,
+ chunkify: "bool | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignMessage"]:
+ return isinstance(msg, cls)
+
+ class SolanaMessageSignature(protobuf.MessageType):
+ signature: "AnyBytes"
+
+ def __init__(
+ self,
+ *,
+ signature: "AnyBytes",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["SolanaMessageSignature"]:
+ return isinstance(msg, cls)
+
+ class SolanaVerifyMessage(protobuf.MessageType):
+ envelope: "AnyBytes"
+ chunkify: "bool | None"
+
+ def __init__(
+ self,
+ *,
+ envelope: "AnyBytes",
+ chunkify: "bool | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["SolanaVerifyMessage"]:
+ return isinstance(msg, cls)
+
class StellarAsset(protobuf.MessageType):
type: "StellarAssetType"
code: "str | None"
diff --git a/core/translations/en.json b/core/translations/en.json
index a21fb7cc..7bed7806 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -2595,6 +2595,7 @@
"Eckhart": "Serial number"
},
"solana__account_index": "Account index",
+ "solana__app_domain": "Application domain",
"solana__associated_token_account": "Associated token account",
"solana__base_fee": "Base fee",
"solana__claim": "Claim",
@@ -2610,6 +2611,7 @@
"solana__max_fees_rent": "Max fees and rent",
"solana__max_rent_fee": "Max rent fee",
"solana__multiple_signers": "Multiple signers",
+ "solana__offchain_is_multisig": "The following message requires multiple signers.",
"solana__priority_fee": "Priority fee",
"solana__stake": "Stake",
"solana__stake_account": "Stake account",
diff --git a/core/translations/order.json b/core/translations/order.json
index 91187c93..ac1a7589 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1229,5 +1229,7 @@
"1227": "ripple__account_to_be_deleted",
"1228": "words__account_deletion",
"1229": "words__tap_to_wake",
- "1230": "words__made_in"
+ "1230": "words__made_in",
+ "1231": "solana__app_domain",
+ "1232": "solana__offchain_is_multisig"
}
diff --git a/python/.changelog.d/6759.added b/python/.changelog.d/6759.added
new file mode 100644
index 00000000..579df99d
--- /dev/null
+++ b/python/.changelog.d/6759.added
@@ -0,0 +1 @@
+Support for [Solana off-chain message signing v0](https://docs.anza.xyz/proposals/off-chain-message-signing).
diff --git a/python/src/trezorlib/cli/solana.py b/python/src/trezorlib/cli/solana.py
index 3769af10..c284e116 100644
--- a/python/src/trezorlib/cli/solana.py
+++ b/python/src/trezorlib/cli/solana.py
@@ -136,3 +136,118 @@ def sign_tx(
additional_info_msg,
)
return signature.hex()
+
+
+DEFAULT_APP_DOMAIN = b"\x00" * solana.OffchainMessageV0.APP_LEN
+
+
+@cli.command()
+@click.argument("message", type=str)
+@click.option("-n", "--address", default=DEFAULT_PATH, help=PATH_HELP)
+@click.option("-t", "--text", is_flag=True, help="Interpret message as text")
+@click.option("-a", "--app", type=str, help="Application domain (base58, 32 B)")
+@click.option(
+ "-s",
+ "--signer",
+ type=str,
+ multiple=True,
+ help="Public key of an additional signer (base58)",
+)
+@click.option("-C", "--chunkify", is_flag=True)
+@with_session
+def sign_message(
+ session: "Session",
+ address: str,
+ message: str,
+ text: bool,
+ app: str | None,
+ signer: tuple[str, ...],
+ chunkify: bool,
+) -> str:
+ """Sign a Solana off-chain message.
+
+ There are two ways to specify the MESSAGE argument:
+
+ 1. Raw mode (Default)
+
+ sign-message [--address PATH] MESSAGE
+
+ The provided MESSAGE is treated as a hex string representing a
+ pre-formatted off-chain message. Only the --address option is
+ allowed in this mode [1]. The specified address must correspond
+ to one of the public keys in the message.
+
+ 2. Format mode
+
+ sign-message [--address PATH] [--app APP] [--signer SIGNER]... --text MESSAGE
+
+ The provided MESSAGE is treated as a plain text string. The
+ formatted message will be constructed based on the supplied
+ arguments. The --signer option can be used multiple times. The
+ final list of signers will consist of the public key derived
+ from --address, plus any public keys provided via --signer.
+
+ Also see: https://docs.anza.xyz/proposals/off-chain-message-signing
+
+ [1] The --chunkify flag is always applicable.
+ """
+
+ address_n = tools.parse_path(address)
+
+ if text:
+ app_bytes = tools.b58decode(app) if app else DEFAULT_APP_DOMAIN
+
+ signers = [solana.get_public_key(session, address_n, show_display=False)]
+ signers.extend(tools.b58decode(s) for s in signer)
+
+ offchain_msg = solana.OffchainMessageV0(app_bytes, signers, message).to_bytes()
+ else:
+ if app or signer:
+ raise click.UsageError("Only the --address option can be used in raw mode")
+
+ offchain_msg = bytes.fromhex(message)
+
+ click.echo(f"Message: {offchain_msg.hex()}")
+
+ signature = solana.sign_message(session, address_n, offchain_msg, chunkify=chunkify)
+ return signature.hex()
+
+
+@cli.command()
+@click.argument("message", type=str)
+@click.option("-s", "--signature", type=str, multiple=True, help="Signature (hex)")
+@click.option("-C", "--chunkify", is_flag=True)
+@with_session
+def verify_message(
+ session: "Session",
+ message: str,
+ signature: tuple[str, ...],
+ chunkify: bool,
+) -> bool:
+ """Verify a signed Solana off-chain message.
+
+ This command operates in two modes depending on whether the
+ --signature option is provided; in either case, the provided MESSAGE
+ must be a hex string:
+
+ If the --signature option is not used, the provided MESSAGE must
+ be wrapped in an envelope containing signatures from all signers
+ specified in the message header.
+
+ If the --signature option is used, the provided MESSAGE must be a
+ pre-formatted off-chain message without an envelope. You must provide
+ all signatures in the correct order as specified by the message header
+ (by using the --signature option multiple times if necessary). The
+ envelope will be constructed automatically.
+
+ Also see: https://docs.anza.xyz/proposals/off-chain-message-signing#envelope
+ """
+
+ if signature:
+ signatures = [bytes.fromhex(s) for s in signature]
+ msg_bytes = bytes.fromhex(message)
+ envelope = solana.Envelope(signatures, msg_bytes).to_bytes()
+ else:
+ envelope = bytes.fromhex(message)
+
+ return solana.verify_message(session, envelope, chunkify=chunkify)
diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py
index 176cb4e2..f310fa0f 100644
--- a/python/src/trezorlib/messages.py
+++ b/python/src/trezorlib/messages.py
@@ -746,6 +746,9 @@ class MessageType(IntEnum):
SolanaAddress = 903
SolanaSignTx = 904
SolanaTxSignature = 905
+ SolanaSignMessage = 906
+ SolanaMessageSignature = 907
+ SolanaVerifyMessage = 908
ThpCreateNewSession = 1000
ThpCredentialRequest = 1016
ThpCredentialResponse = 1017
@@ -7835,6 +7838,57 @@ class SolanaTxSignature(protobuf.MessageType):
self.signature = signature
+class SolanaSignMessage(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = 906
+ FIELDS = {
+ 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None),
+ 2: protobuf.Field("message", "bytes", repeated=False, required=True),
+ 3: protobuf.Field("chunkify", "bool", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ message: "bytes",
+ address_n: Optional[Sequence["int"]] = None,
+ chunkify: Optional["bool"] = None,
+ ) -> None:
+ self.address_n: Sequence["int"] = address_n if address_n is not None else []
+ self.message = message
+ self.chunkify = chunkify
+
+
+class SolanaMessageSignature(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = 907
+ FIELDS = {
+ 1: protobuf.Field("signature", "bytes", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ signature: "bytes",
+ ) -> None:
+ self.signature = signature
+
+
+class SolanaVerifyMessage(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = 908
+ FIELDS = {
+ 1: protobuf.Field("envelope", "bytes", repeated=False, required=True),
+ 2: protobuf.Field("chunkify", "bool", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ envelope: "bytes",
+ chunkify: Optional["bool"] = None,
+ ) -> None:
+ self.envelope = envelope
+ self.chunkify = chunkify
+
+
class StellarAsset(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
diff --git a/python/src/trezorlib/solana.py b/python/src/trezorlib/solana.py
index 45624fe7..08a6e2d2 100644
--- a/python/src/trezorlib/solana.py
+++ b/python/src/trezorlib/solana.py
@@ -14,9 +14,11 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
-from typing import TYPE_CHECKING, Any, List, Optional
+from abc import ABC, abstractmethod
+from enum import IntEnum
+from typing import TYPE_CHECKING, Any, ClassVar, List, Optional
-from . import messages
+from . import exceptions, messages
from .tools import workflow
if TYPE_CHECKING:
@@ -73,3 +75,124 @@ def sign_tx(
),
expect=messages.SolanaTxSignature,
).signature
+
+
+class OffchainMessage(ABC):
+ """Off-Chain Message Signing Base Class
+
+ This implementation below is optimistic: for incorrect arguments,
+ it will produce incorrect message. We leave it up to the wallet
+ to verify the message conforms to the spec.
+ """
+
+ SIGNING_DOMAIN = b"\xffsolana offchain"
+ VERSION: ClassVar[int]
+
+ def __init__(self, signers: list[bytes], message: str) -> None:
+ self.signers = signers
+ self.message = message
+
+ @abstractmethod
+ def _write_preamble(self, buf: bytearray) -> None: ...
+
+ def to_bytes(self) -> bytes:
+ buf = bytearray(self.SIGNING_DOMAIN)
+ buf.append(self.VERSION)
+ self._write_preamble(buf)
+ buf.extend(self.message.encode("utf-8"))
+ return bytes(buf)
+
+
+class OffchainMessageV0(OffchainMessage):
+ """Off-Chain Message Signing v0
+
+ https://docs.anza.xyz/proposals/off-chain-message-signing
+ """
+
+ VERSION = 0
+
+ APP_LEN = 32
+ PUB_KEY_LEN = 32
+ MAX_MSG_LEN_SHORT = 1232
+
+ def __init__(self, app: bytes, signers: list[bytes], message: str) -> None:
+ super().__init__(signers, message)
+ self.app = app
+
+ class MessageFormat(IntEnum):
+ RESTRICTED_ASCII = 0
+ UTF8_SHORT = 1
+ UTF8_LONG = 2
+
+ def message_format(self) -> MessageFormat:
+ preamble_len = 0x35 + len(self.signers) * self.PUB_KEY_LEN
+ combined_len = preamble_len + len(self.message.encode("utf-8"))
+
+ if combined_len <= self.MAX_MSG_LEN_SHORT:
+ if all(0x20 <= ord(c) <= 0x7E for c in self.message):
+ return self.MessageFormat.RESTRICTED_ASCII
+ return self.MessageFormat.UTF8_SHORT
+ return self.MessageFormat.UTF8_LONG
+
+ def _write_preamble(self, buf: bytearray) -> None:
+ buf.extend(self.app)
+ buf.append(self.message_format())
+ buf.append(len(self.signers))
+ for signer in self.signers:
+ buf.extend(signer)
+ msg_len = len(self.message.encode("utf-8"))
+ buf.extend(msg_len.to_bytes(2, "little"))
+
+
+@workflow(capability=messages.Capability.Solana)
+def sign_message(
+ session: "Session",
+ address_n: List[int],
+ message: bytes,
+ chunkify: bool = False,
+) -> bytes:
+ return session.call(
+ messages.SolanaSignMessage(
+ address_n=address_n, message=message, chunkify=chunkify
+ ),
+ expect=messages.SolanaMessageSignature,
+ ).signature
+
+
+class Envelope:
+ """Signed Off-Chain Message Envelope
+
+ Common format for spec v0 and v1:
+ * https://docs.anza.xyz/proposals/off-chain-message-signing#envelope
+ * https://github.com/solana-foundation/SRFCs/discussions/3
+ """
+
+ SIG_LEN = 64
+
+ def __init__(self, signatures: list[bytes], message: bytes) -> None:
+ self.signatures = signatures
+ self.message = message
+
+ def to_bytes(self) -> bytes:
+ buf = bytearray()
+ buf.append(len(self.signatures))
+ for signature in self.signatures:
+ buf.extend(signature)
+ buf.extend(self.message)
+ return bytes(buf)
+
+
+@workflow(capability=messages.Capability.Solana)
+def verify_message(
+ session: "Session",
+ envelope: bytes,
+ chunkify: bool = False,
+) -> bool:
+ try:
+ session.call(
+ messages.SolanaVerifyMessage(envelope=envelope, chunkify=chunkify),
+ expect=messages.Success,
+ )
+ return True
+ except exceptions.TrezorFailure:
+ return False
diff --git a/rust/trezor-client/src/messages/generated.rs b/rust/trezor-client/src/messages/generated.rs
index c0f463d2..eb371e12 100644
--- a/rust/trezor-client/src/messages/generated.rs
+++ b/rust/trezor-client/src/messages/generated.rs
@@ -287,6 +287,9 @@ trezor_message_impl! {
SolanaAddress => MessageType_SolanaAddress,
SolanaSignTx => MessageType_SolanaSignTx,
SolanaTxSignature => MessageType_SolanaTxSignature,
+ SolanaSignMessage => MessageType_SolanaSignMessage,
+ SolanaMessageSignature => MessageType_SolanaMessageSignature,
+ SolanaVerifyMessage => MessageType_SolanaVerifyMessage,
}
#[cfg(feature = "stellar")]
diff --git a/rust/trezor-client/src/protos/generated/messages.rs b/rust/trezor-client/src/protos/generated/messages.rs
index 5e0d3e3c..6fb6d834 100644
--- a/rust/trezor-client/src/protos/generated/messages.rs
+++ b/rust/trezor-client/src/protos/generated/messages.rs
@@ -541,6 +541,12 @@ pub enum MessageType {
MessageType_SolanaSignTx = 904,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaTxSignature)
MessageType_SolanaTxSignature = 905,
+ // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaSignMessage)
+ MessageType_SolanaSignMessage = 906,
+ // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaMessageSignature)
+ MessageType_SolanaMessageSignature = 907,
+ // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaVerifyMessage)
+ MessageType_SolanaVerifyMessage = 908,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_ThpCreateNewSession)
MessageType_ThpCreateNewSession = 1000,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_ThpCredentialRequest)
@@ -873,6 +879,9 @@ impl ::protobuf::Enum for MessageType {
903 => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress),
904 => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx),
905 => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature),
+ 906 => ::std::option::Option::Some(MessageType::MessageType_SolanaSignMessage),
+ 907 => ::std::option::Option::Some(MessageType::MessageType_SolanaMessageSignature),
+ 908 => ::std::option::Option::Some(MessageType::MessageType_SolanaVerifyMessage),
1000 => ::std::option::Option::Some(MessageType::MessageType_ThpCreateNewSession),
1016 => ::std::option::Option::Some(MessageType::MessageType_ThpCredentialRequest),
1017 => ::std::option::Option::Some(MessageType::MessageType_ThpCredentialResponse),
@@ -1168,6 +1177,9 @@ impl ::protobuf::Enum for MessageType {
"MessageType_SolanaAddress" => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress),
"MessageType_SolanaSignTx" => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx),
"MessageType_SolanaTxSignature" => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature),
+ "MessageType_SolanaSignMessage" => ::std::option::Option::Some(MessageType::MessageType_SolanaSignMessage),
+ "MessageType_SolanaMessageSignature" => ::std::option::Option::Some(MessageType::MessageType_SolanaMessageSignature),
+ "MessageType_SolanaVerifyMessage" => ::std::option::Option::Some(MessageType::MessageType_SolanaVerifyMessage),
"MessageType_ThpCreateNewSession" => ::std::option::Option::Some(MessageType::MessageType_ThpCreateNewSession),
"MessageType_ThpCredentialRequest" => ::std::option::Option::Some(MessageType::MessageType_ThpCredentialRequest),
"MessageType_ThpCredentialResponse" => ::std::option::Option::Some(MessageType::MessageType_ThpCredentialResponse),
@@ -1462,6 +1474,9 @@ impl ::protobuf::Enum for MessageType {
MessageType::MessageType_SolanaAddress,
MessageType::MessageType_SolanaSignTx,
MessageType::MessageType_SolanaTxSignature,
+ MessageType::MessageType_SolanaSignMessage,
+ MessageType::MessageType_SolanaMessageSignature,
+ MessageType::MessageType_SolanaVerifyMessage,
MessageType::MessageType_ThpCreateNewSession,
MessageType::MessageType_ThpCredentialRequest,
MessageType::MessageType_ThpCredentialResponse,
@@ -1762,38 +1777,41 @@ impl ::protobuf::EnumFull for MessageType {
MessageType::MessageType_SolanaAddress => 254,
MessageType::MessageType_SolanaSignTx => 255,
MessageType::MessageType_SolanaTxSignature => 256,
- MessageType::MessageType_ThpCreateNewSession => 257,
- MessageType::MessageType_ThpCredentialRequest => 258,
- MessageType::MessageType_ThpCredentialResponse => 259,
- MessageType::MessageType_NostrGetPubkey => 260,
- MessageType::MessageType_NostrPubkey => 261,
- MessageType::MessageType_NostrSignEvent => 262,
- MessageType::MessageType_NostrEventSignature => 263,
- MessageType::MessageType_EvoluGetNode => 264,
- MessageType::MessageType_EvoluNode => 265,
- MessageType::MessageType_EvoluSignRegistrationRequest => 266,
- MessageType::MessageType_EvoluRegistrationRequest => 267,
- MessageType::MessageType_EvoluGetDelegatedIdentityKey => 268,
- MessageType::MessageType_EvoluDelegatedIdentityKey => 269,
- MessageType::MessageType_EvoluIndexManagement => 270,
- MessageType::MessageType_EvoluIndexManagementResponse => 271,
- MessageType::MessageType_TronGetAddress => 272,
- MessageType::MessageType_TronAddress => 273,
- MessageType::MessageType_TronSignTx => 274,
- MessageType::MessageType_TronSignature => 275,
- MessageType::MessageType_TronContractRequest => 276,
- MessageType::MessageType_TronTransferContract => 277,
- MessageType::MessageType_TronTriggerSmartContract => 278,
- MessageType::MessageType_TronFreezeBalanceV2Contract => 279,
- MessageType::MessageType_TronUnfreezeBalanceV2Contract => 280,
- MessageType::MessageType_TronWithdrawUnfreeze => 281,
- MessageType::MessageType_TronVoteWitnessContract => 282,
- MessageType::MessageType_BenchmarkListNames => 283,
- MessageType::MessageType_BenchmarkNames => 284,
- MessageType::MessageType_BenchmarkRun => 285,
- MessageType::MessageType_BenchmarkResult => 286,
- MessageType::MessageType_TelemetryGet => 287,
- MessageType::MessageType_Telemetry => 288,
+ MessageType::MessageType_SolanaSignMessage => 257,
+ MessageType::MessageType_SolanaMessageSignature => 258,
+ MessageType::MessageType_SolanaVerifyMessage => 259,
+ MessageType::MessageType_ThpCreateNewSession => 260,
+ MessageType::MessageType_ThpCredentialRequest => 261,
+ MessageType::MessageType_ThpCredentialResponse => 262,
+ MessageType::MessageType_NostrGetPubkey => 263,
+ MessageType::MessageType_NostrPubkey => 264,
+ MessageType::MessageType_NostrSignEvent => 265,
+ MessageType::MessageType_NostrEventSignature => 266,
+ MessageType::MessageType_EvoluGetNode => 267,
+ MessageType::MessageType_EvoluNode => 268,
+ MessageType::MessageType_EvoluSignRegistrationRequest => 269,
+ MessageType::MessageType_EvoluRegistrationRequest => 270,
+ MessageType::MessageType_EvoluGetDelegatedIdentityKey => 271,
+ MessageType::MessageType_EvoluDelegatedIdentityKey => 272,
+ MessageType::MessageType_EvoluIndexManagement => 273,
+ MessageType::MessageType_EvoluIndexManagementResponse => 274,
+ MessageType::MessageType_TronGetAddress => 275,
+ MessageType::MessageType_TronAddress => 276,
+ MessageType::MessageType_TronSignTx => 277,
+ MessageType::MessageType_TronSignature => 278,
+ MessageType::MessageType_TronContractRequest => 279,
+ MessageType::MessageType_TronTransferContract => 280,
+ MessageType::MessageType_TronTriggerSmartContract => 281,
+ MessageType::MessageType_TronFreezeBalanceV2Contract => 282,
+ MessageType::MessageType_TronUnfreezeBalanceV2Contract => 283,
+ MessageType::MessageType_TronWithdrawUnfreeze => 284,
+ MessageType::MessageType_TronVoteWitnessContract => 285,
+ MessageType::MessageType_BenchmarkListNames => 286,
+ MessageType::MessageType_BenchmarkNames => 287,
+ MessageType::MessageType_BenchmarkRun => 288,
+ MessageType::MessageType_BenchmarkResult => 289,
+ MessageType::MessageType_TelemetryGet => 290,
+ MessageType::MessageType_Telemetry => 291,
};
Self::enum_descriptor().value_by_index(index)
}
@@ -1812,7 +1830,7 @@ impl MessageType {
}
static file_descriptor_proto_data: &'static [u8] = b"\
- \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\xd9e\
+ \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\xdef\
\n\x0bMessageType\x12(\n\x16MessageType_Initialize\x10\0\x1a\x0c\xb0\xb5\
\x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12\x1e\n\x10MessageType_Ping\
\x10\x01\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12%\n\x13MessageType_S\
@@ -2107,50 +2125,53 @@ static file_descriptor_proto_data: &'static [u8] = b"\
dress\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_SolanaAd\
dress\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_SolanaSi\
gnTx\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_SolanaTxS\
- ignature\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12.\n\x1fMessageType_ThpCr\
- eateNewSession\x10\xe8\x07\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12/\
- \n\x20MessageType_ThpCredentialRequest\x10\xf8\x07\x1a\x08\x80\xa6\x1d\
- \x01\x90\xb5\x18\x01\x120\n!MessageType_ThpCredentialResponse\x10\xf9\
- \x07\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12%\n\x1aMessageType_Nostr\
- GetPubkey\x10\xd1\x0f\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_Nos\
- trPubkey\x10\xd2\x0f\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_Nostr\
- SignEvent\x10\xd3\x0f\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_Nost\
- rEventSignature\x10\xd4\x0f\x1a\x04\x98\xb5\x18\x01\x12'\n\x18MessageTyp\
- e_EvoluGetNode\x10\xb4\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12$\
- \n\x15MessageType_EvoluNode\x10\xb5\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\
- \x18\x01\x127\n(MessageType_EvoluSignRegistrationRequest\x10\xb6\x10\x1a\
- \x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x123\n$MessageType_EvoluRegistratio\
- nRequest\x10\xb7\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x127\n(Mess\
- ageType_EvoluGetDelegatedIdentityKey\x10\xb8\x10\x1a\x08\x80\xa6\x1d\x01\
- \x90\xb5\x18\x01\x124\n%MessageType_EvoluDelegatedIdentityKey\x10\xb9\
- \x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12/\n\x20MessageType_Evolu\
- IndexManagement\x10\xba\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x127\
- \n(MessageType_EvoluIndexManagementResponse\x10\xbb\x10\x1a\x08\x80\xa6\
- \x1d\x01\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\x10\x98\
- \x11\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\x10\x99\
- \x11\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\x10\x9a\
- \x11\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_TronSignature\x10\x9b\
- \x11\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_TronContractRequest\
- \x10\x9c\x11\x1a\x04\x98\xb5\x18\x01\x12+\n\x20MessageType_TronTransferC\
- ontract\x10\x9d\x11\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_TronTrigg\
- erSmartContract\x10\x9e\x11\x1a\x04\x90\xb5\x18\x01\x122\n'MessageType_T\
- ronFreezeBalanceV2Contract\x10\x9f\x11\x1a\x04\x90\xb5\x18\x01\x124\n)Me\
- ssageType_TronUnfreezeBalanceV2Contract\x10\xa0\x11\x1a\x04\x90\xb5\x18\
- \x01\x12+\n\x20MessageType_TronWithdrawUnfreeze\x10\xa1\x11\x1a\x04\x90\
- \xb5\x18\x01\x12.\n#MessageType_TronVoteWitnessContract\x10\xa2\x11\x1a\
- \x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkListNames\x10\x8cG\
- \x1a\x04\x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\x10\x8dG\
- \x1a\x04\x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\x8eG\x1a\
- \x04\x80\xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\x10\x8fG\x1a\
- \x04\x80\xa6\x1d\x01\x12'\n\x18MessageType_TelemetryGet\x10\xcc\x08\x1a\
- \x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12$\n\x15MessageType_Telemetry\x10\
- \xcd\x08\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x1a\x08\xc8\xf3\x18\x01\
- \xd0\xf3\x18\x01\"\x04\x08Z\x10\\\"\x04\x08G\x10J\"\x04\x08r\x10z\"\x05\
- \x08{\x10\x95\x01\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\xe0\x01\x10\
- \xe0\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\x10\xb8\x02\"\
- \x06\x08\xbc\x05\x10\xc5\x05\"\x06\x08\xe9\x07\x10\xf7\x07\"\x06\x08\xfa\
- \x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.protobufB\rTrezorMessage\
- \x80\xa6\x1d\x01\
+ ignature\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_Solan\
+ aSignMessage\x10\x8a\x07\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_Sol\
+ anaMessageSignature\x10\x8b\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessag\
+ eType_SolanaVerifyMessage\x10\x8c\x07\x1a\x04\x90\xb5\x18\x01\x12.\n\x1f\
+ MessageType_ThpCreateNewSession\x10\xe8\x07\x1a\x08\x80\xa6\x1d\x01\x90\
+ \xb5\x18\x01\x12/\n\x20MessageType_ThpCredentialRequest\x10\xf8\x07\x1a\
+ \x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x120\n!MessageType_ThpCredentialRes\
+ ponse\x10\xf9\x07\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12%\n\x1aMess\
+ ageType_NostrGetPubkey\x10\xd1\x0f\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17Me\
+ ssageType_NostrPubkey\x10\xd2\x0f\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMess\
+ ageType_NostrSignEvent\x10\xd3\x0f\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMes\
+ sageType_NostrEventSignature\x10\xd4\x0f\x1a\x04\x98\xb5\x18\x01\x12'\n\
+ \x18MessageType_EvoluGetNode\x10\xb4\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\
+ \x18\x01\x12$\n\x15MessageType_EvoluNode\x10\xb5\x10\x1a\x08\x80\xa6\x1d\
+ \x01\x98\xb5\x18\x01\x127\n(MessageType_EvoluSignRegistrationRequest\x10\
+ \xb6\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x123\n$MessageType_Evol\
+ uRegistrationRequest\x10\xb7\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\
+ \x127\n(MessageType_EvoluGetDelegatedIdentityKey\x10\xb8\x10\x1a\x08\x80\
+ \xa6\x1d\x01\x90\xb5\x18\x01\x124\n%MessageType_EvoluDelegatedIdentityKe\
+ y\x10\xb9\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12/\n\x20MessageT\
+ ype_EvoluIndexManagement\x10\xba\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\
+ \x01\x127\n(MessageType_EvoluIndexManagementResponse\x10\xbb\x10\x1a\x08\
+ \x80\xa6\x1d\x01\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\
+ \x10\x98\x11\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\
+ \x10\x99\x11\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\
+ \x10\x9a\x11\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_TronSignature\
+ \x10\x9b\x11\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_TronContractR\
+ equest\x10\x9c\x11\x1a\x04\x98\xb5\x18\x01\x12+\n\x20MessageType_TronTra\
+ nsferContract\x10\x9d\x11\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_Tro\
+ nTriggerSmartContract\x10\x9e\x11\x1a\x04\x90\xb5\x18\x01\x122\n'Message\
+ Type_TronFreezeBalanceV2Contract\x10\x9f\x11\x1a\x04\x90\xb5\x18\x01\x12\
+ 4\n)MessageType_TronUnfreezeBalanceV2Contract\x10\xa0\x11\x1a\x04\x90\
+ \xb5\x18\x01\x12+\n\x20MessageType_TronWithdrawUnfreeze\x10\xa1\x11\x1a\
+ \x04\x90\xb5\x18\x01\x12.\n#MessageType_TronVoteWitnessContract\x10\xa2\
+ \x11\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkListNames\
+ \x10\x8cG\x1a\x04\x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\
+ \x10\x8dG\x1a\x04\x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\
+ \x8eG\x1a\x04\x80\xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\x10\
+ \x8fG\x1a\x04\x80\xa6\x1d\x01\x12'\n\x18MessageType_TelemetryGet\x10\xcc\
+ \x08\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12$\n\x15MessageType_Telem\
+ etry\x10\xcd\x08\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x1a\x08\xc8\xf3\
+ \x18\x01\xd0\xf3\x18\x01\"\x04\x08Z\x10\\\"\x04\x08G\x10J\"\x04\x08r\x10\
+ z\"\x05\x08{\x10\x95\x01\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\xe0\x01\
+ \x10\xe0\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\x10\xb8\x02\
+ \"\x06\x08\xbc\x05\x10\xc5\x05\"\x06\x08\xe9\x07\x10\xf7\x07\"\x06\x08\
+ \xfa\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.protobufB\rTrezorMes\
+ sage\x80\xa6\x1d\x01\
";
/// `FileDescriptorProto` object which was a source for this generated file
diff --git a/rust/trezor-client/src/protos/generated/messages_solana.rs b/rust/trezor-client/src/protos/generated/messages_solana.rs
index e5dca09f..6e369f3a 100644
--- a/rust/trezor-client/src/protos/generated/messages_solana.rs
+++ b/rust/trezor-client/src/protos/generated/messages_solana.rs
@@ -1666,6 +1666,584 @@ impl ::protobuf::reflect::ProtobufValue for SolanaTxSignature {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
+// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaSignMessage)
+#[derive(PartialEq,Clone,Default,Debug)]
+pub struct SolanaSignMessage {
+ // message fields
+ // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaSignMessage.address_n)
+ pub address_n: ::std::vec::Vec<u32>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaSignMessage.message)
+ pub message: ::std::option::Option<::std::vec::Vec<u8>>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaSignMessage.chunkify)
+ pub chunkify: ::std::option::Option<bool>,
+ // special fields
+ // @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaSignMessage.special_fields)
+ pub special_fields: ::protobuf::SpecialFields,
+}
+
+impl<'a> ::std::default::Default for &'a SolanaSignMessage {
+ fn default() -> &'a SolanaSignMessage {
+ <SolanaSignMessage as ::protobuf::Message>::default_instance()
+ }
+}
+
+impl SolanaSignMessage {
+ pub fn new() -> SolanaSignMessage {
+ ::std::default::Default::default()
+ }
+
+ // required bytes message = 2;
+
+ pub fn message(&self) -> &[u8] {
+ match self.message.as_ref() {
+ Some(v) => v,
+ None => &[],
+ }
+ }
+
+ pub fn clear_message(&mut self) {
+ self.message = ::std::option::Option::None;
+ }
+
+ pub fn has_message(&self) -> bool {
+ self.message.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_message(&mut self, v: ::std::vec::Vec<u8>) {
+ self.message = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_message(&mut self) -> &mut ::std::vec::Vec<u8> {
+ if self.message.is_none() {
+ self.message = ::std::option::Option::Some(::std::vec::Vec::new());
+ }
+ self.message.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_message(&mut self) -> ::std::vec::Vec<u8> {
+ self.message.take().unwrap_or_else(|| ::std::vec::Vec::new())
+ }
+
+ // optional bool chunkify = 3;
+
+ pub fn chunkify(&self) -> bool {
+ self.chunkify.unwrap_or(false)
+ }
+
+ pub fn clear_chunkify(&mut self) {
+ self.chunkify = ::std::option::Option::None;
+ }
+
+ pub fn has_chunkify(&self) -> bool {
+ self.chunkify.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_chunkify(&mut self, v: bool) {
+ self.chunkify = ::std::option::Option::Some(v);
+ }
+
+ fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
+ let mut fields = ::std::vec::Vec::with_capacity(3);
+ let mut oneofs = ::std::vec::Vec::with_capacity(0);
+ fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
+ "address_n",
+ |m: &SolanaSignMessage| { &m.address_n },
+ |m: &mut SolanaSignMessage| { &mut m.address_n },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "message",
+ |m: &SolanaSignMessage| { &m.message },
+ |m: &mut SolanaSignMessage| { &mut m.message },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "chunkify",
+ |m: &SolanaSignMessage| { &m.chunkify },
+ |m: &mut SolanaSignMessage| { &mut m.chunkify },
+ ));
+ ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SolanaSignMessage>(
+ "SolanaSignMessage",
+ fields,
+ oneofs,
+ )
+ }
+}
+
+impl ::protobuf::Message for SolanaSignMessage {
+ const NAME: &'static str = "SolanaSignMessage";
+
+ fn is_initialized(&self) -> bool {
+ if self.message.is_none() {
+ return false;
+ }
+ true
+ }
+
+ fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
+ while let Some(tag) = is.read_raw_tag_or_eof()? {
+ match tag {
+ 10 => {
+ is.read_repeated_packed_uint32_into(&mut self.address_n)?;
+ },
+ 8 => {
+ self.address_n.push(is.read_uint32()?);
+ },
+ 18 => {
+ self.message = ::std::option::Option::Some(is.read_bytes()?);
+ },
+ 24 => {
+ self.chunkify = ::std::option::Option::Some(is.read_bool()?);
+ },
+ tag => {
+ ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
+ },
+ };
+ }
+ ::std::result::Result::Ok(())
+ }
+
+ // Compute sizes of nested messages
+ #[allow(unused_variables)]
+ fn compute_size(&self) -> u64 {
+ let mut my_size = 0;
+ for value in &self.address_n {
+ my_size += ::protobuf::rt::uint32_size(1, *value);
+ };
+ if let Some(v) = self.message.as_ref() {
+ my_size += ::protobuf::rt::bytes_size(2, &v);
+ }
+ if let Some(v) = self.chunkify {
+ my_size += 1 + 1;
+ }
+ my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
+ self.special_fields.cached_size().set(my_size as u32);
+ my_size
+ }
+
+ fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
+ for v in &self.address_n {
+ os.write_uint32(1, *v)?;
+ };
+ if let Some(v) = self.message.as_ref() {
+ os.write_bytes(2, v)?;
+ }
+ if let Some(v) = self.chunkify {
+ os.write_bool(3, v)?;
+ }
+ os.write_unknown_fields(self.special_fields.unknown_fields())?;
+ ::std::result::Result::Ok(())
+ }
+
+ fn special_fields(&self) -> &::protobuf::SpecialFields {
+ &self.special_fields
+ }
+
+ fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
+ &mut self.special_fields
+ }
+
+ fn new() -> SolanaSignMessage {
+ SolanaSignMessage::new()
+ }
+
+ fn clear(&mut self) {
+ self.address_n.clear();
+ self.message = ::std::option::Option::None;
+ self.chunkify = ::std::option::Option::None;
+ self.special_fields.clear();
+ }
+
+ fn default_instance() -> &'static SolanaSignMessage {
+ static instance: SolanaSignMessage = SolanaSignMessage {
+ address_n: ::std::vec::Vec::new(),
+ message: ::std::option::Option::None,
+ chunkify: ::std::option::Option::None,
+ special_fields: ::protobuf::SpecialFields::new(),
+ };
+ &instance
+ }
+}
+
+impl ::protobuf::MessageFull for SolanaSignMessage {
+ fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
+ static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
+ descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaSignMessage").unwrap()).clone()
+ }
+}
+
+impl ::std::fmt::Display for SolanaSignMessage {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ ::protobuf::text_format::fmt(self, f)
+ }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SolanaSignMessage {
+ type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
+}
+
+// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaMessageSignature)
+#[derive(PartialEq,Clone,Default,Debug)]
+pub struct SolanaMessageSignature {
+ // message fields
+ // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaMessageSignature.signature)
+ pub signature: ::std::option::Option<::std::vec::Vec<u8>>,
+ // special fields
+ // @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaMessageSignature.special_fields)
+ pub special_fields: ::protobuf::SpecialFields,
+}
+
+impl<'a> ::std::default::Default for &'a SolanaMessageSignature {
+ fn default() -> &'a SolanaMessageSignature {
+ <SolanaMessageSignature as ::protobuf::Message>::default_instance()
+ }
+}
+
+impl SolanaMessageSignature {
+ pub fn new() -> SolanaMessageSignature {
+ ::std::default::Default::default()
+ }
+
+ // required bytes signature = 1;
+
+ pub fn signature(&self) -> &[u8] {
+ match self.signature.as_ref() {
+ Some(v) => v,
+ None => &[],
+ }
+ }
+
+ pub fn clear_signature(&mut self) {
+ self.signature = ::std::option::Option::None;
+ }
+
+ pub fn has_signature(&self) -> bool {
+ self.signature.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_signature(&mut self, v: ::std::vec::Vec<u8>) {
+ self.signature = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_signature(&mut self) -> &mut ::std::vec::Vec<u8> {
+ if self.signature.is_none() {
+ self.signature = ::std::option::Option::Some(::std::vec::Vec::new());
+ }
+ self.signature.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_signature(&mut self) -> ::std::vec::Vec<u8> {
+ self.signature.take().unwrap_or_else(|| ::std::vec::Vec::new())
+ }
+
+ fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
+ let mut fields = ::std::vec::Vec::with_capacity(1);
+ let mut oneofs = ::std::vec::Vec::with_capacity(0);
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "signature",
+ |m: &SolanaMessageSignature| { &m.signature },
+ |m: &mut SolanaMessageSignature| { &mut m.signature },
+ ));
+ ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SolanaMessageSignature>(
+ "SolanaMessageSignature",
+ fields,
+ oneofs,
+ )
+ }
+}
+
+impl ::protobuf::Message for SolanaMessageSignature {
+ const NAME: &'static str = "SolanaMessageSignature";
+
+ fn is_initialized(&self) -> bool {
+ if self.signature.is_none() {
+ return false;
+ }
+ true
+ }
+
+ fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
+ while let Some(tag) = is.read_raw_tag_or_eof()? {
+ match tag {
+ 10 => {
+ self.signature = ::std::option::Option::Some(is.read_bytes()?);
+ },
+ tag => {
+ ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
+ },
+ };
+ }
+ ::std::result::Result::Ok(())
+ }
+
+ // Compute sizes of nested messages
+ #[allow(unused_variables)]
+ fn compute_size(&self) -> u64 {
+ let mut my_size = 0;
+ if let Some(v) = self.signature.as_ref() {
+ my_size += ::protobuf::rt::bytes_size(1, &v);
+ }
+ my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
+ self.special_fields.cached_size().set(my_size as u32);
+ my_size
+ }
+
+ fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
+ if let Some(v) = self.signature.as_ref() {
+ os.write_bytes(1, v)?;
+ }
+ os.write_unknown_fields(self.special_fields.unknown_fields())?;
+ ::std::result::Result::Ok(())
+ }
+
+ fn special_fields(&self) -> &::protobuf::SpecialFields {
+ &self.special_fields
+ }
+
+ fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
+ &mut self.special_fields
+ }
+
+ fn new() -> SolanaMessageSignature {
+ SolanaMessageSignature::new()
+ }
+
+ fn clear(&mut self) {
+ self.signature = ::std::option::Option::None;
+ self.special_fields.clear();
+ }
+
+ fn default_instance() -> &'static SolanaMessageSignature {
+ static instance: SolanaMessageSignature = SolanaMessageSignature {
+ signature: ::std::option::Option::None,
+ special_fields: ::protobuf::SpecialFields::new(),
+ };
+ &instance
+ }
+}
+
+impl ::protobuf::MessageFull for SolanaMessageSignature {
+ fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
+ static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
+ descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaMessageSignature").unwrap()).clone()
+ }
+}
+
+impl ::std::fmt::Display for SolanaMessageSignature {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ ::protobuf::text_format::fmt(self, f)
+ }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SolanaMessageSignature {
+ type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
+}
+
+// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaVerifyMessage)
+#[derive(PartialEq,Clone,Default,Debug)]
+pub struct SolanaVerifyMessage {
+ // message fields
+ // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaVerifyMessage.envelope)
+ pub envelope: ::std::option::Option<::std::vec::Vec<u8>>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaVerifyMessage.chunkify)
+ pub chunkify: ::std::option::Option<bool>,
+ // special fields
+ // @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaVerifyMessage.special_fields)
+ pub special_fields: ::protobuf::SpecialFields,
+}
+
+impl<'a> ::std::default::Default for &'a SolanaVerifyMessage {
+ fn default() -> &'a SolanaVerifyMessage {
+ <SolanaVerifyMessage as ::protobuf::Message>::default_instance()
+ }
+}
+
+impl SolanaVerifyMessage {
+ pub fn new() -> SolanaVerifyMessage {
+ ::std::default::Default::default()
+ }
+
+ // required bytes envelope = 1;
+
+ pub fn envelope(&self) -> &[u8] {
+ match self.envelope.as_ref() {
+ Some(v) => v,
+ None => &[],
+ }
+ }
+
+ pub fn clear_envelope(&mut self) {
+ self.envelope = ::std::option::Option::None;
+ }
+
+ pub fn has_envelope(&self) -> bool {
+ self.envelope.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_envelope(&mut self, v: ::std::vec::Vec<u8>) {
+ self.envelope = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_envelope(&mut self) -> &mut ::std::vec::Vec<u8> {
+ if self.envelope.is_none() {
+ self.envelope = ::std::option::Option::Some(::std::vec::Vec::new());
+ }
+ self.envelope.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_envelope(&mut self) -> ::std::vec::Vec<u8> {
+ self.envelope.take().unwrap_or_else(|| ::std::vec::Vec::new())
+ }
+
+ // optional bool chunkify = 2;
+
+ pub fn chunkify(&self) -> bool {
+ self.chunkify.unwrap_or(false)
+ }
+
+ pub fn clear_chunkify(&mut self) {
+ self.chunkify = ::std::option::Option::None;
+ }
+
+ pub fn has_chunkify(&self) -> bool {
+ self.chunkify.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_chunkify(&mut self, v: bool) {
+ self.chunkify = ::std::option::Option::Some(v);
+ }
+
+ fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
+ let mut fields = ::std::vec::Vec::with_capacity(2);
+ let mut oneofs = ::std::vec::Vec::with_capacity(0);
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "envelope",
+ |m: &SolanaVerifyMessage| { &m.envelope },
+ |m: &mut SolanaVerifyMessage| { &mut m.envelope },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "chunkify",
+ |m: &SolanaVerifyMessage| { &m.chunkify },
+ |m: &mut SolanaVerifyMessage| { &mut m.chunkify },
+ ));
+ ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SolanaVerifyMessage>(
+ "SolanaVerifyMessage",
+ fields,
+ oneofs,
+ )
+ }
+}
+
+impl ::protobuf::Message for SolanaVerifyMessage {
+ const NAME: &'static str = "SolanaVerifyMessage";
+
+ fn is_initialized(&self) -> bool {
+ if self.envelope.is_none() {
+ return false;
+ }
+ true
+ }
+
+ fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
+ while let Some(tag) = is.read_raw_tag_or_eof()? {
+ match tag {
+ 10 => {
+ self.envelope = ::std::option::Option::Some(is.read_bytes()?);
+ },
+ 16 => {
+ self.chunkify = ::std::option::Option::Some(is.read_bool()?);
+ },
+ tag => {
+ ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
+ },
+ };
+ }
+ ::std::result::Result::Ok(())
+ }
+
+ // Compute sizes of nested messages
+ #[allow(unused_variables)]
+ fn compute_size(&self) -> u64 {
+ let mut my_size = 0;
+ if let Some(v) = self.envelope.as_ref() {
+ my_size += ::protobuf::rt::bytes_size(1, &v);
+ }
+ if let Some(v) = self.chunkify {
+ my_size += 1 + 1;
+ }
+ my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
+ self.special_fields.cached_size().set(my_size as u32);
+ my_size
+ }
+
+ fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
+ if let Some(v) = self.envelope.as_ref() {
+ os.write_bytes(1, v)?;
+ }
+ if let Some(v) = self.chunkify {
+ os.write_bool(2, v)?;
+ }
+ os.write_unknown_fields(self.special_fields.unknown_fields())?;
+ ::std::result::Result::Ok(())
+ }
+
+ fn special_fields(&self) -> &::protobuf::SpecialFields {
+ &self.special_fields
+ }
+
+ fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
+ &mut self.special_fields
+ }
+
+ fn new() -> SolanaVerifyMessage {
+ SolanaVerifyMessage::new()
+ }
+
+ fn clear(&mut self) {
+ self.envelope = ::std::option::Option::None;
+ self.chunkify = ::std::option::Option::None;
+ self.special_fields.clear();
+ }
+
+ fn default_instance() -> &'static SolanaVerifyMessage {
+ static instance: SolanaVerifyMessage = SolanaVerifyMessage {
+ envelope: ::std::option::Option::None,
+ chunkify: ::std::option::Option::None,
+ special_fields: ::protobuf::SpecialFields::new(),
+ };
+ &instance
+ }
+}
+
+impl ::protobuf::MessageFull for SolanaVerifyMessage {
+ fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
+ static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
+ descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaVerifyMessage").unwrap()).clone()
+ }
+}
+
+impl ::std::fmt::Display for SolanaVerifyMessage {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ ::protobuf::text_format::fmt(self, f)
+ }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SolanaVerifyMessage {
+ type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
+}
+
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x15messages-solana.proto\x12\x19hw.trezor.messages.solana\x1a\x15mess\
ages-common.proto\"T\n\x12SolanaGetPublicKey\x12\x1b\n\taddress_n\x18\
@@ -1688,8 +2266,14 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x03\x20\x01(\x0b21.hw.trezor.messages.solana.SolanaTxAdditionalInfoR\
\x0eadditionalInfo\x12J\n\x0bpayment_req\x18\x04\x20\x01(\x0b2).hw.trezo\
r.messages.common.PaymentRequestR\npaymentReq\"1\n\x11SolanaTxSignature\
- \x12\x1c\n\tsignature\x18\x01\x20\x02(\x0cR\tsignatureB:\n#com.satoshila\
- bs.trezor.lib.protobufB\x13TrezorMessageSolana\
+ \x12\x1c\n\tsignature\x18\x01\x20\x02(\x0cR\tsignature\"f\n\x11SolanaSig\
+ nMessage\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12\x18\n\
+ \x07message\x18\x02\x20\x02(\x0cR\x07message\x12\x1a\n\x08chunkify\x18\
+ \x03\x20\x01(\x08R\x08chunkify\"6\n\x16SolanaMessageSignature\x12\x1c\n\
+ \tsignature\x18\x01\x20\x02(\x0cR\tsignature\"M\n\x13SolanaVerifyMessage\
+ \x12\x1a\n\x08envelope\x18\x01\x20\x02(\x0cR\x08envelope\x12\x1a\n\x08ch\
+ unkify\x18\x02\x20\x01(\x08R\x08chunkifyB:\n#com.satoshilabs.trezor.lib.\
+ protobufB\x13TrezorMessageSolana\
";
/// `FileDescriptorProto` object which was a source for this generated file
@@ -1708,7 +2292,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
let mut deps = ::std::vec::Vec::with_capacity(1);
deps.push(super::messages_common::file_descriptor().clone());
- let mut messages = ::std::vec::Vec::with_capacity(8);
+ let mut messages = ::std::vec::Vec::with_capacity(11);
messages.push(SolanaGetPublicKey::generated_message_descriptor_data());
messages.push(SolanaPublicKey::generated_message_descriptor_data());
messages.push(SolanaGetAddress::generated_message_descriptor_data());
@@ -1717,6 +2301,9 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
messages.push(SolanaTxAdditionalInfo::generated_message_descriptor_data());
messages.push(SolanaSignTx::generated_message_descriptor_data());
messages.push(SolanaTxSignature::generated_message_descriptor_data());
+ messages.push(SolanaSignMessage::generated_message_descriptor_data());
+ messages.push(SolanaMessageSignature::generated_message_descriptor_data());
+ messages.push(SolanaVerifyMessage::generated_message_descriptor_data());
let mut enums = ::std::vec::Vec::with_capacity(0);
::protobuf::reflect::GeneratedFileDescriptor::new_generated(
file_descriptor_proto(),
Why this scored 36/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.