diff --git a/common/defs/support.json b/common/defs/support.json index d98e4ee948..a30e233a78 100644 --- a/common/defs/support.json +++ b/common/defs/support.json @@ -1605,22 +1605,14 @@ "erc20:eth:pBTC": "1.9.1", "erc20:eth:pUSD": "1.9.1", "erc20:eth:uni0xBTC": "1.9.1", - "erc20:eth:uniAMN": "1.9.1", "erc20:eth:uniAMPL": "1.9.1", "erc20:eth:uniANT": "1.9.1", "erc20:eth:uniBAT": "1.9.1", "erc20:eth:uniBLT": "1.9.1", - "erc20:eth:uniBNT": "1.9.1", - "erc20:eth:uniC20": "1.9.1", - "erc20:eth:uniCELR": "1.9.1", "erc20:eth:uniCHAI": "1.9.1", - "erc20:eth:uniCVC": "1.9.1", "erc20:eth:uniDAI": "1.9.1", - "erc20:eth:uniDATA": "1.9.1", - "erc20:eth:uniDGD": "1.9.1", "erc20:eth:uniDGX": "1.9.1", "erc20:eth:uniDIP": "1.9.1", - "erc20:eth:uniDONUT": "1.9.1", "erc20:eth:uniENJ": "1.9.1", "erc20:eth:uniFAME": "1.9.1", "erc20:eth:uniFOAM": "1.9.1", @@ -1628,49 +1620,23 @@ "erc20:eth:uniGEN": "1.9.1", "erc20:eth:uniGNO": "1.9.1", "erc20:eth:uniGRID": "1.9.1", - "erc20:eth:uniGST2": "1.9.1", - "erc20:eth:uniHOT": "1.9.1", - "erc20:eth:uniIOTX": "1.9.1", - "erc20:eth:uniKIN": "1.9.1", - "erc20:eth:uniKNC": "1.9.1", "erc20:eth:uniLEND": "1.9.1", "erc20:eth:uniLINK": "1.9.1", "erc20:eth:uniLOOM": "1.9.1", "erc20:eth:uniLPT": "1.9.1", "erc20:eth:uniLQD": "1.9.1", - "erc20:eth:uniLRC": "1.9.1", "erc20:eth:uniMANA": "1.9.1", - "erc20:eth:uniMATIC": "1.9.1", - "erc20:eth:uniMBC": "1.9.1", - "erc20:eth:uniMGN": "1.9.1", "erc20:eth:uniMKR": "1.9.1", - "erc20:eth:uniMLN": "1.9.1", - "erc20:eth:uniMOD": "1.9.1", "erc20:eth:uniNEXO": "1.9.1", "erc20:eth:uniNMR": "1.9.1", - "erc20:eth:uniOXT": "1.9.1", - "erc20:eth:uniPAN": "1.9.1", - "erc20:eth:uniPAX": "1.9.1", "erc20:eth:uniPAXG": "1.9.1", "erc20:eth:uniPNK": "1.9.1", - "erc20:eth:uniPOA20": "1.9.1", "erc20:eth:uniQCH": "1.9.1", - "erc20:eth:uniQSP": "1.9.1", "erc20:eth:uniRCN": "1.9.1", - "erc20:eth:uniRDN": "1.9.1", - "erc20:eth:uniREN": "1.9.1", - "erc20:eth:uniREP": "1.9.1", - "erc20:eth:uniRING": "1.9.1", - "erc20:eth:uniRLC": "1.9.1", "erc20:eth:uniRPL": "1.9.1", "erc20:eth:uniSAI": "1.9.1", - "erc20:eth:uniSALT": "1.9.1", - "erc20:eth:uniSAN": "1.9.1", - "erc20:eth:uniSHUF": "1.9.1", - "erc20:eth:uniSNT": "1.9.1", "erc20:eth:uniSNX": "1.9.1", "erc20:eth:uniSOCKS": "1.9.1", - "erc20:eth:uniSPANK": "1.9.1", "erc20:eth:uniSTORJ": "1.9.1", "erc20:eth:uniTAUD": "1.9.1", "erc20:eth:uniTCAD": "1.9.1", @@ -1843,30 +1809,10 @@ "erc20:eth:univ2sXAUUSDC": "1.9.3", "erc20:eth:univ2uTOPIAETH": "1.9.3", "erc20:eth:univ2wNXMETH": "1.9.5", - "erc20:eth:usBAT": "1.9.0", - "erc20:eth:usBAT2x": "1.9.0", - "erc20:eth:usBAT3x": "1.9.0", - "erc20:eth:usBAT4x": "1.9.0", - "erc20:eth:usETH": "1.9.0", - "erc20:eth:usETH2x": "1.9.0", - "erc20:eth:usETH3x": "1.9.0", - "erc20:eth:usETH4x": "1.9.0", - "erc20:eth:usKNC": "1.9.0", - "erc20:eth:usKNC2x": "1.9.0", - "erc20:eth:usKNC3x": "1.9.0", - "erc20:eth:usKNC4x": "1.9.0", - "erc20:eth:usREP": "1.9.0", - "erc20:eth:usREP2x": "1.9.0", - "erc20:eth:usREP3x": "1.9.0", - "erc20:eth:usREP4x": "1.9.0", "erc20:eth:usWBTC": "1.9.0", "erc20:eth:usWBTC2x": "1.9.0", "erc20:eth:usWBTC3x": "1.9.0", "erc20:eth:usWBTC4x": "1.9.0", - "erc20:eth:usZRX": "1.9.0", - "erc20:eth:usZRX2x": "1.9.0", - "erc20:eth:usZRX3x": "1.9.0", - "erc20:eth:usZRX4x": "1.9.0", "erc20:eth:veOGV": "1.11.3", "erc20:eth:xDOT": "1.9.0", "erc20:eth:xEDG": "1.9.0", diff --git a/common/protob/messages-nostr.proto b/common/protob/messages-nostr.proto new file mode 100644 index 0000000000..b7969e7ccc --- /dev/null +++ b/common/protob/messages-nostr.proto @@ -0,0 +1,101 @@ +syntax = "proto2"; +package hw.trezor.messages.nostr; + +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.trezor.lib.protobuf"; +option java_outer_classname = "TrezorMessageNostr"; + +/** + * Request: PublicKey at the specified index + * @start + * @next NostrPublicKey + */ +message NostrGetPublicKey { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional bool show_display = 2; // optionally show on display before sending the result +} + +/** + * Response: NostrPublicKey for the given index + * @end + */ +message NostrPublicKey { + optional string publickey = 1; + optional string npub = 2; +} + +/** + * Request: Ask device to sign Nostr event + * @start + * @next NostrSignEvent + */ +message NostrSignEvent { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required bytes event = 2; // serialized raw transaction +} + +/** + * Response: Contains nostr event signature + * @end + */ +message NostrSignedEvent { + required bytes event = 1; +} + +/** + * Request: Ask device to sign Nostr hash + * @start + * @next NostrSignedSchnorr + */ +message NostrSignSchnorr { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required string hash = 2; // hash +} + +/** + * Response: Contains nostr hash signature + * @end + */ +message NostrSignedSchnorr { + required bytes signature = 1; +} + +/** + * Request: Ask device to encrypt nostr message + * @start + * @next NostrEncryptedMessage + */ +message NostrEncryptMessage { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required string pubkey = 2; // pubkey + required string msg = 3; // message + optional bool show_display = 4; // optionally show on display before sending the result +} + +/** + * Response: Contains encrypted message + * @end + */ +message NostrEncryptedMessage { + required string msg = 1; +} + +/** + * Request: Ask device to decrypt nostr message + * @start + * @next NostrEncryptedMessage + */ +message NostrDecryptMessage { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required string pubkey = 2; // pubkey + required string msg = 3; // message + optional bool show_display = 4; // optionally show on display before sending the result +} + +/** + * Response: Contains decrypted message + * @end + */ +message NostrDecryptedMessage { + required string msg = 1; +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index bacd12fb0b..2df70eefcf 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -495,6 +495,18 @@ enum MessageType { MessageType_NexaTxInputRequest = 11404 [(wire_out) = true]; MessageType_NexaTxInputAck = 11405 [(wire_in) = true]; + // Nostr + MessageType_NostrGetPublicKey = 11500 [(wire_in) = true]; + MessageType_NostrPublicKey = 11501 [(wire_out) = true]; + MessageType_NostrSignEvent = 11502 [(wire_in) = true]; + MessageType_NostrSignedEvent = 11503 [(wire_out) = true]; + MessageType_NostrEncryptMessage = 11504 [(wire_in) = true]; + MessageType_NostrEncryptedMessage = 11505 [(wire_out) = true]; + MessageType_NostrDecryptMessage = 11506 [(wire_in) = true]; + MessageType_NostrDecryptedMessage = 11507 [(wire_out) = true]; + MessageType_NostrSignSchnorr = 11508 [(wire_in) = true]; + MessageType_NostrSignedSchnorr = 11509 [(wire_out) = true]; + //onekey MessageType_DeviceEraseSector= 10026 [deprecated = true]; diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index fbca270ff8..b77bbd0830 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -335,3 +335,13 @@ NexaSignedTx = 11403 NexaTxInputRequest = 11404 NexaTxInputAck = 11405 + NostrGetPublicKey = 11500 + NostrPublicKey = 11501 + NostrSignEvent = 11502 + NostrSignedEvent = 11503 + NostrEncryptMessage = 11504 + NostrEncryptedMessage = 11505 + NostrDecryptMessage = 11506 + NostrDecryptedMessage = 11507 + NostrSignSchnorr = 11508 + NostrSignedSchnorr = 11509 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 6c76f07c3f..b8de01359d 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -353,6 +353,16 @@ class MessageType(IntEnum): NexaSignedTx = 11403 NexaTxInputRequest = 11404 NexaTxInputAck = 11405 + NostrGetPublicKey = 11500 + NostrPublicKey = 11501 + NostrSignEvent = 11502 + NostrSignedEvent = 11503 + NostrEncryptMessage = 11504 + NostrEncryptedMessage = 11505 + NostrDecryptMessage = 11506 + NostrDecryptedMessage = 11507 + NostrSignSchnorr = 11508 + NostrSignedSchnorr = 11509 DeviceEraseSector = 10026 class FailureType(IntEnum): diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 4ac5da43de..63f9a7011e 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -6340,6 +6340,166 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["NexaSignedTx"]: return isinstance(msg, cls) + class NostrGetPublicKey(protobuf.MessageType): + address_n: "list[int]" + show_display: "bool | None" + + def __init__( + self, + *, + address_n: "list[int] | None" = None, + show_display: "bool | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrGetPublicKey"]: + return isinstance(msg, cls) + + class NostrPublicKey(protobuf.MessageType): + publickey: "str | None" + npub: "str | None" + + def __init__( + self, + *, + publickey: "str | None" = None, + npub: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrPublicKey"]: + return isinstance(msg, cls) + + class NostrSignEvent(protobuf.MessageType): + address_n: "list[int]" + event: "bytes" + + def __init__( + self, + *, + event: "bytes", + address_n: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrSignEvent"]: + return isinstance(msg, cls) + + class NostrSignedEvent(protobuf.MessageType): + event: "bytes" + + def __init__( + self, + *, + event: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrSignedEvent"]: + return isinstance(msg, cls) + + class NostrSignSchnorr(protobuf.MessageType): + address_n: "list[int]" + hash: "str" + + def __init__( + self, + *, + hash: "str", + address_n: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrSignSchnorr"]: + return isinstance(msg, cls) + + class NostrSignedSchnorr(protobuf.MessageType): + signature: "bytes" + + def __init__( + self, + *, + signature: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrSignedSchnorr"]: + return isinstance(msg, cls) + + class NostrEncryptMessage(protobuf.MessageType): + address_n: "list[int]" + pubkey: "str" + msg: "str" + show_display: "bool | None" + + def __init__( + self, + *, + pubkey: "str", + msg: "str", + address_n: "list[int] | None" = None, + show_display: "bool | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrEncryptMessage"]: + return isinstance(msg, cls) + + class NostrEncryptedMessage(protobuf.MessageType): + msg: "str" + + def __init__( + self, + *, + msg: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrEncryptedMessage"]: + return isinstance(msg, cls) + + class NostrDecryptMessage(protobuf.MessageType): + address_n: "list[int]" + pubkey: "str" + msg: "str" + show_display: "bool | None" + + def __init__( + self, + *, + pubkey: "str", + msg: "str", + address_n: "list[int] | None" = None, + show_display: "bool | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrDecryptMessage"]: + return isinstance(msg, cls) + + class NostrDecryptedMessage(protobuf.MessageType): + msg: "str" + + def __init__( + self, + *, + msg: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrDecryptedMessage"]: + return isinstance(msg, cls) + class PolkadotGetAddress(protobuf.MessageType): address_n: "list[int]" prefix: "int" diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index 93d23eb503..d00ad33c45 100755 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -135,6 +135,8 @@ OBJS += cbor.o OBJS += sui.o OBJS += kaspa.o OBJS += nexa.o +OBJS += nostr.o +OBJS += base64.o endif OBJS += debug.o @@ -234,6 +236,7 @@ OBJS += protob/messages-cardano.pb.o OBJS += protob/messages-sui.pb.o OBJS += protob/messages-kaspa.pb.o OBJS += protob/messages-nexa.pb.o +OBJS += protob/messages-nostr.pb.o endif OPTFLAGS ?= -Os diff --git a/legacy/firmware/ada.c b/legacy/firmware/ada.c index a64e277b6d..1d3a40004c 100644 --- a/legacy/firmware/ada.c +++ b/legacy/firmware/ada.c @@ -1110,19 +1110,23 @@ bool hash_stage() { } break; case TX_HASH_BUILDER_IN_CERTIFICATES: - if (ada_signer.remainingCertificates > 0) { - msg_write(MessageType_MessageType_CardanoTxItemAck, &ada_msg_item_ack); - } + msg_write(MessageType_MessageType_CardanoTxItemAck, &ada_msg_item_ack); break; case TX_HASH_BUILDER_IN_WITHDRAWALS: if (ada_signer.remainingWithdrawals > 0) { msg_write(MessageType_MessageType_CardanoTxItemAck, &ada_msg_item_ack); + } else { + ada_signer.state = TX_HASH_BUILDER_IN_AUX_DATA; + hash_stage(); } break; case TX_HASH_BUILDER_IN_AUX_DATA: if (ada_signer.signertx.has_auxiliary_data && ada_signer.tx_dict_items_count > 0) { msg_write(MessageType_MessageType_CardanoTxItemAck, &ada_msg_item_ack); + } else { + ada_signer.state = TX_HASH_BUILDER_IN_VALIDITY_INTERVAL_START; + hash_stage(); } break; case TX_HASH_BUILDER_IN_VALIDITY_INTERVAL_START: @@ -1209,6 +1213,12 @@ bool hash_stage() { break; } + if (!ada_signer.is_finished && 0 == ada_signer.tx_dict_items_count && + ada_signer.state != TX_HASH_BUILDER_FINISHED) { // finish + msg_write(MessageType_MessageType_CardanoTxItemAck, &ada_msg_item_ack); + ada_signer.state = TX_HASH_BUILDER_FINISHED; + } + return true; } diff --git a/legacy/firmware/base64.c b/legacy/firmware/base64.c new file mode 100644 index 0000000000..dae2b762e1 --- /dev/null +++ b/legacy/firmware/base64.c @@ -0,0 +1,139 @@ +#include "base64.h" + +/** Escape values. */ +enum special_e { + notabase64 = 64, /**< Value to return when a non base64 digit is found. */ + terminator = 65, /**< Value to return when the character '=' is found. */ +}; + +/** Lookup table that converts a base64 digit to integer. */ +static char const digittobin[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 64, 64, 64, 65, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, + 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64}; + +/* Convert a base64 null-terminated string to binary format.*/ +void* b64tobin(void* dest, char const* src) { + unsigned char const* s = (unsigned char*)src; + char* p = dest; + for (;;) { + int const a = digittobin[*s]; + if (a == notabase64) return p; + if (a == terminator) return p; + + int const b = digittobin[*++s]; + if (b == notabase64) return 0; + if (b == terminator) return 0; + + *p++ = (a << 2u) | (b >> 4u); + + int const c = digittobin[*++s]; + if (c == notabase64) return 0; + + int const d = digittobin[*++s]; + if (d == notabase64) return 0; + if (c == terminator) { + if (d != terminator) return 0; + return p; + } + + *p++ = (b << 4u) | (c >> 2u); + + if (d == terminator) return p; + + *p++ = (c << 6u) | (d >> 0u); + ++s; + } + + return p; +} + +/** Lookup table that converts a integer to base64 digit. */ +static char const bintodigit[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/"; + +/** Get the first base 64 digit of a block of 4. + * @param a The first byte of the source block of 3. + * @return A base 64 digit. */ +static int get0(int a) { + int const index = a >> 2u; + return bintodigit[index]; +} + +/** Get the second base 64 digit of a block of 4. + * @param a The first byte of the source block of 3. + * @param b The second byte of the source block of 3. + * @return A base 64 digit. */ +static int get1(int a, int b) { + int const indexA = (a & 3) << 4u; + int const indexB = b >> 4u; + int const index = indexA | indexB; + return bintodigit[index]; +} + +/** Get the third base 64 digit of a block of 4. + * @param b The second byte of the source block of 3. + * @param c The third byte of the source block of 3. + * @return A base 64 digit. */ +static unsigned int get2(unsigned int b, unsigned int c) { + int const indexB = (b & 15) << 2u; + int const indexC = c >> 6u; + int const index = indexB | indexC; + return bintodigit[index]; +} + +/** Get the fourth base 64 digit of a block of 4. + * @param c The third byte of the source block of 3. + * @return A base 64 digit. */ +static int get3(int c) { + int const index = c & 0x3f; + return bintodigit[index]; +} + +/* Convert a binary memory block in a base64 null-terminated string. */ +char* bintob64(char* dest, void const* src, size_t size) { + typedef struct { + unsigned char a; + unsigned char b; + unsigned char c; + } block_t; + block_t const* block = (block_t*)src; + for (; size >= sizeof(block_t); size -= sizeof(block_t), ++block) { + *dest++ = get0(block->a); + *dest++ = get1(block->a, block->b); + *dest++ = get2(block->b, block->c); + *dest++ = get3(block->c); + } + + if (!size) goto final; + + *dest++ = get0(block->a); + if (!--size) { + *dest++ = get1(block->a, 0); + *dest++ = '='; + *dest++ = '='; + goto final; + } + + *dest++ = get1(block->a, block->b); + *dest++ = get2(block->b, 0); + *dest++ = '='; + +final: + *dest = '\0'; + return dest; +} diff --git a/legacy/firmware/base64.h b/legacy/firmware/base64.h new file mode 100644 index 0000000000..5b4aa189a7 --- /dev/null +++ b/legacy/firmware/base64.h @@ -0,0 +1,27 @@ +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#include + +/** Convert a binary memory block in a base64 null-terminated string. + * @param dest Destination memory wher to put the base64 null-terminated string. + * @param src Source binary memory block. + * @param size Size in bytes of source binary memory block. + * @return A pointer to the null character of the base64 null-terminated string. + */ +char* bintob64(char* dest, void const* src, size_t size); + +/** Convert a base64 string to binary format. + * @param dest Destination memory block. + * @param src Source base64 string. + * @return If success a pointer to the next byte in memory block. + * Null if string has a bad format. */ +void* b64tobin(void* dest, char const* src); + +/** Convert a base64 string to binary format. + * @param p Source base64 string and destination memory block. + * @return If success a pointer to the next byte in memory block. + * Null if string has a bad format. */ +static inline void* b64decode(void* p) { return b64tobin(p, (char*)p); } + +#endif /* _BASE64_H_ */ diff --git a/legacy/firmware/fsm.c b/legacy/firmware/fsm.c index a6b9eee328..a306ea6ad1 100644 --- a/legacy/firmware/fsm.c +++ b/legacy/firmware/fsm.c @@ -80,6 +80,7 @@ #include "nem.h" #include "nem2.h" #include "nexa.h" +#include "nostr.h" #include "polkadot.h" #include "ripple.h" #include "solana.h" @@ -587,6 +588,7 @@ bool fsm_layoutPathWarning(uint32_t address_n_count, #include "fsm_msg_near.h" #include "fsm_msg_nem.h" #include "fsm_msg_nexa.h" +#include "fsm_msg_nostr.h" #include "fsm_msg_polkadot.h" #include "fsm_msg_ripple.h" #include "fsm_msg_solana.h" diff --git a/legacy/firmware/fsm.h b/legacy/firmware/fsm.h index aea12db3da..9a08cdc363 100644 --- a/legacy/firmware/fsm.h +++ b/legacy/firmware/fsm.h @@ -37,6 +37,7 @@ #include "messages-near.pb.h" #include "messages-nem.pb.h" #include "messages-nexa.pb.h" +#include "messages-nostr.pb.h" #include "messages-polkadot.pb.h" #include "messages-ripple.pb.h" #include "messages-solana.pb.h" @@ -301,4 +302,11 @@ void fsm_msgNexaGetAddress(const NexaGetAddress *msg); void fsm_msgNexaSignTx(const NexaSignTx *msg); void fsm_msgNexaTxInputAck(const NexaTxInputAck *msg); +// Nostr +void fsm_msgNostrGetPublicKey(const NostrGetPublicKey *msg); +void fsm_msgNostrSignEvent(const NostrSignEvent *msg); +void fsm_msgNostrEncryptMessage(NostrEncryptMessage *msg); +void fsm_msgNostrDecryptMessage(NostrDecryptMessage *msg); +void fsm_msgNostrSignSchnorr(const NostrSignSchnorr *msg); + #endif diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index fac4cfe2dd..8cc5f41149 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -43,7 +43,8 @@ bool get_features(Features *resp) { strlcpy(resp->fw_vendor, "UNSAFE, DO NOT USE!", sizeof(resp->fw_vendor)); } bool trezor_comp_mode = false; - if (config_getTrezorCompMode(&trezor_comp_mode)) { + config_getTrezorCompMode(&trezor_comp_mode); + if (trezor_comp_mode) { strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor)); } else { strlcpy(resp->vendor, "onekey.so", sizeof(resp->vendor)); diff --git a/legacy/firmware/fsm_msg_nostr.h b/legacy/firmware/fsm_msg_nostr.h new file mode 100644 index 0000000000..2c317edbd1 --- /dev/null +++ b/legacy/firmware/fsm_msg_nostr.h @@ -0,0 +1,166 @@ +/* + * 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 . + */ + +void fsm_msgNostrGetPublicKey(const NostrGetPublicKey *msg) { + CHECK_INITIALIZED + + CHECK_PIN + + RESP_INIT(NostrPublicKey); + + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + if (hdnode_fill_public_key(node) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive public key")); + layoutHome(); + return; + } + + resp->has_publickey = true; + resp->has_npub = true; + data2hexaddr(node->public_key + 1, 32, resp->publickey); + if (!nostr_get_pubkey(resp->npub, node->public_key + 1)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to get pubkey")); + layoutHome(); + return; + } + + if (msg->has_show_display && msg->show_display) { + char desc[32] = {0}; + strcat(desc, "Nostr"); + strcat(desc, " "); + strcat(desc, _("Address:")); + if (!fsm_layoutAddress(resp->publickey, NULL, desc, false, 0, + msg->address_n, msg->address_n_count, true, NULL, 0, + 0, NULL)) { + return; + } + } + + msg_write(MessageType_MessageType_NostrPublicKey, resp); + layoutHome(); +} + +void fsm_msgNostrSignEvent(const NostrSignEvent *msg) { + CHECK_INITIALIZED + + CHECK_PIN + + RESP_INIT(NostrSignedEvent); + + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + if (hdnode_fill_public_key(node) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive public key")); + layoutHome(); + return; + } + + if (!nostr_sign_event(msg, node, resp)) { + return; + } + msg_write(MessageType_MessageType_NostrSignedEvent, resp); + + layoutHome(); +} + +void fsm_msgNostrEncryptMessage(NostrEncryptMessage *msg) { + CHECK_INITIALIZED + + CHECK_PIN + + RESP_INIT(NostrEncryptedMessage); + + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + if (hdnode_fill_public_key(node) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive public key")); + layoutHome(); + return; + } + + if (!nostr_encrypt_message(msg, node, resp)) { + return; + } + msg_write(MessageType_MessageType_NostrEncryptedMessage, resp); + + layoutHome(); +} + +void fsm_msgNostrDecryptMessage(NostrDecryptMessage *msg) { + CHECK_INITIALIZED + + CHECK_PIN + + RESP_INIT(NostrDecryptedMessage); + + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + if (hdnode_fill_public_key(node) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive public key")); + layoutHome(); + return; + } + + if (!nostr_decrypt_message(msg, node, resp)) { + return; + } + msg_write(MessageType_MessageType_NostrDecryptedMessage, resp); + + layoutHome(); +} + +void fsm_msgNostrSignSchnorr(const NostrSignSchnorr *msg) { + CHECK_INITIALIZED + + CHECK_PIN + + RESP_INIT(NostrSignedSchnorr); + + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + if (hdnode_fill_public_key(node) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive public key")); + layoutHome(); + return; + } + + if (!nostr_sign_schnorr(msg, node, resp)) { + return; + } + msg_write(MessageType_MessageType_NostrSignedSchnorr, resp); + + layoutHome(); +} diff --git a/legacy/firmware/language.c b/legacy/firmware/language.c index 3c4a9df601..52e3f4e671 100644 --- a/legacy/firmware/language.c +++ b/legacy/firmware/language.c @@ -111,7 +111,7 @@ const char *languages[][2] = { // layout2.c {"BLUETOOTH NAME:", "蓝牙名称:"}, // - {"BLUETOOTH VERSION:", "蓝牙版本:"}, + {"BLUETOOTH:", "蓝牙版本:"}, // {"BOOTLOADER:", "BOOTLOADER版本:"}, // layout2.c @@ -267,6 +267,8 @@ const char *languages[][2] = { // ethereum.c {"Data length exceeds limit", ""}, // msg // layout2.c + {"Decrypt Message", "解密消息"}, + {"Decrypt Nostr Message", "解密 Nostr 消息"}, {"Decrypt for:", "解密"}, // layout2.c {"Decrypt value of this key?", "为该值解密"}, @@ -324,6 +326,7 @@ const char *languages[][2] = { {"Do you want to", "请确认"}, {"Do you want to change the\nhome screen?", "确定要更改主屏幕吗?"}, // layout2.c + {"Do you want to decrypt\nNostr message?", "确认要解密本次 Nostr 消息吗?"}, {"Do you want to decrypt?", "请确认解密"}, // fsm_msg_common.h {"Do you want to disable\nUSB Lock?", "要禁用 USB 锁吗?"}, @@ -332,6 +335,7 @@ const char *languages[][2] = { {"Do you want to enable\nUSB Lock?", "要启用 USB 锁吗?"}, {"Do you want to enable\npassphrase protection?", "要启用 passphrase 加密吗?"}, + {"Do you want to encrypt\nNostr message?", "确认要加密本次 Nostr 消息吗?"}, {"Do you want to restart", "确定要重启设备并进入更新"}, // menu_list.c {"Do you want to restore the\ninput direction to default?", @@ -359,6 +363,8 @@ const char *languages[][2] = { {"Enable Passphrase", "启用 Passphrase"}, {"Enable USB Lock", "启用 USB 锁"}, // layout2.c + {"Encrypt Message", "加密消息"}, + {"Encrypt Nostr Message", "加密 Nostr 消息"}, {"Encrypt message?", "加密消息"}, // layout2.c {"Encrypt value of this key?", "为这个值加密"}, @@ -401,7 +407,7 @@ const char *languages[][2] = { {"Error computing multisig fingerprint", ""}, // {"Exit", "退出"}, - {"FIRMWARE VERSION:", "固件版本:"}, + {"FIRMWARE:", "固件版本:"}, // signing.c signing.c signing.c signing.c // signing.c // signing.c @@ -523,7 +529,7 @@ const char *languages[][2] = { {"Next screen will show the\npassphrase!", "接下来, 将在屏幕中展示输\n入的 Passphrase"}, {"Next, Follow the onscreen\ninstructions to set up your\nOneKey Classic.", - "接下来,请按照屏幕上的指令\n完成开始设置您的 \nOneKey Classic"}, + "接下来,请按照屏幕上\n的指令开始设置您的 \nOneKey Classic"}, {"Next, check the written ", "接下来, 请再次检查刚刚抄写\n的 "}, {"Next, follow the guide and\ncheck words one by one.", "接下来, 请跟随引导, 逐一核\n对单词."}, diff --git a/legacy/firmware/layout2.c b/legacy/firmware/layout2.c index 1bac326418..05b95f215e 100644 --- a/legacy/firmware/layout2.c +++ b/legacy/firmware/layout2.c @@ -3866,12 +3866,12 @@ void layoutDeviceParameters(int num) { break; case 1: - oledDrawStringAdapter(0, y, _("FIRMWARE VERSION:"), FONT_STANDARD); + oledDrawStringAdapter(0, y, _("FIRMWARE:"), FONT_STANDARD); y += font->pixel + 1; oledDrawStringAdapter(0, y, ONEKEY_VERSION, FONT_STANDARD); y += font->pixel + 4; - oledDrawStringAdapter(0, y, _("BLUETOOTH VERSION:"), FONT_STANDARD); + oledDrawStringAdapter(0, y, _("BLUETOOTH:"), FONT_STANDARD); y += font->pixel + 1; oledDrawStringAdapter(0, y, ble_get_ver(), FONT_STANDARD); break; @@ -5165,7 +5165,7 @@ bool layoutSignMessage(const char *chain_name, bool verify, const char *signer, if (0 == index) { layoutHeader(title); - if (address_rowcount > 3) { + if (address_rowcount > 4) { str = split_message((const uint8_t *)signer, addrlen, rowlen); if (0 == sub_index) { oledDrawStringAdapter(0, 13, _("Signed by:"), FONT_STANDARD); @@ -5362,6 +5362,239 @@ bool layoutSignMessage(const char *chain_name, bool verify, const char *signer, return result; } +bool layoutNostrEncryptMessage(const char *chain_name, bool en, + const char *signer, const uint8_t *data, + uint16_t len, bool is_ascii) { + (void)chain_name; + bool result = false; + int index = 0, sub_index = 0, data_rowcount; + int i, bar_heght, bar_start = 12, bar_end = 52; + uint8_t max_index = 3; + uint8_t key = KEY_NULL; + char title[64] = {0}; + char title_tx[128] = {0}; + char lines[21] = {0}; + uint32_t rowlen = 21; + const char **str; + uint32_t addrlen = strlen(signer); + int address_rowcount = addrlen / rowlen + 1; + if (!is_ascii) { + data_rowcount = len % 10 ? len / 10 + 1 : len / 10; + } else { + data_rowcount = len % 20 ? len / 20 + 1 : len / 20; + } + +#if !EMULATOR + enableLongPress(true); +#endif + + if (en) { + snprintf(title, 64, "%s", _("Encrypt Nostr Message")); + snprintf(title_tx, 128, "%s", _("Do you want to encrypt\nNostr message?")); + } else { + snprintf(title, 64, "%s", _("Decrypt Nostr Message")); + snprintf(title_tx, 128, "%s", _("Do you want to decrypt\nNostr message?")); + } + + ButtonRequest resp = {0}; + memzero(&resp, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = ButtonRequestType_ButtonRequest_SignTx; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + +refresh_layout: + layoutSwipe(); + oledClear(); + + if (0 == index) { + layoutHeader(title); + if (address_rowcount > 4) { + str = split_message((const uint8_t *)signer, addrlen, rowlen); + if (0 == sub_index) { + oledDrawStringAdapter(0, 13, _("Signed by:"), FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 1 * 10, str[0], FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 2 * 10, str[1], FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 3 * 10, str[2], FONT_STANDARD); + oledDrawBitmap(3 * OLED_WIDTH / 4 - 8, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_down); + } else { + oledDrawStringAdapter(0, 13, str[sub_index - 1], FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 1 * 10, str[sub_index], FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 2 * 10, str[sub_index + 1], + FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 3 * 10, str[sub_index + 2], + FONT_STANDARD); + if (sub_index == address_rowcount - 3) { + oledDrawBitmap(OLED_WIDTH / 4, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_up); + } else { + oledDrawBitmap(OLED_WIDTH / 4, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_up); + oledDrawBitmap(3 * OLED_WIDTH / 4 - 8, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_down); + } + } + + // scrollbar + drawScrollbar(2, sub_index); + } else { + oledDrawStringAdapter(0, 13, _("Signer:"), FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 10, signer, FONT_STANDARD); + } + + layoutButtonNoAdapter(NULL, &bmp_bottom_left_close); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_arrow); + } else if (1 == index) { + layoutHeader(title); + if (data_rowcount > 4) { + if (is_ascii) { + memcpy(lines, data + 20 * (sub_index), 20); + oledDrawStringAdapter(0, 13, lines, FONT_STANDARD); + memcpy(lines, data + 20 * (sub_index + 1), 20); + oledDrawStringAdapter(0, 13 + 1 * 10, lines, FONT_STANDARD); + memcpy(lines, data + 20 * (sub_index + 2), 20); + oledDrawStringAdapter(0, 13 + 2 * 10, lines, FONT_STANDARD); + if (sub_index >= data_rowcount - 4) { + if (len % 20) { + memset(lines, 0, 21); + memcpy(lines, data + 20 * (sub_index + 3), len % 20); + } else { + memcpy(lines, data + 20 * (sub_index + 3), 20); + } + } else { + memcpy(lines, data + 20 * (sub_index + 3), 20); + } + oledDrawStringAdapter(0, 13 + 3 * 10, lines, FONT_STANDARD); + } else { + data2hexaddr(data + 10 * (sub_index), 10, lines); + oledDrawStringAdapter(0, 13, lines, FONT_STANDARD); + data2hexaddr(data + 10 * (sub_index + 1), 10, lines); + oledDrawStringAdapter(0, 13 + 1 * 10, lines, FONT_STANDARD); + data2hexaddr(data + 10 * (sub_index + 2), 10, lines); + oledDrawStringAdapter(0, 13 + 2 * 10, lines, FONT_STANDARD); + if (sub_index >= data_rowcount - 4) { + if (len % 10) { + data2hexaddr(data + 10 * (sub_index + 3), len % 10, lines); + } else { + data2hexaddr(data + 10 * (sub_index + 3), 10, lines); + } + } else { + data2hexaddr(data + 10 * (sub_index + 3), 10, lines); + } + oledDrawStringAdapter(0, 13 + 3 * 10, lines, FONT_STANDARD); + } + + // scrollbar + bar_heght = 40 - 2 * (data_rowcount - 5); + if (bar_heght < 6) bar_heght = 6; + for (i = bar_start; i < bar_end; i += 2) { // 40 pixel + oledDrawPixel(OLED_WIDTH - 1, i); + } + if (sub_index <= 18) { + for (i = bar_start + 2 * ((int)sub_index); + i < (bar_start + bar_heght + 2 * ((int)sub_index - 1)) - 1; i++) { + oledDrawPixel(OLED_WIDTH - 1, i); + oledDrawPixel(OLED_WIDTH - 2, i); + } + } else { + for (i = bar_start + 2 * 18; + i < (bar_start + bar_heght + 2 * (18 - 1)) - 1; i++) { + oledDrawPixel(OLED_WIDTH - 1, i); + oledDrawPixel(OLED_WIDTH - 2, i); + } + } + + if (sub_index == 0) { + oledDrawBitmap(3 * OLED_WIDTH / 4 - 8, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_down); + } else if (sub_index == data_rowcount - 4) { + oledDrawBitmap(OLED_WIDTH / 4, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_up); + } else { + oledDrawBitmap(OLED_WIDTH / 4, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_up); + oledDrawBitmap(3 * OLED_WIDTH / 4 - 8, OLED_HEIGHT - 8, + &bmp_bottom_middle_arrow_down); + } + layoutButtonNoAdapter(NULL, &bmp_bottom_left_arrow); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_next); + } else { + if (is_ascii) { + oledDrawStringAdapter(0, 13, (char *)data, FONT_STANDARD); + } else { + char buf[90] = {0}; + data2hexaddr(data, len, buf); + oledDrawStringAdapter(0, 13, buf, FONT_STANDARD); + } + layoutButtonNoAdapter(NULL, &bmp_bottom_left_arrow); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_arrow); + } + } else { + if (en) { + layoutHeader(_("Encrypt Message")); + } else { + layoutHeader(_("Decrypt Message")); + } + oledDrawStringAdapter(0, 13, title_tx, FONT_STANDARD); + layoutButtonNoAdapter(NULL, &bmp_bottom_left_close); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_confirm); + } + oledRefresh(); + + key = protectWaitKey(0, 0); +#if !EMULATOR + if (isLongPress(KEY_UP_OR_DOWN) && getLongPressStatus()) { + if (isLongPress(KEY_UP)) { + key = KEY_UP; + } else if (isLongPress(KEY_DOWN)) { + key = KEY_DOWN; + } + delay_ms(75); + } +#endif + switch (key) { + case KEY_UP: + if (sub_index > 0) { + sub_index--; + } + goto refresh_layout; + case KEY_DOWN: + if (index == 0 && sub_index < address_rowcount - 3) { + sub_index++; + } + if (index == 1 && sub_index < data_rowcount - 4) { + sub_index++; + } + goto refresh_layout; + case KEY_CONFIRM: + if (index == max_index - 1) { + result = true; + break; + } + if (index < max_index) { + index++; + } + sub_index = 0; + goto refresh_layout; + case KEY_CANCEL: + if (0 == index || index == max_index - 1) { + result = false; + break; + } + if (index > 0) { + index--; + } + goto refresh_layout; + default: + break; + } + +#if !EMULATOR + enableLongPress(false); +#endif + return result; +} + bool layoutSignHash(const char *chain_name, bool verify, const char *signer, const char *domain_hash, const char *message_hash, const char *warning) { @@ -5466,3 +5699,76 @@ bool layoutSignHash(const char *chain_name, bool verify, const char *signer, return result; } + +bool layoutSignSchnorrHash(const char *chain_name, const char *signer, + const char *hash) { + bool result = false; + int index = 0; + uint8_t max_index = 3; + uint8_t key = KEY_NULL; + char title[64] = {0}; + char title_tx[64] = {0}; + + snprintf(title, 64, "%s %s", chain_name, _("message")); + snprintf(title_tx, 64, "%s%s %s?", _("Do you want to sign this\n"), + chain_name, _("message")); + + ButtonRequest resp = {0}; + memzero(&resp, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = ButtonRequestType_ButtonRequest_SignTx; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + +refresh_layout: + layoutSwipe(); + oledClear(); + + if (0 == index) { + layoutHeader(title); + oledDrawStringAdapter(0, 13, _("Signer:"), FONT_STANDARD); + oledDrawStringAdapter(0, 13 + 10, signer, FONT_STANDARD); + layoutButtonNoAdapter(NULL, &bmp_bottom_left_close); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_arrow); + } else if (1 == index) { + layoutHeader(title); + oledDrawStringAdapter(0, 13, hash, FONT_STANDARD); + layoutButtonNoAdapter(NULL, &bmp_bottom_left_arrow); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_arrow); + } else { + layoutHeader(title); + oledDrawStringAdapter(0, 13, title_tx, FONT_STANDARD); + layoutButtonNoAdapter(NULL, &bmp_bottom_left_close); + layoutButtonYesAdapter(NULL, &bmp_bottom_right_arrow); + } + oledRefresh(); + + key = protectWaitKey(0, 0); + switch (key) { + case KEY_UP: + goto refresh_layout; + case KEY_DOWN: + goto refresh_layout; + case KEY_CONFIRM: + if (index == max_index - 1) { + result = true; + break; + } + if (index < max_index) { + index++; + } + goto refresh_layout; + case KEY_CANCEL: + if (0 == index || index == max_index - 1) { + result = false; + break; + } + if (index > 0) { + index--; + } + goto refresh_layout; + default: + break; + } + + return result; +} diff --git a/legacy/firmware/layout2.h b/legacy/firmware/layout2.h index 454c704168..49ee5db28a 100644 --- a/legacy/firmware/layout2.h +++ b/legacy/firmware/layout2.h @@ -295,9 +295,14 @@ bool layoutBlindSign(const char *chain_name, bool is_contract, const char *key3, const char *value3); bool layoutSignMessage(const char *chain_name, bool verify, const char *signer, const uint8_t *data, uint16_t len, bool is_ascii); +bool layoutNostrEncryptMessage(const char *chain_name, bool verify, + const char *signer, const uint8_t *data, + uint16_t len, bool is_ascii); bool layoutSignHash(const char *chain_name, bool verify, const char *signer, const char *domain_hash, const char *message_hash, const char *tips); +bool layoutSignSchnorrHash(const char *chain_name, const char *signer, + const char *hash); bool layoutPaginated(const char *title, const uint8_t *data, uint16_t len); void onboarding(uint8_t key); diff --git a/legacy/firmware/nostr.c b/legacy/firmware/nostr.c new file mode 100644 index 0000000000..27a5a9d0be --- /dev/null +++ b/legacy/firmware/nostr.c @@ -0,0 +1,306 @@ +#include "nostr.h" +#include +#include "aes/aes.h" +#include "algo/base64.h" +#include "base64.h" +#include "cosmos/json_parser.h" +#include "fsm.h" +#include "gettext.h" +#include "layout2.h" +#include "messages.h" +#include "messages.pb.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" + +extern int ethereum_is_canonic(uint8_t v, uint8_t signature[64]); +extern int convert_bits(uint8_t *out, size_t *outlen, int outbits, + const uint8_t *in, size_t inlen, int inbits, int pad); + +int nostr_get_pubkey(char *address, const uint8_t *public_key) { + uint8_t data[65] = {0}; + size_t datalen = 0; + + // bech32_encode + bech32_encoding enc = BECH32_ENCODING_BECH32; + convert_bits(data, &datalen, 5, public_key, 32, 8, 1); + + return bech32_encode(address, "npub", data, datalen, enc); +} + +bool nostr_sign_event(const NostrSignEvent *msg, const HDNode *node, + NostrSignedEvent *resp) { + parsed_json_t json; + parser_error_t err; + uint16_t req_root_item_key_token_idx = 0; + uint8_t unsignedEvent_bytes[2048] = {0}; + int unsignedEvent_index = 0; + char sig[129] = {0}; + char id[65] = {0}; + char pk[65] = {0}; + char npub[92] = {0}; + int tlen; + + nostr_get_pubkey(npub, node->public_key + 1); + if (!fsm_layoutSignMessage("Nostr", npub, msg->event.bytes, + msg->event.size)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + + data2hexaddr(node->public_key + 1, 32, pk); + + err = json_parse(&json, (const char *)msg->event.bytes, msg->event.size); + if (err != parser_ok) { + return err; + } + + // [0, pubkey, j["created_at"], j["kind"], j["tags"], j["content"]] + strcat((char *)unsignedEvent_bytes, "[0,"); + unsignedEvent_index += 3; + + // pubkey + unsignedEvent_bytes[unsignedEvent_index++] = '"'; + data2hexaddr(node->public_key + 1, 32, + (char *)(unsignedEvent_bytes + unsignedEvent_index)); + unsignedEvent_index += 64; + unsignedEvent_bytes[unsignedEvent_index++] = '"'; + unsignedEvent_bytes[unsignedEvent_index++] = ','; + + // created_at + err = object_get_value(&json, 0, "created_at", &req_root_item_key_token_idx); + if (err != parser_ok) { + fsm_sendFailure(FailureType_Failure_DataError, "created_at unexist"); + layoutHome(); + return false; + } + tlen = json.tokens[req_root_item_key_token_idx].end - + json.tokens[req_root_item_key_token_idx].start; + memcpy(unsignedEvent_bytes + unsignedEvent_index, + msg->event.bytes + json.tokens[req_root_item_key_token_idx].start, + tlen); + unsignedEvent_index += tlen; + unsignedEvent_bytes[unsignedEvent_index++] = ','; + + // kind + err = object_get_value(&json, 0, "kind", &req_root_item_key_token_idx); + if (err != parser_ok) { + fsm_sendFailure(FailureType_Failure_DataError, "kind unexist"); + layoutHome(); + return false; + } + tlen = json.tokens[req_root_item_key_token_idx].end - + json.tokens[req_root_item_key_token_idx].start; + memcpy(unsignedEvent_bytes + unsignedEvent_index, + msg->event.bytes + json.tokens[req_root_item_key_token_idx].start, + tlen); + unsignedEvent_index += tlen; + unsignedEvent_bytes[unsignedEvent_index++] = ','; + + // tags + err = object_get_value(&json, 0, "tags", &req_root_item_key_token_idx); + if (err != parser_ok) { + fsm_sendFailure(FailureType_Failure_DataError, "tags unexist"); + layoutHome(); + return false; + } + tlen = json.tokens[req_root_item_key_token_idx].end - + json.tokens[req_root_item_key_token_idx].start; + memcpy(unsignedEvent_bytes + unsignedEvent_index, + msg->event.bytes + json.tokens[req_root_item_key_token_idx].start, + tlen); + unsignedEvent_index += tlen; + unsignedEvent_bytes[unsignedEvent_index++] = ','; + + // content + err = object_get_value(&json, 0, "content", &req_root_item_key_token_idx); + if (err != parser_ok) { + fsm_sendFailure(FailureType_Failure_DataError, "content unexist"); + layoutHome(); + return false; + } + unsignedEvent_bytes[unsignedEvent_index++] = '"'; + tlen = json.tokens[req_root_item_key_token_idx].end - + json.tokens[req_root_item_key_token_idx].start; + memcpy(unsignedEvent_bytes + unsignedEvent_index, + msg->event.bytes + json.tokens[req_root_item_key_token_idx].start, + tlen); + unsignedEvent_index += tlen; + unsignedEvent_bytes[unsignedEvent_index++] = '"'; + unsignedEvent_bytes[unsignedEvent_index++] = ']'; + + uint8_t hash[32]; + uint8_t signature[64]; + pb_size_t sig_len = 0; + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, unsignedEvent_bytes, unsignedEvent_index); + sha256_Final(&ctx, hash); + + data2hexaddr(hash, 32, id); + + tx_sign_bip340_internal(node->private_key, hash, signature, &sig_len); + data2hexaddr(signature, 64, sig); + + resp->event.size = + snprintf((char *)resp->event.bytes, 512, + "{\"pubkey\":\"%s\",\"id\":\"%s\",\"sig\":\"%s\"}", pk, id, sig); + return true; +} + +bool nostr_encrypt_message(NostrEncryptMessage *msg, const HDNode *node, + NostrEncryptedMessage *resp) { + uint8_t shared_secret[33] = {0}; + uint8_t pk[33] = {0}; + char npub[92] = {0}; + unsigned int pk_len = 0; + pk[0] = 0x02; + + if (msg->has_show_display && msg->show_display) { + nostr_get_pubkey(npub, node->public_key + 1); + if (!layoutNostrEncryptMessage("Nostr", true, npub, + (const uint8_t *)msg->msg, strlen(msg->msg), + true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + } + + hex2data(msg->pubkey, pk + 1, &pk_len); + ecdh_multiply(&secp256k1, node->private_key, pk, shared_secret); + + uint8_t iv[16] = {0}; + char iv_b64[64] = {0}; + + random_buffer(iv, 16); + base64_encode((char *)iv, 16, iv_b64, sizeof(iv_b64)); + + int msg_len = strlen(msg->msg); + int pad_len = 16 - (msg_len % 16); + for (int i = 0; i < pad_len; i++) { + msg->msg[msg_len++] = pad_len; + } + + aes_encrypt_ctx ctx = {0}; + uint8_t encrypted[1563] = {0}; + int ret = aes_encrypt_key256(shared_secret + 1, &ctx); + if (ret != EXIT_SUCCESS) { + fsm_sendFailure(FailureType_Failure_DataError, "aes_encrypt_key256 failed"); + layoutHome(); + return false; + } + if (aes_cbc_encrypt((uint8_t *)msg->msg, encrypted, msg_len, iv, &ctx) != + EXIT_SUCCESS) { + fsm_sendFailure(FailureType_Failure_DataError, "aes_cbc_encrypt failed"); + return false; + } + + base64_encode((char *)encrypted, msg_len, (char *)resp->msg, + sizeof(resp->msg)); + ret = strlen(resp->msg); + memcpy(resp->msg + ret, "?iv=", 4); + memcpy(resp->msg + ret + 4, iv_b64, strlen(iv_b64)); + + return true; +} + +bool nostr_decrypt_message(NostrDecryptMessage *msg, const HDNode *node, + NostrDecryptedMessage *resp) { + uint8_t shared_secret[33] = {0}; + uint8_t pk[33] = {0}; + char npub[92] = {0}; + uint8_t ct[1563] = {0}; + uint8_t iv[16] = {0}; + unsigned int pk_len = 0; + int ct_len, ret, pad_val; + + if (msg->has_show_display && msg->show_display) { + nostr_get_pubkey(npub, node->public_key + 1); + if (!layoutNostrEncryptMessage("Nostr", false, npub, + (const uint8_t *)msg->msg, strlen(msg->msg), + true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + } + + pk[0] = 0x02; + hex2data(msg->pubkey, pk + 1, &pk_len); + ecdh_multiply(&secp256k1, node->private_key, pk, shared_secret); + + char *iv_b64 = strstr(msg->msg, "?iv="); + if (!iv_b64) { + fsm_sendFailure(FailureType_Failure_DataError, "iv null"); + layoutHome(); + return false; + } + iv_b64[0] = '\0'; + iv_b64 += 4; + + char *end = b64tobin(iv, iv_b64); + if (!end) { + fsm_sendFailure(FailureType_Failure_DataError, "iv error"); + layoutHome(); + return false; + } + end = b64tobin(ct, msg->msg); + if (!end) { + fsm_sendFailure(FailureType_Failure_DataError, "ct error"); + layoutHome(); + return false; + } + ct_len = end - (char *)ct; + + aes_decrypt_ctx ctx = {0}; + ret = aes_decrypt_key256(shared_secret + 1, &ctx); + if (ret != EXIT_SUCCESS) { + fsm_sendFailure(FailureType_Failure_DataError, "aes_encrypt_key256 failed"); + layoutHome(); + return false; + } + + if (aes_cbc_decrypt(ct, (unsigned char *)resp->msg, ct_len, iv, &ctx) != + EXIT_SUCCESS) { + fsm_sendFailure(FailureType_Failure_DataError, "aes_cbc_decrypt failed"); + return false; + } + ct_len = strlen(resp->msg); + pad_val = resp->msg[ct_len - 1]; + if (pad_val <= 0xf) { + resp->msg[ct_len - pad_val] = '\0'; + } + return true; +} + +bool nostr_sign_schnorr(const NostrSignSchnorr *msg, const HDNode *node, + NostrSignedSchnorr *resp) { + uint8_t hash[32] = {0}; + char npub[92] = {0}; + unsigned int len = 0; + pb_size_t sig_len = 0; + + hex2data(msg->hash, hash, &len); + nostr_get_pubkey(npub, node->public_key + 1); + + if (!layoutSignSchnorrHash("Nostr", npub, msg->hash)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + + tx_sign_bip340_internal(node->private_key, hash, resp->signature.bytes, + &sig_len); + resp->signature.size = sig_len; + + return true; +} diff --git a/legacy/firmware/nostr.h b/legacy/firmware/nostr.h new file mode 100644 index 0000000000..1607d552aa --- /dev/null +++ b/legacy/firmware/nostr.h @@ -0,0 +1,36 @@ +/* + * 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 __NOSTR_H__ +#define __NOSTR_H__ +#include +#include "bip32.h" +#include "messages-nostr.pb.h" + +int nostr_get_pubkey(char *address, const uint8_t *public_key); +bool nostr_sign_event(const NostrSignEvent *msg, const HDNode *node, + NostrSignedEvent *resp); +bool nostr_encrypt_message(NostrEncryptMessage *msg, const HDNode *node, + NostrEncryptedMessage *resp); +bool nostr_decrypt_message(NostrDecryptMessage *msg, const HDNode *node, + NostrDecryptedMessage *resp); +bool nostr_sign_schnorr(const NostrSignSchnorr *msg, const HDNode *node, + NostrSignedSchnorr *resp); + +#endif // __NOSTR_H__ diff --git a/legacy/firmware/polkadot.c b/legacy/firmware/polkadot.c index 1c0d7735c8..a501b5d10c 100644 --- a/legacy/firmware/polkadot.c +++ b/legacy/firmware/polkadot.c @@ -132,6 +132,8 @@ static bool get_signer_address(const PolkadotSignTx *msg, const HDNode *node, addressType = 5; } else if (!strncmp(msg->network, "westend", 7)) { addressType = 42; + } else if (!strncmp(msg->network, "joystream", 9)) { + addressType = 126; } else { return false; } diff --git a/legacy/firmware/polkadot/parser_impl_common.c b/legacy/firmware/polkadot/parser_impl_common.c index 9d553cdd48..3d6b921156 100644 --- a/legacy/firmware/polkadot/parser_impl_common.c +++ b/legacy/firmware/polkadot/parser_impl_common.c @@ -377,6 +377,10 @@ uint16_t _detectAddressType(const parser_context_t *c) { __polkadot_dicimal = COIN_AMOUNT_DECIMAL_PLACES_12; memcpy(__polkadot_ticker, WESTEND_COIN_TICKER, 4); return 42; + } else if (!strncmp(polkadot_network, "joystream", 9)) { + __polkadot_dicimal = COIN_AMOUNT_DECIMAL_PLACES; + memcpy(__polkadot_ticker, JOY_COIN_TICKER, 4); + return 126; } return 42; diff --git a/legacy/firmware/polkadot/substrate/substrate_coin.h b/legacy/firmware/polkadot/substrate/substrate_coin.h index 29fdd216a5..a232a9b7f7 100644 --- a/legacy/firmware/polkadot/substrate/substrate_coin.h +++ b/legacy/firmware/polkadot/substrate/substrate_coin.h @@ -14,5 +14,6 @@ #define KUSAMA_COIN_TICKER " KSM" #define WESTEND_COIN_TICKER " WND" #define ASTAR_COIN_TICKER " ASTR" +#define JOY_COIN_TICKER " JOY" #endif diff --git a/legacy/firmware/polkadot/substrate/substrate_dispatch.c b/legacy/firmware/polkadot/substrate/substrate_dispatch.c index 7c4f672b2c..510e27ad67 100644 --- a/legacy/firmware/polkadot/substrate/substrate_dispatch.c +++ b/legacy/firmware/polkadot/substrate/substrate_dispatch.c @@ -15,6 +15,8 @@ parser_error_t _readMethod(parser_context_t* c, uint8_t moduleIdx, return _readMethod_V18_westend(c, moduleIdx, callIdx, &method->V18); } else if (!strncmp(polkadot_network, "astar", 5)) { return _readMethod_astar(c, moduleIdx, callIdx, &method->V18); + } else if (!strncmp(polkadot_network, "joystream", 9)) { + return _readMethod_joystream(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 600e7ac4ae..0288e8aad0 100644 --- a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.c +++ b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.c @@ -32,6 +32,47 @@ __Z_INLINE parser_error_t _readMethod_balances_transfer_all_V18( return parser_ok; } +parser_error_t _readAccountIdLookupOfT_joy(parser_context_t* c, + pd_AccountIdLookupOfT_V18_t* v) { + CHECK_INPUT() + CHECK_ERROR(_readAccountId_V18(c, &v->id)) + return parser_ok; +} + +__Z_INLINE parser_error_t _readMethod_balances_transfer_joy( + parser_context_t* c, pd_balances_transfer_V18_t* m) { + m->dest.value = 0; + CHECK_ERROR(_readAccountIdLookupOfT_joy(c, &m->dest)) + CHECK_ERROR(_readCompactBalance(c, &m->amount)) + return parser_ok; +} + +__Z_INLINE parser_error_t _readMethod_balances_force_transfer_joy( + parser_context_t* c, pd_balances_force_transfer_V18_t* m) { + m->source.value = 0; + CHECK_ERROR(_readAccountIdLookupOfT_joy(c, &m->source)) + m->dest.value = 0; + CHECK_ERROR(_readAccountIdLookupOfT_joy(c, &m->dest)) + CHECK_ERROR(_readCompactBalance(c, &m->amount)) + return parser_ok; +} + +__Z_INLINE parser_error_t _readMethod_balances_transfer_keep_alive_joystream( + parser_context_t* c, pd_balances_transfer_keep_alive_V18_t* m) { + m->dest.value = 0; + CHECK_ERROR(_readAccountIdLookupOfT_joy(c, &m->dest)) + CHECK_ERROR(_readCompactBalance(c, &m->amount)) + return parser_ok; +} + +__Z_INLINE parser_error_t _readMethod_balances_transfer_all_joy( + parser_context_t* c, pd_balances_transfer_all_V18_t* m) { + m->dest.value = 0; + CHECK_ERROR(_readAccountIdLookupOfT_joy(c, &m->dest)) + CHECK_ERROR(_readbool(c, &m->keep_alive)) + return parser_ok; +} + parser_error_t _readMethod_V18(parser_context_t* c, uint8_t moduleIdx, uint8_t callIdx, pd_Method_V18_t* method) { uint16_t callPrivIdx = ((uint16_t)moduleIdx << 8u) + callIdx; @@ -143,6 +184,29 @@ parser_error_t _readMethod_astar(parser_context_t* c, uint8_t moduleIdx, return parser_ok; } +parser_error_t _readMethod_joystream(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 1280: /* module 5 call 0 */ + CHECK_ERROR(_readMethod_balances_transfer_joy( + c, &method->nested.balances_transfer_V18)) + break; + case 1283: /* module 5 call 3 */ + CHECK_ERROR(_readMethod_balances_transfer_keep_alive_joystream( + c, &method->nested.balances_transfer_keep_alive_V18)) + break; + case 1284: /* module 5 call 4 */ + CHECK_ERROR(_readMethod_balances_transfer_all_joy( + c, &method->basic.balances_transfer_all_V18)) + break; + default: + return parser_unexpected_callIndex; + } + + return parser_ok; +} + ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// diff --git a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h index e057f1557c..5b24049721 100644 --- a/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h +++ b/legacy/firmware/polkadot/substrate/substrate_dispatch_V18.h @@ -17,6 +17,8 @@ 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); const char* _getMethod_ModuleName_V18(uint8_t moduleIdx); diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 312c14a178..e54657d6bb 100755 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -21,7 +21,7 @@ PROTO_NAMES = messages messages-bitcoin messages-common messages-crypto messages messages-ethereum-onekey messages-ethereum messages-ethereum-definitions messages-management messages-nem messages-stellar \ messages-solana messages-starcoin messages-tron messages-aptos messages-near messages-conflux \ messages-algorand messages-ripple messages-filecoin messages-cosmos \ - messages-polkadot messages-cardano messages-sui messages-kaspa messages-nexa + messages-polkadot messages-cardano messages-sui messages-kaspa messages-nexa messages-nostr PROTO_OPTIONS = $(PROTO_NAMES:=.options) PROTO_COMPILED = $(PROTO_NAMES:=.pb) diff --git a/legacy/firmware/protob/messages-nostr.options b/legacy/firmware/protob/messages-nostr.options new file mode 100644 index 0000000000..368fd709f8 --- /dev/null +++ b/legacy/firmware/protob/messages-nostr.options @@ -0,0 +1,21 @@ +NostrGetPublicKey.address_n max_count:8 +NostrPublicKey.publickey max_size:65 +NostrPublicKey.npub max_size:92 + +NostrSignEvent.address_n max_count:8 +NostrSignEvent.event max_size:2048 +NostrSignedEvent.event max_size:512 + +NostrSignSchnorr.address_n max_count:8 +NostrSignSchnorr.hash max_size:65 +NostrSignedSchnorr.signature max_size:65 + +NostrEncryptMessage.address_n max_count:8 +NostrEncryptMessage.pubkey max_size:92 +NostrEncryptMessage.msg max_size:1563 +NostrEncryptedMessage.msg max_size:1563 + +NostrDecryptMessage.address_n max_count:8 +NostrDecryptMessage.pubkey max_size:92 +NostrDecryptMessage.msg max_size:1563 +NostrDecryptedMessage.msg max_size:1563 diff --git a/legacy/firmware/protob/messages-nostr.proto b/legacy/firmware/protob/messages-nostr.proto new file mode 120000 index 0000000000..9d45d7295d --- /dev/null +++ b/legacy/firmware/protob/messages-nostr.proto @@ -0,0 +1 @@ +../../vendor/trezor-common/protob/messages-nostr.proto \ No newline at end of file diff --git a/legacy/firmware/protob/messages-sui.options b/legacy/firmware/protob/messages-sui.options index 0632751829..4825bafe74 100644 --- a/legacy/firmware/protob/messages-sui.options +++ b/legacy/firmware/protob/messages-sui.options @@ -2,7 +2,7 @@ SuiGetAddress.address_n max_count:8 SuiAddress.address max_size:70 SuiSignTx.address_n max_count:8 -SuiSignTx.raw_tx max_size:4096 +SuiSignTx.raw_tx max_size:6144 SuiSignedTx.public_key max_size:32 SuiSignedTx.signature max_size:64 diff --git a/legacy/firmware/transaction.c b/legacy/firmware/transaction.c index 584c0f13eb..cb47c8871e 100644 --- a/legacy/firmware/transaction.c +++ b/legacy/firmware/transaction.c @@ -545,6 +545,13 @@ bool tx_sign_bip340(const uint8_t *private_key, const uint8_t *hash, return ret; } +bool tx_sign_bip340_internal(const uint8_t *private_key, const uint8_t *hash, + uint8_t *out, pb_size_t *size) { + bool ret = (zkp_bip340_sign_digest(private_key, hash, out, NULL) == 0); + *size = ret ? 64 : 0; + return ret; +} + // tx methods bool tx_input_check_hash(Hasher *hasher, const TxInputType *input) { hasher_Update(hasher, (const uint8_t *)&input->address_n_count, diff --git a/legacy/firmware/transaction.h b/legacy/firmware/transaction.h old mode 100755 new mode 100644 index 51bea75816..22cb7dfe5b --- a/legacy/firmware/transaction.h +++ b/legacy/firmware/transaction.h @@ -113,6 +113,8 @@ bool tx_sign_ecdsa(const ecdsa_curve *curve, const uint8_t *private_key, const uint8_t *hash, uint8_t *out, pb_size_t *size); bool tx_sign_bip340(const uint8_t *private_key, const uint8_t *hash, uint8_t *out, pb_size_t *size); +bool tx_sign_bip340_internal(const uint8_t *private_key, const uint8_t *hash, + uint8_t *out, pb_size_t *size); void op_return_to_script_pubkey(const uint8_t *op_return_data, size_t op_return_size, uint8_t *script_pubkey, pb_size_t *script_pubkey_size); diff --git a/legacy/firmware/version.h b/legacy/firmware/version.h index 230b164cb9..984846d639 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.4.0" -#define ONEKEY_VERSION_HEX 0x3400 +#define ONEKEY_VERSION "3.6.0" +#define ONEKEY_VERSION_HEX 0x3600 diff --git a/legacy/util.c b/legacy/util.c old mode 100755 new mode 100644 index ec54a70667..41f649e34d --- a/legacy/util.c +++ b/legacy/util.c @@ -18,6 +18,8 @@ */ #include "util.h" +#include +#include inline void delay(uint32_t wait) { while (--wait > 0) __asm__("nop"); @@ -67,3 +69,31 @@ void uint2str(uint32_t num, char *str) { str[i - 1 - j] = temp; } } + +int hex2data(const char *hexStr, unsigned char *output, + unsigned int *outputLen) { + size_t len = strlen(hexStr); + if (len % 2 != 0) { + return -1; + } + size_t finalLen = len / 2; + *outputLen = finalLen; + for (size_t inIdx = 0, outIdx = 0; outIdx < finalLen; inIdx += 2, outIdx++) { + if ((hexStr[inIdx] - 48) <= 9 && (hexStr[inIdx + 1] - 48) <= 9) { + goto convert; + } else { + if (((hexStr[inIdx] - 65) <= 5 && (hexStr[inIdx + 1] - 65) <= 5) || + ((hexStr[inIdx] - 97) <= 5 && (hexStr[inIdx + 1] - 97) <= 5)) { + goto convert; + } else { + *outputLen = 0; + return -1; + } + } + convert: + output[outIdx] = + (hexStr[inIdx] % 32 + 9) % 25 * 16 + (hexStr[inIdx + 1] % 32 + 9) % 25; + } + output[finalLen] = '\0'; + return 0; +} diff --git a/legacy/util.h b/legacy/util.h old mode 100755 new mode 100644 index d96da5aa0d..c53c09f742 --- a/legacy/util.h +++ b/legacy/util.h @@ -61,6 +61,9 @@ void data2hexaddr(const uint8_t *data, uint32_t len, char *str); void uint2str(uint32_t num, char *str); +int hex2data(const char *hexStr, unsigned char *output, + unsigned int *outputLen); + // defined in startup.s (or setup.c for emulator) extern void __attribute__((noreturn)) shutdown(void); diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index f332768444..83e2079f65 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -361,6 +361,16 @@ class MessageType(IntEnum): NexaSignedTx = 11403 NexaTxInputRequest = 11404 NexaTxInputAck = 11405 + NostrGetPublicKey = 11500 + NostrPublicKey = 11501 + NostrSignEvent = 11502 + NostrSignedEvent = 11503 + NostrEncryptMessage = 11504 + NostrEncryptedMessage = 11505 + NostrDecryptMessage = 11506 + NostrDecryptedMessage = 11507 + NostrSignSchnorr = 11508 + NostrSignedSchnorr = 11509 DeviceEraseSector = 10026 @@ -8004,6 +8014,176 @@ def __init__( self.signature = signature +class NostrGetPublicKey(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11500 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("show_display", "bool", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + address_n: Optional[Sequence["int"]] = None, + show_display: Optional["bool"] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.show_display = show_display + + +class NostrPublicKey(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11501 + FIELDS = { + 1: protobuf.Field("publickey", "string", repeated=False, required=False, default=None), + 2: protobuf.Field("npub", "string", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + publickey: Optional["str"] = None, + npub: Optional["str"] = None, + ) -> None: + self.publickey = publickey + self.npub = npub + + +class NostrSignEvent(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11502 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("event", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + event: "bytes", + address_n: Optional[Sequence["int"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.event = event + + +class NostrSignedEvent(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11503 + FIELDS = { + 1: protobuf.Field("event", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + event: "bytes", + ) -> None: + self.event = event + + +class NostrSignSchnorr(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11508 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("hash", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + hash: "str", + address_n: Optional[Sequence["int"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.hash = hash + + +class NostrSignedSchnorr(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11509 + FIELDS = { + 1: protobuf.Field("signature", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + signature: "bytes", + ) -> None: + self.signature = signature + + +class NostrEncryptMessage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11504 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("pubkey", "string", repeated=False, required=True), + 3: protobuf.Field("msg", "string", repeated=False, required=True), + 4: protobuf.Field("show_display", "bool", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + pubkey: "str", + msg: "str", + address_n: Optional[Sequence["int"]] = None, + show_display: Optional["bool"] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.pubkey = pubkey + self.msg = msg + self.show_display = show_display + + +class NostrEncryptedMessage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11505 + FIELDS = { + 1: protobuf.Field("msg", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + msg: "str", + ) -> None: + self.msg = msg + + +class NostrDecryptMessage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11506 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("pubkey", "string", repeated=False, required=True), + 3: protobuf.Field("msg", "string", repeated=False, required=True), + 4: protobuf.Field("show_display", "bool", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + pubkey: "str", + msg: "str", + address_n: Optional[Sequence["int"]] = None, + show_display: Optional["bool"] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.pubkey = pubkey + self.msg = msg + self.show_display = show_display + + +class NostrDecryptedMessage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 11507 + FIELDS = { + 1: protobuf.Field("msg", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + msg: "str", + ) -> None: + self.msg = msg + + class PolkadotGetAddress(protobuf.MessageType): MESSAGE_WIRE_TYPE = 11000 FIELDS = {