remove off-chain message signing

solana-poc
gabrielkerekes 9 months ago
parent 47a0574ec4
commit a120d6d625

@ -53,28 +53,9 @@ message SolanaSignTx {
}
/**
* Response: Contains the hash of the signed transaction and the signature
* Response: Contains the transaction signature
* @end
*/
message SolanaTxSignature {
required bytes signature = 1; // tx signature
}
/**
* Request: Ask device to sign a Solana off-chain message
* @start
* @next SolanaOffChainMessageSignature
* @next Failure
*/
message SolanaSignOffChainMessage {
repeated uint32 address_n = 1; // BIP-32 path to derive the key to sign with
required bytes serialized_message = 2; // serialized off-chain message to be signed
}
/**
* Response: Contains a off-chain message signature
* @end
*/
message SolanaOffChainMessageSignature {
required bytes signature = 1; // off-chain message signature
}

@ -367,6 +367,4 @@ enum MessageType {
MessageType_SolanaAddress = 903 [(wire_out) = true];
MessageType_SolanaSignTx = 904 [(wire_in) = true];
MessageType_SolanaTxSignature = 905 [(wire_out) = true];
MessageType_SolanaSignOffChainMessage = 906 [(wire_in) = true];
MessageType_SolanaOffChainMessageSignature = 907 [(wire_out) = true];
}

@ -393,8 +393,6 @@ apps.solana.parsing.parse_instructions
import apps.solana.parsing.parse_instructions
apps.solana.parsing.utils
import apps.solana.parsing.utils
apps.solana.sign_off_chain_message
import apps.solana.sign_off_chain_message
apps.solana.sign_tx
import apps.solana.sign_tx
apps.solana.types

@ -1,142 +0,0 @@
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 (
SolanaSignOffChainMessage,
SolanaOffChainMessageSignature,
)
from apps.common.keychain import Keychain
FORMAT_ASCII = 0
FORMAT_UTF8 = 1
MAX_MESSAGE_LENGTH = 1212
@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE)
async def sign_off_chain_message(
msg: SolanaSignOffChainMessage,
keychain: Keychain,
) -> SolanaOffChainMessageSignature:
from trezor.crypto import base58
from trezor.crypto.curve import ed25519
from trezor.messages import SolanaOffChainMessageSignature
from trezor.ui.layouts import confirm_signverify
from apps.common import seed
address_n = msg.address_n
serialized_message = msg.serialized_message
message, format = _parse_off_chain_message(serialized_message)
node = keychain.derive(address_n)
address = base58.encode(seed.remove_ed25519_prefix(node.public_key()))
await confirm_signverify(
"SOL", message.decode("ascii" if format == 0 else "utf-8"), address, False
)
signature = ed25519.sign(node.private_key(), serialized_message)
return SolanaOffChainMessageSignature(signature=signature)
def _parse_off_chain_message(serialized_message: bytes) -> tuple[bytes, int]:
from trezor.utils import BufferReader
SIGNING_DOMAIN_SPECIFIER = b"\xffsolana offchain"
serialized_message_b = BufferReader(serialized_message)
signing_domain_specifier = serialized_message_b.read(16)
if signing_domain_specifier != SIGNING_DOMAIN_SPECIFIER:
raise ValueError("Invalid message")
version = serialized_message_b.get()
if version != 0:
raise ValueError("Invalid message")
format = serialized_message_b.get()
if format > FORMAT_UTF8:
raise ValueError("Invalid message")
length = int.from_bytes(serialized_message_b.read(2), "little")
if serialized_message_b.remaining_count() != length:
raise ValueError("Invalid message")
message = serialized_message_b.read(length)
if format == FORMAT_ASCII and not _is_ascii(message):
raise ValueError("Invalid message")
elif format == FORMAT_UTF8 and not _is_utf8(message):
raise ValueError("Invalid message")
return message, format
def _is_utf8(data: bytes) -> bool:
"""Adapted from: https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c"""
length = len(data)
i = 0
while i < length:
if data[i] < 0x80:
# 0xxxxxxx
i += 1
elif (data[i] & 0xE0) == 0xC0:
# 110XXXXx 10xxxxxx
if (
i + 1 >= length
or (data[i + 1] & 0xC0) != 0x80
or (data[i] & 0xFE) == 0xC0
): # overlong?
return False
else:
i += 2
elif (data[i] & 0xF0) == 0xE0:
# 1110XXXX 10Xxxxxx 10xxxxxx
if (
i + 2 >= length
or (data[i + 1] & 0xC0) != 0x80
or (data[i + 2] & 0xC0) != 0x80
or (data[i] == 0xE0 and (data[i + 1] & 0xE0) == 0x80)
or (data[i] == 0xED and (data[i + 1] & 0xE0) == 0xA0)
or (
data[i] == 0xEF
and data[i + 1] == 0xBF
and (data[i + 2] & 0xFE) == 0xBE
)
): # U+FFFE or U+FFFF?
return False
else:
i += 3
elif (data[i] & 0xF8) == 0xF0:
# 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx
if (
i + 3 >= length
or (data[i + 1] & 0xC0) != 0x80
or (data[i + 2] & 0xC0) != 0x80
or (data[i + 3] & 0xC0) != 0x80
or (data[i] == 0xF0 and (data[i + 1] & 0xF0) == 0x80)
or (data[i] == 0xF4 and data[i + 1] > 0x8F)
or data[i] > 0xF4
):
return False
else:
i += 4
else:
return False
return True
def _is_ascii(data: bytes) -> bool:
for byte in data:
if byte < 0x20 or byte > 0x7e:
return False;
return True

@ -193,8 +193,6 @@ 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.SolanaSignOffChainMessage:
return "apps.solana.sign_off_chain_message"
raise ValueError

@ -239,5 +239,3 @@ if not utils.BITCOIN_ONLY:
SolanaAddress = 903
SolanaSignTx = 904
SolanaTxSignature = 905
SolanaSignOffChainMessage = 906
SolanaOffChainMessageSignature = 907

@ -257,8 +257,6 @@ if TYPE_CHECKING:
SolanaAddress = 903
SolanaSignTx = 904
SolanaTxSignature = 905
SolanaSignOffChainMessage = 906
SolanaOffChainMessageSignature = 907
class FailureType(IntEnum):
UnexpectedMessage = 1

@ -5192,36 +5192,6 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxSignature"]:
return isinstance(msg, cls)
class SolanaSignOffChainMessage(protobuf.MessageType):
address_n: "list[int]"
serialized_message: "bytes"
def __init__(
self,
*,
serialized_message: "bytes",
address_n: "list[int] | None" = None,
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaSignOffChainMessage"]:
return isinstance(msg, cls)
class SolanaOffChainMessageSignature(protobuf.MessageType):
signature: "bytes"
def __init__(
self,
*,
signature: "bytes",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaOffChainMessageSignature"]:
return isinstance(msg, cls)
class StellarAsset(protobuf.MessageType):
type: "StellarAssetType"
code: "str | None"

@ -57,18 +57,3 @@ def sign_tx(
address_n = tools.parse_path(address)
client.init_device()
return solana.sign_tx(client, address_n, bytes.fromhex(serialized_tx))
@cli.command()
@click.option("-n", "--address", required=True, help=PATH_HELP)
@click.option("-m", "--serialized-message", required=True)
@with_client
def sign_off_chain_message(
client: "TrezorClient",
address: str,
serialized_message: str,
) -> messages.SolanaOffChainMessageSignature:
"""Sign Solana off-chain message."""
address_n = tools.parse_path(address)
client.init_device()
return solana.sign_off_chain_message(client, address_n, bytes.fromhex(serialized_message))

@ -265,8 +265,6 @@ class MessageType(IntEnum):
SolanaAddress = 903
SolanaSignTx = 904
SolanaTxSignature = 905
SolanaSignOffChainMessage = 906
SolanaOffChainMessageSignature = 907
class FailureType(IntEnum):
@ -6621,37 +6619,6 @@ class SolanaTxSignature(protobuf.MessageType):
self.signature = signature
class SolanaSignOffChainMessage(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 906
FIELDS = {
1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None),
2: protobuf.Field("serialized_message", "bytes", repeated=False, required=True),
}
def __init__(
self,
*,
serialized_message: "bytes",
address_n: Optional[Sequence["int"]] = None,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.serialized_message = serialized_message
class SolanaOffChainMessageSignature(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 StellarAsset(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {

@ -42,17 +42,3 @@ def sign_tx(
serialized_tx=serialized_tx,
)
)
@expect(messages.SolanaOffChainMessageSignature)
def sign_off_chain_message(
client: "TrezorClient",
address_n: List[int],
serialized_message: bytes,
) -> "MessageType":
return client.call(
messages.SolanaSignOffChainMessage(
address_n=address_n,
serialized_message=serialized_message,
)
)

Loading…
Cancel
Save