diff --git a/common/protob/messages-lnurl.proto b/common/protob/messages-lnurl.proto new file mode 100644 index 0000000000..96acadaa1a --- /dev/null +++ b/common/protob/messages-lnurl.proto @@ -0,0 +1,26 @@ +syntax = "proto2"; +package hw.trezor.messages.lnurl; + +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.trezor.lib.protobuf"; +option java_outer_classname = "TrezorMessageLnurl"; + +/** + * Request: linkingKey derivation + * @start + * @next LnurlPublicKey + */ +message LnurlAuth { + required bytes domain = 2; // domain + required bytes data = 3; // random_data +} + +/** + * Response: LnurlPublicKey for the given index + * @end + */ +message LnurlAuthResp { + optional string publickey = 1; + optional string path = 2; + optional bytes signature = 3; +} diff --git a/common/protob/messages-sui.proto b/common/protob/messages-sui.proto index a280b43ed3..4260de62a6 100644 --- a/common/protob/messages-sui.proto +++ b/common/protob/messages-sui.proto @@ -26,13 +26,16 @@ message SuiAddress { /** * Request: ask device to sign Sui transaction * @start - * @next SuiSignedTx + * @next SuiSignedTx/SuiTxRequest */ message SuiSignTx { repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node required bytes raw_tx = 2; // serialized raw transaction + optional bytes data_initial_chunk = 3 [default='']; // The initial data chunk (<= 1024 bytes) + optional uint32 data_length = 4; // Length of transaction payload } + /** * Response: signature for transaction * @end @@ -42,6 +45,27 @@ message SuiSignedTx { required bytes signature = 2; // the signature of the raw transaction } +/** + * Response: Device asks for more data from transaction payload, or returns the signature. + * If data_length is set, device awaits that many more bytes of payload. + * Otherwise, the signature fields contain the computed transaction signature. All three fields will be present. + * @end + * @next SuiTxAck + */ + message SuiTxRequest { + optional uint32 data_length = 1; // Number of bytes being requested (<= 1024) + optional bytes public_key = 2; // public key for the private key used to sign tx + optional bytes signature = 3; // the signature of the raw transaction +} + +/** + * Request: Transaction payload data. + * @next SuiTxRequest + */ + message SuiTxAck { + required bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes) +} + /** * Request: Ask device to sign message * @next SuiMessageSignature diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 07a54cbd46..cd74933d9e 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -478,6 +478,8 @@ enum MessageType { MessageType_SuiSignedTx = 11103 [(wire_out) = true]; MessageType_SuiSignMessage = 11104 [(wire_in) = true]; MessageType_SuiMessageSignature = 11105 [(wire_out) = true]; + MessageType_SuiTxRequest = 11106 [(wire_out) = true]; + MessageType_SuiTxAck = 11107 [(wire_in) = true]; // Filecoin MessageType_FilecoinGetAddress = 11200 [(wire_in) = true]; @@ -512,6 +514,10 @@ enum MessageType { MessageType_NostrSignSchnorr = 11508 [(wire_in) = true]; MessageType_NostrSignedSchnorr = 11509 [(wire_out) = true]; + // lnurl + MessageType_LnurlAuth = 11600 [(wire_in) = true]; + MessageType_LnurlAuthResp = 11601 [(wire_out) = true]; + //onekey MessageType_DeviceInfoSettings = 10001 [(wire_in) = true]; MessageType_GetDeviceInfo = 10002 [(wire_in) = true]; diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index ce2d9bb54a..d9cd1d383e 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -318,6 +318,8 @@ SuiSignedTx = 11103 SuiSignMessage = 11104 SuiMessageSignature = 11105 + SuiTxRequest = 11106 + SuiTxAck = 11107 FilecoinGetAddress = 11200 FilecoinAddress = 11201 FilecoinSignTx = 11202 @@ -344,6 +346,8 @@ NostrDecryptedMessage = 11507 NostrSignSchnorr = 11508 NostrSignedSchnorr = 11509 + LnurlAuth = 11600 + LnurlAuthResp = 11601 DeviceInfoSettings = 10001 GetDeviceInfo = 10002 DeviceInfo = 10003 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 8b4e34b762..42ce23830f 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -341,6 +341,8 @@ class MessageType(IntEnum): SuiSignedTx = 11103 SuiSignMessage = 11104 SuiMessageSignature = 11105 + SuiTxRequest = 11106 + SuiTxAck = 11107 FilecoinGetAddress = 11200 FilecoinAddress = 11201 FilecoinSignTx = 11202 @@ -367,6 +369,8 @@ class MessageType(IntEnum): NostrDecryptedMessage = 11507 NostrSignSchnorr = 11508 NostrSignedSchnorr = 11509 + LnurlAuth = 11600 + LnurlAuthResp = 11601 DeviceInfoSettings = 10001 GetDeviceInfo = 10002 DeviceInfo = 10003 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 2dec08fb47..bb98880d46 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -5235,6 +5235,40 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["KaspaSignedTx"]: return isinstance(msg, cls) + class LnurlAuth(protobuf.MessageType): + domain: "bytes" + data: "bytes" + + def __init__( + self, + *, + domain: "bytes", + data: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["LnurlAuth"]: + return isinstance(msg, cls) + + class LnurlAuthResp(protobuf.MessageType): + publickey: "str | None" + path: "str | None" + signature: "bytes | None" + + def __init__( + self, + *, + publickey: "str | None" = None, + path: "str | None" = None, + signature: "bytes | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["LnurlAuthResp"]: + return isinstance(msg, cls) + class MoneroTransactionSourceEntry(protobuf.MessageType): outputs: "list[MoneroOutputEntry]" real_output: "int | None" @@ -7516,12 +7550,16 @@ def is_type_of(cls, msg: Any) -> TypeGuard["SuiAddress"]: class SuiSignTx(protobuf.MessageType): address_n: "list[int]" raw_tx: "bytes" + data_initial_chunk: "bytes" + data_length: "int | None" def __init__( self, *, raw_tx: "bytes", address_n: "list[int] | None" = None, + data_initial_chunk: "bytes | None" = None, + data_length: "int | None" = None, ) -> None: pass @@ -7545,6 +7583,38 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["SuiSignedTx"]: return isinstance(msg, cls) + class SuiTxRequest(protobuf.MessageType): + data_length: "int | None" + public_key: "bytes | None" + signature: "bytes | None" + + def __init__( + self, + *, + data_length: "int | None" = None, + public_key: "bytes | None" = None, + signature: "bytes | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SuiTxRequest"]: + return isinstance(msg, cls) + + class SuiTxAck(protobuf.MessageType): + data_chunk: "bytes" + + def __init__( + self, + *, + data_chunk: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SuiTxAck"]: + return isinstance(msg, cls) + class SuiSignMessage(protobuf.MessageType): address_n: "list[int]" message: "bytes" diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index 7295cb0922..307a9401ab 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -137,6 +137,7 @@ OBJS += kaspa.o OBJS += nexa.o OBJS += nostr.o OBJS += base64.o +OBJS += lnurl.o endif OBJS += debug.o @@ -237,6 +238,7 @@ OBJS += protob/messages-cardano.pb.o OBJS += protob/messages-kaspa.pb.o OBJS += protob/messages-nexa.pb.o OBJS += protob/messages-nostr.pb.o +OBJS += protob/messages-lnurl.pb.o endif OPTFLAGS ?= -Os diff --git a/legacy/firmware/cosmos_networks.c b/legacy/firmware/cosmos_networks.c index ed28b84397..6540250c68 100644 --- a/legacy/firmware/cosmos_networks.c +++ b/legacy/firmware/cosmos_networks.c @@ -2,35 +2,36 @@ #include const CosmosNetworkType cosmos_networks[COSMOS_NETWORK_COUNT] = { - {"cosmoshub-4", "Cosmos Hub", "ATOM", "uatom", 6}, - {"osmosis-1", "Osmosis", "OSMO", "uosmo", 6}, - {"secret-4", "Secret Network", "SCRT", "uscrt", 6}, - {"akashnet-2", "Akash", "AKT", "uakt", 6}, - {"crypto-org-chain-mainnet-1", "Crypto.org", "CRO", "basecro", 8}, - {"iov-mainnet-ibc", "Starname", "IOV", "uiov", 6}, - {"sifchain-1", "Sifchain", "ROWAN", "rowan", 18}, - {"shentu-2.2", "Shentu", "CTK", "uctk", 6}, - {"irishub-1", "IRISnet", "IRIS", "uiris", 6}, - {"regen-1", "Regen", "REGEN", "uregen", 6}, - {"core-1", "Persistence", "XPRT", "uxprt", 6}, - {"sentinelhub-2", "Sentinel", "DVPN", "udvpn", 6}, - {"ixo-4", "ixo", "IXO", "uixo", 6}, - {"emoney-3", "e-Money", "NGM", "ungm", 6}, - {"agoric-3", "Agoric", "BLD", "ubld", 6}, - {"bostrom", "Bostrom", "BOOT", "boot", 0}, - {"juno-1", "Juno", "JUNO", "ujuno", 6}, - {"stargaze-1", "Stargaze", "STARS", "ustars", 6}, - {"axelar-dojo-1", "Axelar", "AXL", "uaxl", 6}, - {"sommelier-3", "Sommelier", "SOMM", "usomm", 6}, - {"umee-1", "Umee", "UMEE", "uumee", 6}, - {"gravity-bridge-3", "Gravity Bridge", "GRAV", "ugraviton", 6}, - {"tgrade-mainnet-1", "Tgrade", "TGD", "utgd", 6}, - {"stride-1", "Stride", "STRD", "ustrd", 6}, - {"evmos_9001-2", "Evmos", "EVMOS", "aevmos", 18}, - {"injective-1", "Injective", "INJ", "inj", 18}, - {"kava_2222-10", "Kava", "KAVA", "ukava", 6}, - {"quicksilver-1", "Quicksilver", "QCK", "uqck", 6}, - {"fetchhub-4", "Fetch.ai", "FET", "afet", 18}, + {"cosmoshub-4", "cosmos", "Cosmos Hub", "ATOM", "uatom", 6}, + {"osmosis-1", "osmo", "Osmosis", "OSMO", "uosmo", 6}, + {"secret-4", "secret", "Secret Network", "SCRT", "uscrt", 6}, + {"akashnet-2", "akash", "Akash", "AKT", "uakt", 6}, + {"crypto-org-chain-mainnet-1", "cro", "Crypto.org", "CRO", "basecro", 8}, + {"iov-mainnet-ibc", "star", "Starname", "IOV", "uiov", 6}, + {"sifchain-1", "sif", "Sifchain", "ROWAN", "rowan", 18}, + {"shentu-2.2", "certik", "Shentu", "CTK", "uctk", 6}, + {"irishub-1", "iaa", "IRISnet", "IRIS", "uiris", 6}, + {"regen-1", "regen", "Regen", "REGEN", "uregen", 6}, + {"core-1", "persistence", "Persistence", "XPRT", "uxprt", 6}, + {"sentinelhub-2", "sent", "Sentinel", "DVPN", "udvpn", 6}, + {"ixo-4", "ixo", "ixo", "IXO", "uixo", 6}, + {"emoney-3", "emoney", "e-Money", "NGM", "ungm", 6}, + {"agoric-3", "agoric", "Agoric", "BLD", "ubld", 6}, + {"bostrom", "bostrom", "Bostrom", "BOOT", "boot", 0}, + {"juno-1", "juno", "Juno", "JUNO", "ujuno", 6}, + {"stargaze-1", "stars", "Stargaze", "STARS", "ustars", 6}, + {"axelar-dojo-1", "axelar", "Axelar", "AXL", "uaxl", 6}, + {"sommelier-3", "somm", "Sommelier", "SOMM", "usomm", 6}, + {"umee-1", "umee", "Umee", "UMEE", "uumee", 6}, + {"gravity-bridge-3", "gravity", "Gravity Bridge", "GRAV", "ugraviton", 6}, + {"tgrade-mainnet-1", "tgrade", "Tgrade", "TGD", "utgd", 6}, + {"stride-1", "stride", "Stride", "STRD", "ustrd", 6}, + {"evmos_9001-2", "evmos", "Evmos", "EVMOS", "aevmos", 18}, + {"injective-1", "inj", "Injective", "INJ", "inj", 18}, + {"kava_2222-10", "kava", "Kava", "KAVA", "ukava", 6}, + {"quicksilver-1", "quick", "Quicksilver", "QCK", "uqck", 6}, + {"fetchhub-4", "fetch", "Fetch.ai", "FET", "afet", 18}, + {"celestia", "celestia", "Celestia", "TIA", "utia", 6}, }; const CosmosNetworkType *cosmosnetworkByChainId(const char *chain_id) { @@ -41,3 +42,12 @@ const CosmosNetworkType *cosmosnetworkByChainId(const char *chain_id) { } return NULL; } + +const CosmosNetworkType *cosmosnetworkByHrp(const char *hrp) { + for (int i = 0; i < COSMOS_NETWORK_COUNT; i++) { + if (memcmp(hrp, cosmos_networks[i].hrp, strlen(hrp)) == 0) { + return &(cosmos_networks[i]); + } + } + return NULL; +} diff --git a/legacy/firmware/cosmos_networks.h b/legacy/firmware/cosmos_networks.h index 7cb48a58c4..c522f765b8 100644 --- a/legacy/firmware/cosmos_networks.h +++ b/legacy/firmware/cosmos_networks.h @@ -3,10 +3,11 @@ #include -#define COSMOS_NETWORK_COUNT 29 +#define COSMOS_NETWORK_COUNT 30 typedef struct { const char *const chain_id; + const char *const hrp; const char *const chain_name; const char *const coin_denom; const char *const coin_minimal_denom; @@ -16,5 +17,6 @@ typedef struct { extern const CosmosNetworkType cosmos_networks[COSMOS_NETWORK_COUNT]; const CosmosNetworkType *cosmosnetworkByChainId(const char *chain_id); +const CosmosNetworkType *cosmosnetworkByHrp(const char *hrp); #endif diff --git a/legacy/firmware/fsm.c b/legacy/firmware/fsm.c index 6df4db8374..b47d23af89 100644 --- a/legacy/firmware/fsm.c +++ b/legacy/firmware/fsm.c @@ -17,8 +17,9 @@ * along with this library. If not, see . */ +#include "fsm.h" #include - +#include #include "address.h" #include "aes/aes.h" #include "base58.h" @@ -34,7 +35,6 @@ #include "curves.h" #include "debug.h" #include "ecdsa.h" -#include "fsm.h" #include "fw_signatures.h" #include "gettext.h" #include "hmac.h" @@ -75,6 +75,7 @@ #include "ethereum_onekey.h" #include "filecoin.h" #include "kaspa.h" +#include "lnurl.h" #include "near.h" #include "nem.h" #include "nem2.h" @@ -579,6 +580,7 @@ bool fsm_layoutPathWarning(uint32_t address_n_count, #include "fsm_msg_ethereum_onekey.h" #include "fsm_msg_filecoin.h" #include "fsm_msg_kaspa.h" +#include "fsm_msg_lnurl.h" #include "fsm_msg_near.h" #include "fsm_msg_nem.h" #include "fsm_msg_nexa.h" diff --git a/legacy/firmware/fsm.h b/legacy/firmware/fsm.h index ac54a8660e..232f785c2c 100644 --- a/legacy/firmware/fsm.h +++ b/legacy/firmware/fsm.h @@ -33,6 +33,7 @@ #include "messages-ethereum.pb.h" #include "messages-filecoin.pb.h" #include "messages-kaspa.pb.h" +#include "messages-lnurl.pb.h" #include "messages-management.pb.h" #include "messages-near.pb.h" #include "messages-nem.pb.h" @@ -248,6 +249,7 @@ void fsm_msgRippleSignTx(RippleSignTx *msg); void fsm_msgSuiGetAddress(const SuiGetAddress *msg); void fsm_msgSuiSignTx(const SuiSignTx *msg); void fsm_msgSuiSignMessage(SuiSignMessage *msg); +void fsm_msgSuiTxAck(SuiTxAck *msg); // filecoin void fsm_msgFilecoinGetAddress(const FilecoinGetAddress *msg); @@ -313,4 +315,7 @@ void fsm_msgNostrEncryptMessage(NostrEncryptMessage *msg); void fsm_msgNostrDecryptMessage(NostrDecryptMessage *msg); void fsm_msgNostrSignSchnorr(const NostrSignSchnorr *msg); +// lnurl +void fsm_msgLnurlAuth(const LnurlAuth *msg); + #endif diff --git a/legacy/firmware/fsm_msg_lnurl.h b/legacy/firmware/fsm_msg_lnurl.h new file mode 100644 index 0000000000..98230d7cf2 --- /dev/null +++ b/legacy/firmware/fsm_msg_lnurl.h @@ -0,0 +1,101 @@ +/* + * This file is part of the OneKey project, https://onekey.so/ + * + * Copyright (C) 2021 OneKey Team + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +extern int ethereum_is_canonic(uint8_t v, uint8_t signature[64]); + +void fsm_msgLnurlAuth(const LnurlAuth *msg) { + CHECK_INITIALIZED + + CHECK_PIN + + RESP_INIT(LnurlAuthResp); + + if (!layout_lnurl_auth(msg)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + } + uint32_t address_n[8] = {0}; + // # m/138'/0 + address_n[0] = 2147483786; + address_n[1] = 0; + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, address_n, 2, NULL); + if (!node) { + fsm_sendFailure(FailureType_Failure_ProcessError, "Failed to derive node"); + layoutHome(); + return; + } + + uint8_t dm[32] = {0}; + hmac_sha256(msg->domain.bytes, msg->domain.size, node->private_key, 32, dm); + + // "m/138'/{long1}/{long2}/{long3}/{long4}" + address_n[1] = (dm[0] << 24) + (dm[1] << 16) + (dm[2] << 8) + dm[3]; + address_n[2] = (dm[4] << 24) + (dm[5] << 16) + (dm[6] << 8) + dm[7]; + address_n[3] = (dm[8] << 24) + (dm[9] << 16) + (dm[10] << 8) + dm[11]; + address_n[4] = (dm[12] << 24) + (dm[13] << 16) + (dm[14] << 8) + dm[15]; + + resp->has_path = true; +#if !EMULATOR + snprintf(resp->path, 64, "m/138'/%ld/%ld/%ld/%ld", address_n[1], address_n[2], + address_n[3], address_n[4]); +#else + snprintf(resp->path, 64, "m/138'/%d/%d/%d/%d", address_n[1], address_n[2], + address_n[3], address_n[4]); +#endif + + if (msg->data.size > 0) { + if (msg->data.size != 32) { + fsm_sendFailure(FailureType_Failure_ProcessError, "unexpected data size"); + layoutHome(); + return; + } + + node = fsm_getDerivedNode(SECP256K1_NAME, address_n, 5, NULL); + if (!node) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Failed to derive node"); + layoutHome(); + return; + } + + uint8_t v; + uint8_t sig[65] = {0}; + if (ecdsa_sign_digest(&secp256k1, node->private_key, msg->data.bytes, sig, + &v, ethereum_is_canonic) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); + return; + } + if (hdnode_fill_public_key(node) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive public key")); + layoutHome(); + return; + } + + resp->has_signature = true; + resp->signature.size = ecdsa_sig_to_der(sig, resp->signature.bytes); + } + + resp->has_publickey = true; + data2hexaddr(node->public_key, 33, resp->publickey); + + msg_write(MessageType_MessageType_LnurlAuthResp, resp); + + layoutHome(); +} diff --git a/legacy/firmware/fsm_msg_sui.h b/legacy/firmware/fsm_msg_sui.h index c6c3031ee5..94fdffc093 100644 --- a/legacy/firmware/fsm_msg_sui.h +++ b/legacy/firmware/fsm_msg_sui.h @@ -59,10 +59,19 @@ void fsm_msgSuiSignTx(const SuiSignTx *msg) { if (!node) return; hdnode_fill_public_key(node); + if (msg->has_data_length && msg->data_length > 0) { + sui_signing_init(msg, node); + } else { + sui_sign_tx(msg, node, resp); + layoutHome(); + } +} - sui_sign_tx(msg, node, resp); +void fsm_msgSuiTxAck(SuiTxAck *msg) { + CHECK_UNLOCKED layoutHome(); + sui_signing_txack(msg); } void fsm_msgSuiSignMessage(SuiSignMessage *msg) { diff --git a/legacy/firmware/language.c b/legacy/firmware/language.c index 3b2637a649..4d282e1e00 100644 --- a/legacy/firmware/language.c +++ b/legacy/firmware/language.c @@ -272,6 +272,8 @@ const char *languages[][2] = { {"Create multisig", "创建多重签名"}, {"DONE", "完成"}, {"Dashboard", "控制中心"}, + // lnurl.c + {"Data", "数据"}, // fsm.c {"Data error", "数据错误"}, // ethereum.c @@ -352,6 +354,8 @@ const char *languages[][2] = { // layout2.c {"Do you want to sign in?", "请确认登录"}, {"Do you want to sign this\n", "确认要签署此 "}, + // lnurl + {"Domain", "域名"}, // {"Done", "完成"}, {"Download OneKey", "下载OneKey"}, diff --git a/legacy/firmware/layout2.c b/legacy/firmware/layout2.c index 240c0681c0..65558a0a19 100644 --- a/legacy/firmware/layout2.c +++ b/legacy/firmware/layout2.c @@ -3405,7 +3405,7 @@ void layoutDeviceParameters(int num) { #ifdef BUILD_ID oledDrawStringAdapter(0, y, _("BUILD ID:"), FONT_STANDARD); y += font->pixel + 1; - oledDrawStringAdapter(0, y, BUILD_ID, FONT_FIXED); + oledDrawStringAdapter(0, y, BUILD_ID + strlen(BUILD_ID) - 7, FONT_FIXED); y += font->pixel + 1; #endif break; diff --git a/legacy/firmware/lnurl.c b/legacy/firmware/lnurl.c new file mode 100644 index 0000000000..005b0c1d3a --- /dev/null +++ b/legacy/firmware/lnurl.c @@ -0,0 +1,41 @@ +#include +#include "aes/aes.h" +#include "base64.h" +#include "buttons.h" +#include "fsm.h" +#include "gettext.h" +#include "layout2.h" +#include "messages.h" +#include "messages.pb.h" +#include "nostr.h" +#include "protect.h" +#include "ripemd160.h" +#include "rng.h" +#include "secp256k1.h" +#include "segwit_addr.h" +#include "sha3.h" +#include "stdint.h" +#include "transaction.h" +#include "util.h" +#include "zkp_bip340.h" + +bool layout_lnurl_auth(const LnurlAuth *msg) { + char data[65] = {0}; + if (msg->data.size > 0 && msg->data.size != 32) return false; + data2hexaddr(msg->data.bytes, msg->data.size, data); + + if (!fsm_layoutSignMessage_ex(_("Domain"), (const uint8_t *)msg->domain.bytes, + msg->domain.size)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + if (!fsm_layoutSignMessage_ex(_("Data"), (const uint8_t *)data, + strlen(data))) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + + return true; +} diff --git a/legacy/firmware/lnurl.h b/legacy/firmware/lnurl.h new file mode 100644 index 0000000000..7fba598b7e --- /dev/null +++ b/legacy/firmware/lnurl.h @@ -0,0 +1,27 @@ +/* + * This file is part of the OneKey project, https://onekey.so/ + * + * Copyright (C) 2021 OneKey Team + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef __LNURL_H__ +#define __LNURL_H__ + +#include "messages-lnurl.pb.h" + +bool layout_lnurl_auth(const LnurlAuth *msg); + +#endif // __LNURL_H__ diff --git a/legacy/firmware/polkadot.c b/legacy/firmware/polkadot.c index e5e74ce4d3..64da71b1d8 100644 --- a/legacy/firmware/polkadot.c +++ b/legacy/firmware/polkadot.c @@ -173,6 +173,8 @@ static bool get_signer_address(const PolkadotSignTx *msg, const HDNode *node, addressType = 42; } else if (!strncmp(msg->network, "joystream", 9)) { addressType = 126; + } else if (!strncmp(msg->network, "manta", 5)) { + addressType = 77; } else { return false; } diff --git a/legacy/firmware/polkadot/parser_impl_common.c b/legacy/firmware/polkadot/parser_impl_common.c index 3d6b921156..a5fa15dea3 100644 --- a/legacy/firmware/polkadot/parser_impl_common.c +++ b/legacy/firmware/polkadot/parser_impl_common.c @@ -381,6 +381,10 @@ uint16_t _detectAddressType(const parser_context_t *c) { __polkadot_dicimal = COIN_AMOUNT_DECIMAL_PLACES; memcpy(__polkadot_ticker, JOY_COIN_TICKER, 4); return 126; + } else if (!strncmp(polkadot_network, "manta", 5)) { + __polkadot_dicimal = COIN_AMOUNT_DECIMAL_PLACES_18; + memcpy(__polkadot_ticker, MANTA_COIN_TICKER, 6); + return 77; } return 42; diff --git a/legacy/firmware/polkadot/substrate/substrate_coin.h b/legacy/firmware/polkadot/substrate/substrate_coin.h index a232a9b7f7..7a40805d7f 100644 --- a/legacy/firmware/polkadot/substrate/substrate_coin.h +++ b/legacy/firmware/polkadot/substrate/substrate_coin.h @@ -15,5 +15,6 @@ #define WESTEND_COIN_TICKER " WND" #define ASTAR_COIN_TICKER " ASTR" #define JOY_COIN_TICKER " JOY" +#define MANTA_COIN_TICKER " MANTA" #endif diff --git a/legacy/firmware/polkadot/substrate/substrate_dispatch.c b/legacy/firmware/polkadot/substrate/substrate_dispatch.c index 510e27ad67..bb6e1ecbff 100644 --- a/legacy/firmware/polkadot/substrate/substrate_dispatch.c +++ b/legacy/firmware/polkadot/substrate/substrate_dispatch.c @@ -17,6 +17,8 @@ parser_error_t _readMethod(parser_context_t* c, uint8_t moduleIdx, return _readMethod_astar(c, moduleIdx, callIdx, &method->V18); } else if (!strncmp(polkadot_network, "joystream", 9)) { return _readMethod_joystream(c, moduleIdx, callIdx, &method->V18); + } else if (!strncmp(polkadot_network, "manta", 5)) { + return _readMethod_manta(c, moduleIdx, callIdx, &method->V18); } else { return parser_tx_version_not_supported; } diff --git a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.c b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.c index 14104396fb..bd49cfe694 100644 --- a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.c +++ b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.c @@ -207,6 +207,33 @@ parser_error_t _readMethod_joystream(parser_context_t* c, uint8_t moduleIdx, return parser_ok; } +parser_error_t _readMethod_manta(parser_context_t* c, uint8_t moduleIdx, + uint8_t callIdx, pd_Method_V18_t* method) { + uint16_t callPrivIdx = ((uint16_t)moduleIdx << 8u) + callIdx; + switch (callPrivIdx) { + case 2567: + CHECK_ERROR(_readMethod_balances_transfer_V18( + c, &method->nested.balances_transfer_V18)) + break; + case 2562: + CHECK_ERROR(_readMethod_balances_force_transfer_V18( + c, &method->nested.balances_force_transfer_V18)) + break; + case 2563: /* module 5 call 3 */ + CHECK_ERROR(_readMethod_balances_transfer_keep_alive_V18( + c, &method->nested.balances_transfer_keep_alive_V18)) + break; + case 2564: /* module 5 call 4 */ + CHECK_ERROR(_readMethod_balances_transfer_all_V18( + c, &method->basic.balances_transfer_all_V18)) + break; + default: + return parser_unexpected_callIndex; + } + + return parser_ok; +} + ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// @@ -235,18 +262,22 @@ const char* _getMethod_Name_V18(uint8_t moduleIdx, uint8_t callIdx) { case 1031: case 7936: case 7943: + case 2567: return STR_ME_TRANSFER; case 1282: /* module 5 call 2 */ case 1026: case 7938: + case 2562: return STR_ME_FORCE_TRANSFER; case 1283: /* module 5 call 3 */ case 1027: case 7939: + case 2563: return STR_ME_TRANSFER_KEEP_ALIVE; case 1284: /* module 5 call 4 */ case 1028: case 7940: + case 2564: return STR_ME_TRANSFER_ALL; default: return _getMethod_Name_V18_ParserFull(callPrivIdx); @@ -274,18 +305,22 @@ uint8_t _getMethod_NumItems_V18(uint8_t moduleIdx, uint8_t callIdx) { case 1031: case 7936: case 7943: + case 2567: return 2; case 1282: /* module 5 call 2 */ case 1026: case 7938: + case 2562: return 3; case 1283: /* module 5 call 3 */ case 1027: case 7939: + case 2563: return 2; case 1284: /* module 5 call 4 */ case 1028: case 7940: + case 2564: return 2; default: return 0; @@ -305,6 +340,7 @@ const char* _getMethod_ItemName_V18(uint8_t moduleIdx, uint8_t callIdx, case 1031: case 7936: case 7943: + case 2567: switch (itemIdx) { case 0: return STR_IT_amount; @@ -316,6 +352,7 @@ const char* _getMethod_ItemName_V18(uint8_t moduleIdx, uint8_t callIdx, case 1282: /* module 5 call 2 */ case 1026: case 7938: + case 2562: switch (itemIdx) { case 0: return STR_IT_amount; @@ -329,6 +366,7 @@ const char* _getMethod_ItemName_V18(uint8_t moduleIdx, uint8_t callIdx, case 1283: /* module 5 call 3 */ case 1027: case 7939: + case 2563: switch (itemIdx) { case 0: return STR_IT_amount; @@ -340,6 +378,7 @@ const char* _getMethod_ItemName_V18(uint8_t moduleIdx, uint8_t callIdx, case 1284: /* module 5 call 4 */ case 1028: case 7940: + case 2564: switch (itemIdx) { case 0: return STR_IT_dest; @@ -368,6 +407,7 @@ parser_error_t _getMethod_ItemValue_V18(pd_Method_V18_t* m, uint8_t moduleIdx, case 1031: /* module 5 call 0 */ case 7936: /* module 5 call 0 */ case 7943: + case 2567: switch (itemIdx) { case 0: /* balances_transfer_V18 - amount */; return _toStringCompactBalance( @@ -383,6 +423,7 @@ parser_error_t _getMethod_ItemValue_V18(pd_Method_V18_t* m, uint8_t moduleIdx, case 1282: /* module 5 call 2 */ case 1026: /* module 5 call 2 */ case 7938: /* module 5 call 2 */ + case 2562: switch (itemIdx) { case 0: /* balances_force_transfer_V18 - amount */; return _toStringCompactBalance( @@ -402,6 +443,7 @@ parser_error_t _getMethod_ItemValue_V18(pd_Method_V18_t* m, uint8_t moduleIdx, case 1283: /* module 5 call 3 */ case 1027: /* module 5 call 3 */ case 7939: /* module 5 call 3 */ + case 2563: switch (itemIdx) { case 0: /* balances_transfer_keep_alive_V18 - amount */; return _toStringCompactBalance( @@ -417,6 +459,7 @@ parser_error_t _getMethod_ItemValue_V18(pd_Method_V18_t* m, uint8_t moduleIdx, case 1284: /* module 5 call 4 */ case 1028: /* module 5 call 4 */ case 7940: /* module 5 call 4 */ + case 2564: switch (itemIdx) { case 0: /* balances_transfer_all_V18 - dest */; return _toStringAccountIdLookupOfT_V18( @@ -448,7 +491,6 @@ bool _getMethod_ItemIsExpert_V18(uint8_t moduleIdx, uint8_t callIdx, bool _getMethod_IsNestingSupported_V18(uint8_t moduleIdx, uint8_t callIdx) { uint16_t callPrivIdx = ((uint16_t)moduleIdx << 8u) + callIdx; - switch (callPrivIdx) { // Balances & BlindSign case 1280: /* module 5 call 0 */ case 1287: /* module 5 call 0 */ @@ -462,6 +504,10 @@ bool _getMethod_IsNestingSupported_V18(uint8_t moduleIdx, uint8_t callIdx) { case 1283: case 1027: case 7939: + case 2567: + case 2562: + case 2563: + case 2564: return true; default: return false; diff --git a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h index 03ee75c571..cbd918265a 100644 --- a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h +++ b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h @@ -17,9 +17,10 @@ parser_error_t _readMethod_V18_westend(parser_context_t* c, uint8_t moduleIdx, pd_Method_V18_t* method); parser_error_t _readMethod_astar(parser_context_t* c, uint8_t moduleIdx, uint8_t callIdx, pd_Method_V18_t* method); - parser_error_t _readMethod_joystream(parser_context_t* c, uint8_t moduleIdx, uint8_t callIdx, pd_Method_V18_t* method); +parser_error_t _readMethod_manta(parser_context_t* c, uint8_t moduleIdx, + uint8_t callIdx, pd_Method_V18_t* method); const char* _getMethod_ModuleName_V18(uint8_t moduleIdx); diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 4f2b3c969a..c9a5d05ad1 100755 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -23,7 +23,7 @@ PROTO_NAMES = messages messages-bitcoin messages-common messages-crypto messages messages-management messages-nem messages-stellar messages-solana \ messages-starcoin messages-tron messages-aptos messages-near messages-conflux \ messages-algorand messages-ripple messages-sui messages-filecoin messages-cosmos \ - messages-polkadot messages-cardano messages-kaspa messages-nexa messages-nostr + messages-polkadot messages-cardano messages-kaspa messages-nexa messages-nostr messages-lnurl PROTO_OPTIONS = $(PROTO_NAMES:=.options) PROTO_COMPILED = $(PROTO_NAMES:=.pb) diff --git a/legacy/firmware/protob/messages-lnurl.options b/legacy/firmware/protob/messages-lnurl.options new file mode 100644 index 0000000000..cd29a3a1b5 --- /dev/null +++ b/legacy/firmware/protob/messages-lnurl.options @@ -0,0 +1,6 @@ +LnurlAuth.domain max_size:256 +LnurlAuth.data max_size:33 + +LnurlAuthResp.publickey max_size:67 +LnurlAuthResp.path max_size:64 +LnurlAuthResp.signature max_size:128 diff --git a/legacy/firmware/protob/messages-lnurl.proto b/legacy/firmware/protob/messages-lnurl.proto new file mode 120000 index 0000000000..e1958fccac --- /dev/null +++ b/legacy/firmware/protob/messages-lnurl.proto @@ -0,0 +1 @@ +../../vendor/trezor-common/protob/messages-lnurl.proto \ No newline at end of file diff --git a/legacy/firmware/protob/messages-polkadot.options b/legacy/firmware/protob/messages-polkadot.options index 15c6e1a637..35a07a9edb 100755 --- a/legacy/firmware/protob/messages-polkadot.options +++ b/legacy/firmware/protob/messages-polkadot.options @@ -4,7 +4,7 @@ PolkadotAddress.address max_size:64 PolkadotAddress.public_key max_size:65 PolkadotSignTx.address_n max_count:8 -PolkadotSignTx.raw_tx max_size:1024 +PolkadotSignTx.raw_tx max_size:4096 PolkadotSignTx.network max_size:64 PolkadotSignedTx.signature max_size:64 diff --git a/legacy/firmware/protob/messages-sui.options b/legacy/firmware/protob/messages-sui.options index 4825bafe74..7bb8800418 100644 --- a/legacy/firmware/protob/messages-sui.options +++ b/legacy/firmware/protob/messages-sui.options @@ -3,6 +3,12 @@ SuiAddress.address max_size:70 SuiSignTx.address_n max_count:8 SuiSignTx.raw_tx max_size:6144 +SuiSignTx.data_initial_chunk max_size:1024 + +SuiTxRequest.public_key max_size:32 +SuiTxRequest.signature max_size:64 + +SuiTxAck.data_chunk max_size:1024 SuiSignedTx.public_key max_size:32 SuiSignedTx.signature max_size:64 diff --git a/legacy/firmware/sui.c b/legacy/firmware/sui.c index e1862033a2..8cad37180e 100644 --- a/legacy/firmware/sui.c +++ b/legacy/firmware/sui.c @@ -2,12 +2,20 @@ #include "fsm.h" #include "gettext.h" #include "layout2.h" +#include "memzero.h" #include "messages.h" #include "messages.pb.h" #include "protect.h" #include "stdint.h" #include "util.h" +static bool sui_signing = false; +static uint32_t data_total, data_left; +static CONFIDENTIAL uint8_t privkey[32]; +static uint8_t pubkey[32]; +static BLAKE2B_CTX hash_ctx = {0}; +static SuiTxRequest msg_tx_request; + void sui_get_address_from_public_key(const uint8_t *public_key, char *address) { uint8_t buf[32] = {0}; BLAKE2B_CTX ctx; @@ -79,4 +87,103 @@ void sui_message_sign(const SuiSignMessage *msg, const HDNode *node, ed25519_sign(digest, 32, node->private_key, resp->signature.bytes); resp->signature.size = 64; msg_write(MessageType_MessageType_SuiMessageSignature, resp); +} + +void sui_signing_abort(void) { + if (sui_signing) { + memzero(privkey, sizeof(privkey)); + layoutHome(); + sui_signing = false; + } +} + +static inline void hash_data(const uint8_t *buf, size_t size) { + blake2b_Update(&hash_ctx, buf, size); +} + +static void send_signature(void) { + uint8_t digest[32] = {0}; + SuiSignedTx tx; + + blake2b_Final(&hash_ctx, digest, 32); + + ed25519_sign(digest, 32, privkey, tx.signature.bytes); + memcpy(tx.public_key.bytes, pubkey, 32); + tx.signature.size = 64; + tx.public_key.size = 32; + msg_write(MessageType_MessageType_SuiSignedTx, &tx); + + memzero(privkey, sizeof(privkey)); + sui_signing_abort(); +} + +static void send_request_chunk(void) { + msg_tx_request.has_data_length = true; + msg_tx_request.data_length = data_left <= 1024 ? data_left : 1024; + msg_write(MessageType_MessageType_SuiTxRequest, &msg_tx_request); +} + +void sui_signing_init(const SuiSignTx *msg, const HDNode *node) { + char address[67] = {0}; + + sui_signing = true; + blake2b_Init(&hash_ctx, 32); + + sui_get_address_from_public_key(node->public_key + 1, address); + // INTENT_BYTES = b'\x00\x00\x00' + if ((msg->data_initial_chunk.bytes[0] != 0x00) && + ((msg->data_initial_chunk.bytes[1] != 0x00)) && + ((msg->data_initial_chunk.bytes[2] != 0x00))) { + fsm_sendFailure(FailureType_Failure_DataError, "Invalid raw tx"); + sui_signing_abort(); + return; + } + if (!layoutBlindSign("Sui", address)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + "Signing cancelled by user"); + layoutHome(); + return; + } + + memcpy(privkey, node->private_key, 32); + memcpy(pubkey, node->public_key + 1, 32); + + hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); + + data_total = msg->data_length; + data_left = data_total - msg->data_initial_chunk.size; + if (data_left > 0) { + send_request_chunk(); + } else { + send_signature(); + } +} + +void sui_signing_txack(SuiTxAck *tx) { + if (!sui_signing) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Not in sui signing mode")); + layoutHome(); + return; + } + if (tx->data_chunk.size > data_left) { + fsm_sendFailure(FailureType_Failure_DataError, _("Too much data")); + sui_signing_abort(); + return; + } + if (data_left > 0 && tx->data_chunk.size == 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Empty data chunk received")); + sui_signing_abort(); + return; + } + hash_data(tx->data_chunk.bytes, tx->data_chunk.size); + + data_left -= tx->data_chunk.size; + + if (data_left > 0) { + send_request_chunk(); + } else { + send_signature(); + } } \ No newline at end of file diff --git a/legacy/firmware/sui.h b/legacy/firmware/sui.h index 3dbe7f6465..5e40c48a71 100644 --- a/legacy/firmware/sui.h +++ b/legacy/firmware/sui.h @@ -27,4 +27,6 @@ void sui_get_address_from_public_key(const uint8_t *public_key, char *address); void sui_sign_tx(const SuiSignTx *msg, const HDNode *node, SuiSignedTx *resp); void sui_message_sign(const SuiSignMessage *msg, const HDNode *node, SuiMessageSignature *resp); +void sui_signing_init(const SuiSignTx *msg, const HDNode *node); +void sui_signing_txack(SuiTxAck *msg); #endif // __SUI_H__ diff --git a/legacy/firmware/version.h b/legacy/firmware/version.h index 984846d639..208ddc8e3b 100755 --- a/legacy/firmware/version.h +++ b/legacy/firmware/version.h @@ -6,5 +6,5 @@ #define FIX_VERSION_MINOR 99 #define FIX_VERSION_PATCH 99 -#define ONEKEY_VERSION "3.6.0" -#define ONEKEY_VERSION_HEX 0x3600 +#define ONEKEY_VERSION "3.7.0" +#define ONEKEY_VERSION_HEX 0x3700 diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index ecc41c0748..63589effca 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -350,6 +350,8 @@ class MessageType(IntEnum): SuiSignedTx = 11103 SuiSignMessage = 11104 SuiMessageSignature = 11105 + SuiTxRequest = 11106 + SuiTxAck = 11107 FilecoinGetAddress = 11200 FilecoinAddress = 11201 FilecoinSignTx = 11202 @@ -376,6 +378,8 @@ class MessageType(IntEnum): NostrDecryptedMessage = 11507 NostrSignSchnorr = 11508 NostrSignedSchnorr = 11509 + LnurlAuth = 11600 + LnurlAuthResp = 11601 DeviceInfoSettings = 10001 GetDeviceInfo = 10002 DeviceInfo = 10003 @@ -6776,6 +6780,43 @@ def __init__( self.signature = signature +class LnurlAuth(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11600 + FIELDS = { + 2: protobuf.Field("domain", "bytes", repeated=False, required=True), + 3: protobuf.Field("data", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + domain: "bytes", + data: "bytes", + ) -> None: + self.domain = domain + self.data = data + + +class LnurlAuthResp(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11601 + FIELDS = { + 1: protobuf.Field("publickey", "string", repeated=False, required=False, default=None), + 2: protobuf.Field("path", "string", repeated=False, required=False, default=None), + 3: protobuf.Field("signature", "bytes", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + publickey: Optional["str"] = None, + path: Optional["str"] = None, + signature: Optional["bytes"] = None, + ) -> None: + self.publickey = publickey + self.path = path + self.signature = signature + + class MoneroTransactionSourceEntry(protobuf.MessageType): MESSAGE_WIRE_TYPE = None FIELDS = { @@ -9327,6 +9368,8 @@ class SuiSignTx(protobuf.MessageType): FIELDS = { 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), 2: protobuf.Field("raw_tx", "bytes", repeated=False, required=True), + 3: protobuf.Field("data_initial_chunk", "bytes", repeated=False, required=False, default=b''), + 4: protobuf.Field("data_length", "uint32", repeated=False, required=False, default=None), } def __init__( @@ -9334,9 +9377,13 @@ def __init__( *, raw_tx: "bytes", address_n: Optional[Sequence["int"]] = None, + data_initial_chunk: Optional["bytes"] = b'', + data_length: Optional["int"] = None, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.raw_tx = raw_tx + self.data_initial_chunk = data_initial_chunk + self.data_length = data_length class SuiSignedTx(protobuf.MessageType): @@ -9356,6 +9403,40 @@ def __init__( self.signature = signature +class SuiTxRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11106 + FIELDS = { + 1: protobuf.Field("data_length", "uint32", repeated=False, required=False, default=None), + 2: protobuf.Field("public_key", "bytes", repeated=False, required=False, default=None), + 3: protobuf.Field("signature", "bytes", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + data_length: Optional["int"] = None, + public_key: Optional["bytes"] = None, + signature: Optional["bytes"] = None, + ) -> None: + self.data_length = data_length + self.public_key = public_key + self.signature = signature + + +class SuiTxAck(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11107 + FIELDS = { + 1: protobuf.Field("data_chunk", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + data_chunk: "bytes", + ) -> None: + self.data_chunk = data_chunk + + class SuiSignMessage(protobuf.MessageType): MESSAGE_WIRE_TYPE = 11104 FIELDS = {