mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-02 16:26:19 +00:00
feat(core): introduce Nostr
[no changelog]
This commit is contained in:
parent
1954c7cbc2
commit
073038771e
64
common/protob/messages-nostr.proto
Normal file
64
common/protob/messages-nostr.proto
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
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: Ask the device for the Nostr public key
|
||||||
|
* @start
|
||||||
|
* @next NostrPubkey
|
||||||
|
*/
|
||||||
|
message NostrGetPubkey {
|
||||||
|
repeated uint32 address_n = 1; // used to derive the key
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Nostr pubkey
|
||||||
|
* @end
|
||||||
|
*/
|
||||||
|
message NostrPubkey {
|
||||||
|
required bytes pubkey = 1; // pubkey derived from the seed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @embed
|
||||||
|
*/
|
||||||
|
message NostrTag {
|
||||||
|
// Nostr tags consist of at least one string (the key)
|
||||||
|
// followed by an arbitrary number of strings,
|
||||||
|
// the first of which (if present) is called "value".
|
||||||
|
// See NIP-01: https://github.com/nostr-protocol/nips/blob/master/01.md#tags
|
||||||
|
required string key = 1;
|
||||||
|
optional string value = 2;
|
||||||
|
repeated string extra = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to sign an event
|
||||||
|
* @start
|
||||||
|
* @next NostrEventSignature
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message NostrSignEvent {
|
||||||
|
repeated uint32 address_n = 1; // used to derive the key
|
||||||
|
|
||||||
|
// Nostr event fields, except the ones that are calculated by the signer
|
||||||
|
// See NIP-01: https://github.com/nostr-protocol/nips/blob/master/01.md
|
||||||
|
|
||||||
|
required uint32 created_at = 2; // Event created_at: unix timestamp in seconds
|
||||||
|
required uint32 kind = 3; // Event kind: integer between 0 and 65535
|
||||||
|
repeated NostrTag tags = 4; // Event tags
|
||||||
|
required string content = 5; // Event content: arbitrary string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Computed event ID and signature
|
||||||
|
* @end
|
||||||
|
*/
|
||||||
|
message NostrEventSignature {
|
||||||
|
required bytes pubkey = 1; // pubkey used to sign the event
|
||||||
|
required bytes id = 2; // ID of the event
|
||||||
|
required bytes signature = 3; // signature of the event
|
||||||
|
}
|
@ -324,6 +324,12 @@ enum MessageType {
|
|||||||
// THP
|
// THP
|
||||||
reserved 1000 to 1099; // See messages-thp.proto
|
reserved 1000 to 1099; // See messages-thp.proto
|
||||||
|
|
||||||
|
// Nostr
|
||||||
|
MessageType_NostrGetPubkey = 2001 [(wire_in) = true];
|
||||||
|
MessageType_NostrPubkey = 2002 [(wire_out) = true];
|
||||||
|
MessageType_NostrSignEvent = 2003 [(wire_in) = true];
|
||||||
|
MessageType_NostrEventSignature = 2004 [(wire_out) = true];
|
||||||
|
|
||||||
// Benchmark
|
// Benchmark
|
||||||
MessageType_BenchmarkListNames = 9100 [(bitcoin_only) = true];
|
MessageType_BenchmarkListNames = 9100 [(bitcoin_only) = true];
|
||||||
MessageType_BenchmarkNames = 9101 [(bitcoin_only) = true];
|
MessageType_BenchmarkNames = 9101 [(bitcoin_only) = true];
|
||||||
|
1
core/.changelog.d/4160.added
Normal file
1
core/.changelog.d/4160.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add Nostr support (in debug mode only!).
|
@ -662,6 +662,10 @@ if FROZEN:
|
|||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nem/*/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nem/*/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/NEM*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/NEM*.py'))
|
||||||
|
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*/*.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Nostr*.py'))
|
||||||
|
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ripple/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ripple/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Ripple*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Ripple*.py'))
|
||||||
|
|
||||||
|
@ -721,6 +721,10 @@ if FROZEN:
|
|||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nem/*/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nem/*/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/NEM*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/NEM*.py'))
|
||||||
|
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*/*.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Nostr*.py'))
|
||||||
|
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ripple/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ripple/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Ripple*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Ripple*.py'))
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ ALTCOIN_PREFIXES = (
|
|||||||
"fido",
|
"fido",
|
||||||
"monero",
|
"monero",
|
||||||
"nem",
|
"nem",
|
||||||
|
"nostr",
|
||||||
"ripple",
|
"ripple",
|
||||||
"solana",
|
"solana",
|
||||||
"stellar",
|
"stellar",
|
||||||
|
6
core/src/all_modules.py
generated
6
core/src/all_modules.py
generated
@ -415,6 +415,12 @@ apps.misc.get_firmware_hash
|
|||||||
import apps.misc.get_firmware_hash
|
import apps.misc.get_firmware_hash
|
||||||
apps.misc.sign_identity
|
apps.misc.sign_identity
|
||||||
import apps.misc.sign_identity
|
import apps.misc.sign_identity
|
||||||
|
apps.nostr
|
||||||
|
import apps.nostr
|
||||||
|
apps.nostr.get_pubkey
|
||||||
|
import apps.nostr.get_pubkey
|
||||||
|
apps.nostr.sign_event
|
||||||
|
import apps.nostr.sign_event
|
||||||
apps.workflow_handlers
|
apps.workflow_handlers
|
||||||
import apps.workflow_handlers
|
import apps.workflow_handlers
|
||||||
|
|
||||||
|
9
core/src/apps/nostr/__init__.py
Normal file
9
core/src/apps/nostr/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from apps.common.paths import PATTERN_BIP44
|
||||||
|
|
||||||
|
CURVE = "secp256k1"
|
||||||
|
SLIP44_ID = 1237
|
||||||
|
|
||||||
|
# Note: we are not satisfied with using this path even though it is defined in NIP-06.
|
||||||
|
# See this issue for details: https://github.com/nostr-protocol/nips/issues/1774
|
||||||
|
# TODO: we need to create a new NIP using a different derivation path and use that here!
|
||||||
|
PATTERN = PATTERN_BIP44
|
24
core/src/apps/nostr/get_pubkey.py
Normal file
24
core/src/apps/nostr/get_pubkey.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from apps.common.keychain import auto_keychain
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from trezor.messages import NostrGetPubkey, NostrPubkey
|
||||||
|
|
||||||
|
from apps.common.keychain import Keychain
|
||||||
|
|
||||||
|
|
||||||
|
@auto_keychain(__name__)
|
||||||
|
async def get_pubkey(msg: NostrGetPubkey, keychain: Keychain) -> NostrPubkey:
|
||||||
|
from trezor.messages import NostrPubkey
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
|
|
||||||
|
address_n = msg.address_n
|
||||||
|
|
||||||
|
await paths.validate_path(keychain, address_n)
|
||||||
|
|
||||||
|
node = keychain.derive(address_n)
|
||||||
|
pk = node.public_key()[-32:]
|
||||||
|
|
||||||
|
return NostrPubkey(pubkey=pk)
|
48
core/src/apps/nostr/sign_event.py
Normal file
48
core/src/apps/nostr/sign_event.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from apps.common.keychain import auto_keychain
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from trezor.messages import NostrEventSignature, NostrSignEvent
|
||||||
|
|
||||||
|
from apps.common.keychain import Keychain
|
||||||
|
|
||||||
|
|
||||||
|
@auto_keychain(__name__)
|
||||||
|
async def sign_event(msg: NostrSignEvent, keychain: Keychain) -> NostrEventSignature:
|
||||||
|
from ubinascii import hexlify
|
||||||
|
|
||||||
|
from trezor.crypto.curve import secp256k1
|
||||||
|
from trezor.crypto.hashlib import sha256
|
||||||
|
from trezor.messages import NostrEventSignature
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
|
|
||||||
|
address_n = msg.address_n
|
||||||
|
created_at = msg.created_at
|
||||||
|
kind = msg.kind
|
||||||
|
tags = [[t.key] + ([t.value] if t.value else []) + t.extra for t in msg.tags]
|
||||||
|
content = msg.content
|
||||||
|
|
||||||
|
await paths.validate_path(keychain, address_n)
|
||||||
|
|
||||||
|
node = keychain.derive(address_n)
|
||||||
|
pk = node.public_key()[-32:]
|
||||||
|
|
||||||
|
# The event ID is obtained by serializing the event in a specific way:
|
||||||
|
# "[0,pubkey,created_at,kind,tags,content]"
|
||||||
|
# See NIP-01: https://github.com/nostr-protocol/nips/blob/master/01.md
|
||||||
|
serialized_tags = ",".join(
|
||||||
|
["[" + ",".join(f'"{t}"' for t in tag) + "]" for tag in tags]
|
||||||
|
)
|
||||||
|
serialized_event = f'[0,"{hexlify(pk).decode()}",{created_at},{kind},[{serialized_tags}],"{content}"]'
|
||||||
|
event_id = sha256(serialized_event).digest()
|
||||||
|
|
||||||
|
# The event signature is basically the signature of the event ID computed above
|
||||||
|
signature = secp256k1.sign(sk, event_id)[-64:]
|
||||||
|
|
||||||
|
return NostrEventSignature(
|
||||||
|
pubkey=pk,
|
||||||
|
id=event_id,
|
||||||
|
signature=signature,
|
||||||
|
)
|
@ -106,6 +106,12 @@ def _find_message_handler_module(msg_type: int) -> str:
|
|||||||
if msg_type == MessageType.GetFirmwareHash:
|
if msg_type == MessageType.GetFirmwareHash:
|
||||||
return "apps.misc.get_firmware_hash"
|
return "apps.misc.get_firmware_hash"
|
||||||
|
|
||||||
|
# nostr
|
||||||
|
if msg_type == MessageType.NostrGetPubkey:
|
||||||
|
return "apps.nostr.get_pubkey"
|
||||||
|
if msg_type == MessageType.NostrSignEvent:
|
||||||
|
return "apps.nostr.sign_event"
|
||||||
|
|
||||||
if not utils.BITCOIN_ONLY:
|
if not utils.BITCOIN_ONLY:
|
||||||
if msg_type == MessageType.SetU2FCounter:
|
if msg_type == MessageType.SetU2FCounter:
|
||||||
return "apps.management.set_u2f_counter"
|
return "apps.management.set_u2f_counter"
|
||||||
|
4
core/src/trezor/enums/MessageType.py
generated
4
core/src/trezor/enums/MessageType.py
generated
@ -250,3 +250,7 @@ if not utils.BITCOIN_ONLY:
|
|||||||
SolanaAddress = 903
|
SolanaAddress = 903
|
||||||
SolanaSignTx = 904
|
SolanaSignTx = 904
|
||||||
SolanaTxSignature = 905
|
SolanaTxSignature = 905
|
||||||
|
NostrGetPubkey = 2001
|
||||||
|
NostrPubkey = 2002
|
||||||
|
NostrSignEvent = 2003
|
||||||
|
NostrEventSignature = 2004
|
||||||
|
4
core/src/trezor/enums/__init__.py
generated
4
core/src/trezor/enums/__init__.py
generated
@ -591,6 +591,10 @@ if TYPE_CHECKING:
|
|||||||
SolanaAddress = 903
|
SolanaAddress = 903
|
||||||
SolanaSignTx = 904
|
SolanaSignTx = 904
|
||||||
SolanaTxSignature = 905
|
SolanaTxSignature = 905
|
||||||
|
NostrGetPubkey = 2001
|
||||||
|
NostrPubkey = 2002
|
||||||
|
NostrSignEvent = 2003
|
||||||
|
NostrEventSignature = 2004
|
||||||
BenchmarkListNames = 9100
|
BenchmarkListNames = 9100
|
||||||
BenchmarkNames = 9101
|
BenchmarkNames = 9101
|
||||||
BenchmarkRun = 9102
|
BenchmarkRun = 9102
|
||||||
|
86
core/src/trezor/messages.py
generated
86
core/src/trezor/messages.py
generated
@ -5216,6 +5216,92 @@ if TYPE_CHECKING:
|
|||||||
def is_type_of(cls, msg: Any) -> TypeGuard["NEMCosignatoryModification"]:
|
def is_type_of(cls, msg: Any) -> TypeGuard["NEMCosignatoryModification"]:
|
||||||
return isinstance(msg, cls)
|
return isinstance(msg, cls)
|
||||||
|
|
||||||
|
class NostrGetPubkey(protobuf.MessageType):
|
||||||
|
address_n: "list[int]"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
address_n: "list[int] | None" = None,
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_of(cls, msg: Any) -> TypeGuard["NostrGetPubkey"]:
|
||||||
|
return isinstance(msg, cls)
|
||||||
|
|
||||||
|
class NostrPubkey(protobuf.MessageType):
|
||||||
|
pubkey: "bytes"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
pubkey: "bytes",
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_of(cls, msg: Any) -> TypeGuard["NostrPubkey"]:
|
||||||
|
return isinstance(msg, cls)
|
||||||
|
|
||||||
|
class NostrTag(protobuf.MessageType):
|
||||||
|
key: "str"
|
||||||
|
value: "str | None"
|
||||||
|
extra: "list[str]"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
key: "str",
|
||||||
|
extra: "list[str] | None" = None,
|
||||||
|
value: "str | None" = None,
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_of(cls, msg: Any) -> TypeGuard["NostrTag"]:
|
||||||
|
return isinstance(msg, cls)
|
||||||
|
|
||||||
|
class NostrSignEvent(protobuf.MessageType):
|
||||||
|
address_n: "list[int]"
|
||||||
|
created_at: "int"
|
||||||
|
kind: "int"
|
||||||
|
tags: "list[NostrTag]"
|
||||||
|
content: "str"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
created_at: "int",
|
||||||
|
kind: "int",
|
||||||
|
content: "str",
|
||||||
|
address_n: "list[int] | None" = None,
|
||||||
|
tags: "list[NostrTag] | None" = None,
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_of(cls, msg: Any) -> TypeGuard["NostrSignEvent"]:
|
||||||
|
return isinstance(msg, cls)
|
||||||
|
|
||||||
|
class NostrEventSignature(protobuf.MessageType):
|
||||||
|
pubkey: "bytes"
|
||||||
|
id: "bytes"
|
||||||
|
signature: "bytes"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
pubkey: "bytes",
|
||||||
|
id: "bytes",
|
||||||
|
signature: "bytes",
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_of(cls, msg: Any) -> TypeGuard["NostrEventSignature"]:
|
||||||
|
return isinstance(msg, cls)
|
||||||
|
|
||||||
class RippleGetAddress(protobuf.MessageType):
|
class RippleGetAddress(protobuf.MessageType):
|
||||||
address_n: "list[int]"
|
address_n: "list[int]"
|
||||||
show_display: "bool | None"
|
show_display: "bool | None"
|
||||||
|
@ -12,7 +12,8 @@ SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdPro
|
|||||||
Solana StellarClaimClaimableBalanceOp \
|
Solana StellarClaimClaimableBalanceOp \
|
||||||
ChangeLanguage TranslationDataRequest TranslationDataAck \
|
ChangeLanguage TranslationDataRequest TranslationDataAck \
|
||||||
SetBrightness DebugLinkOptigaSetSecMax EntropyCheckReady EntropyCheckContinue \
|
SetBrightness DebugLinkOptigaSetSecMax EntropyCheckReady EntropyCheckContinue \
|
||||||
BenchmarkListNames BenchmarkRun BenchmarkNames BenchmarkResult
|
BenchmarkListNames BenchmarkRun BenchmarkNames BenchmarkResult \
|
||||||
|
NostrGetPubkey NostrPubkey NostrSignEvent NostrEventSignature
|
||||||
|
|
||||||
ifeq ($(BITCOIN_ONLY), 1)
|
ifeq ($(BITCOIN_ONLY), 1)
|
||||||
SKIPPED_MESSAGES += Ethereum NEM Stellar
|
SKIPPED_MESSAGES += Ethereum NEM Stellar
|
||||||
|
93
python/src/trezorlib/cli/nostr.py
Normal file
93
python/src/trezorlib/cli/nostr.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# This file is part of the Trezor project.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2025 SatoshiLabs and contributors
|
||||||
|
#
|
||||||
|
# This library is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License version 3
|
||||||
|
# as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# 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 License along with this library.
|
||||||
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from .. import messages, nostr, tools
|
||||||
|
from . import with_client
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from ..client import TrezorClient
|
||||||
|
|
||||||
|
|
||||||
|
PATH_TEMPLATE = "m/44h/1237h/{}h/0/0"
|
||||||
|
|
||||||
|
|
||||||
|
@click.group(name="nostr")
|
||||||
|
def cli() -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.option("-a", "--account", default=0, help="Account index")
|
||||||
|
@with_client
|
||||||
|
def get_pubkey(
|
||||||
|
client: "TrezorClient",
|
||||||
|
account: int,
|
||||||
|
) -> str:
|
||||||
|
"""Return the pubkey derived by the given path."""
|
||||||
|
|
||||||
|
address_n = tools.parse_path(PATH_TEMPLATE.format(account))
|
||||||
|
|
||||||
|
return nostr.get_pubkey(
|
||||||
|
client,
|
||||||
|
address_n,
|
||||||
|
).hex()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.option("-a", "--account", default=0, help="Account index")
|
||||||
|
@click.argument("event")
|
||||||
|
@with_client
|
||||||
|
def sign_event(
|
||||||
|
client: "TrezorClient",
|
||||||
|
account: int,
|
||||||
|
event: str,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
"""Sign an event using the key derived by the given path."""
|
||||||
|
|
||||||
|
event_json = json.loads(event)
|
||||||
|
|
||||||
|
address_n = tools.parse_path(PATH_TEMPLATE.format(account))
|
||||||
|
|
||||||
|
res = nostr.sign_event(
|
||||||
|
client,
|
||||||
|
messages.NostrSignEvent(
|
||||||
|
address_n=address_n,
|
||||||
|
created_at=event_json["created_at"],
|
||||||
|
kind=event_json["kind"],
|
||||||
|
tags=[
|
||||||
|
messages.NostrTag(
|
||||||
|
key=t[0], value=t[1] if len(t) > 1 else None, extra=t[2:]
|
||||||
|
)
|
||||||
|
for t in event_json["tags"]
|
||||||
|
],
|
||||||
|
content=event_json["content"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
event_json["id"] = res.id.hex()
|
||||||
|
event_json["pubkey"] = res.pubkey.hex()
|
||||||
|
event_json["sig"] = res.signature.hex()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"signed_event": event_json,
|
||||||
|
}
|
@ -44,6 +44,7 @@ from . import (
|
|||||||
firmware,
|
firmware,
|
||||||
monero,
|
monero,
|
||||||
nem,
|
nem,
|
||||||
|
nostr,
|
||||||
ripple,
|
ripple,
|
||||||
settings,
|
settings,
|
||||||
solana,
|
solana,
|
||||||
@ -409,6 +410,7 @@ cli.add_command(ethereum.cli)
|
|||||||
cli.add_command(fido.cli)
|
cli.add_command(fido.cli)
|
||||||
cli.add_command(monero.cli)
|
cli.add_command(monero.cli)
|
||||||
cli.add_command(nem.cli)
|
cli.add_command(nem.cli)
|
||||||
|
cli.add_command(nostr.cli)
|
||||||
cli.add_command(ripple.cli)
|
cli.add_command(ripple.cli)
|
||||||
cli.add_command(settings.cli)
|
cli.add_command(settings.cli)
|
||||||
cli.add_command(solana.cli)
|
cli.add_command(solana.cli)
|
||||||
|
98
python/src/trezorlib/messages.py
generated
98
python/src/trezorlib/messages.py
generated
@ -644,6 +644,10 @@ class MessageType(IntEnum):
|
|||||||
SolanaAddress = 903
|
SolanaAddress = 903
|
||||||
SolanaSignTx = 904
|
SolanaSignTx = 904
|
||||||
SolanaTxSignature = 905
|
SolanaTxSignature = 905
|
||||||
|
NostrGetPubkey = 2001
|
||||||
|
NostrPubkey = 2002
|
||||||
|
NostrSignEvent = 2003
|
||||||
|
NostrEventSignature = 2004
|
||||||
BenchmarkListNames = 9100
|
BenchmarkListNames = 9100
|
||||||
BenchmarkNames = 9101
|
BenchmarkNames = 9101
|
||||||
BenchmarkRun = 9102
|
BenchmarkRun = 9102
|
||||||
@ -6775,6 +6779,100 @@ class NEMCosignatoryModification(protobuf.MessageType):
|
|||||||
self.public_key = public_key
|
self.public_key = public_key
|
||||||
|
|
||||||
|
|
||||||
|
class NostrGetPubkey(protobuf.MessageType):
|
||||||
|
MESSAGE_WIRE_TYPE = 2001
|
||||||
|
FIELDS = {
|
||||||
|
1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
address_n: Optional[Sequence["int"]] = None,
|
||||||
|
) -> None:
|
||||||
|
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||||
|
|
||||||
|
|
||||||
|
class NostrPubkey(protobuf.MessageType):
|
||||||
|
MESSAGE_WIRE_TYPE = 2002
|
||||||
|
FIELDS = {
|
||||||
|
1: protobuf.Field("pubkey", "bytes", repeated=False, required=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
pubkey: "bytes",
|
||||||
|
) -> None:
|
||||||
|
self.pubkey = pubkey
|
||||||
|
|
||||||
|
|
||||||
|
class NostrTag(protobuf.MessageType):
|
||||||
|
MESSAGE_WIRE_TYPE = None
|
||||||
|
FIELDS = {
|
||||||
|
1: protobuf.Field("key", "string", repeated=False, required=True),
|
||||||
|
2: protobuf.Field("value", "string", repeated=False, required=False, default=None),
|
||||||
|
3: protobuf.Field("extra", "string", repeated=True, required=False, default=None),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
key: "str",
|
||||||
|
extra: Optional[Sequence["str"]] = None,
|
||||||
|
value: Optional["str"] = None,
|
||||||
|
) -> None:
|
||||||
|
self.extra: Sequence["str"] = extra if extra is not None else []
|
||||||
|
self.key = key
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
|
class NostrSignEvent(protobuf.MessageType):
|
||||||
|
MESSAGE_WIRE_TYPE = 2003
|
||||||
|
FIELDS = {
|
||||||
|
1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None),
|
||||||
|
2: protobuf.Field("created_at", "uint32", repeated=False, required=True),
|
||||||
|
3: protobuf.Field("kind", "uint32", repeated=False, required=True),
|
||||||
|
4: protobuf.Field("tags", "NostrTag", repeated=True, required=False, default=None),
|
||||||
|
5: protobuf.Field("content", "string", repeated=False, required=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
created_at: "int",
|
||||||
|
kind: "int",
|
||||||
|
content: "str",
|
||||||
|
address_n: Optional[Sequence["int"]] = None,
|
||||||
|
tags: Optional[Sequence["NostrTag"]] = None,
|
||||||
|
) -> None:
|
||||||
|
self.address_n: Sequence["int"] = address_n if address_n is not None else []
|
||||||
|
self.tags: Sequence["NostrTag"] = tags if tags is not None else []
|
||||||
|
self.created_at = created_at
|
||||||
|
self.kind = kind
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
|
||||||
|
class NostrEventSignature(protobuf.MessageType):
|
||||||
|
MESSAGE_WIRE_TYPE = 2004
|
||||||
|
FIELDS = {
|
||||||
|
1: protobuf.Field("pubkey", "bytes", repeated=False, required=True),
|
||||||
|
2: protobuf.Field("id", "bytes", repeated=False, required=True),
|
||||||
|
3: protobuf.Field("signature", "bytes", repeated=False, required=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
pubkey: "bytes",
|
||||||
|
id: "bytes",
|
||||||
|
signature: "bytes",
|
||||||
|
) -> None:
|
||||||
|
self.pubkey = pubkey
|
||||||
|
self.id = id
|
||||||
|
self.signature = signature
|
||||||
|
|
||||||
|
|
||||||
class RippleGetAddress(protobuf.MessageType):
|
class RippleGetAddress(protobuf.MessageType):
|
||||||
MESSAGE_WIRE_TYPE = 400
|
MESSAGE_WIRE_TYPE = 400
|
||||||
FIELDS = {
|
FIELDS = {
|
||||||
|
40
python/src/trezorlib/nostr.py
Normal file
40
python/src/trezorlib/nostr.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# This file is part of the Trezor project.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2025 SatoshiLabs and contributors
|
||||||
|
#
|
||||||
|
# This library is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License version 3
|
||||||
|
# as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# 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 License along with this library.
|
||||||
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||||
|
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from . import messages
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .client import TrezorClient
|
||||||
|
from .tools import Address
|
||||||
|
|
||||||
|
|
||||||
|
def get_pubkey(client: "TrezorClient", n: "Address") -> bytes:
|
||||||
|
return client.call(
|
||||||
|
messages.NostrGetPubkey(
|
||||||
|
address_n=n,
|
||||||
|
),
|
||||||
|
expect=messages.NostrPubkey,
|
||||||
|
).pubkey
|
||||||
|
|
||||||
|
|
||||||
|
def sign_event(
|
||||||
|
client: "TrezorClient",
|
||||||
|
sign_event: messages.NostrSignEvent,
|
||||||
|
) -> messages.NostrEventSignature:
|
||||||
|
return client.call(sign_event, expect=messages.NostrEventSignature)
|
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Generates the `trezor_message_impl!` macro calls for the `src/messages/mod.rs` file.
|
# Generates the `trezor_message_impl!` macro calls for the `src/messages/generated.rs` file.
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ FEATURES = {
|
|||||||
"EOS": "eos",
|
"EOS": "eos",
|
||||||
"Monero": "monero",
|
"Monero": "monero",
|
||||||
"NEM": "nem",
|
"NEM": "nem",
|
||||||
|
"Nostr": "nostr",
|
||||||
"Ripple": "ripple",
|
"Ripple": "ripple",
|
||||||
"Solana": "solana",
|
"Solana": "solana",
|
||||||
"Stellar": "stellar",
|
"Stellar": "stellar",
|
||||||
|
8
rust/trezor-client/src/messages/generated.rs
generated
8
rust/trezor-client/src/messages/generated.rs
generated
@ -237,6 +237,14 @@ trezor_message_impl! {
|
|||||||
NEMDecryptedMessage => MessageType_NEMDecryptedMessage,
|
NEMDecryptedMessage => MessageType_NEMDecryptedMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nostr")]
|
||||||
|
trezor_message_impl! {
|
||||||
|
NostrGetPubkey => MessageType_NostrGetPubkey,
|
||||||
|
NostrPubkey => MessageType_NostrPubkey,
|
||||||
|
NostrSignEvent => MessageType_NostrSignEvent,
|
||||||
|
NostrEventSignature => MessageType_NostrEventSignature,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ripple")]
|
#[cfg(feature = "ripple")]
|
||||||
trezor_message_impl! {
|
trezor_message_impl! {
|
||||||
RippleGetAddress => MessageType_RippleGetAddress,
|
RippleGetAddress => MessageType_RippleGetAddress,
|
||||||
|
56
rust/trezor-client/src/protos/generated/messages.rs
generated
56
rust/trezor-client/src/protos/generated/messages.rs
generated
@ -514,6 +514,14 @@ pub enum MessageType {
|
|||||||
MessageType_SolanaSignTx = 904,
|
MessageType_SolanaSignTx = 904,
|
||||||
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaTxSignature)
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaTxSignature)
|
||||||
MessageType_SolanaTxSignature = 905,
|
MessageType_SolanaTxSignature = 905,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrGetPubkey)
|
||||||
|
MessageType_NostrGetPubkey = 2001,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrPubkey)
|
||||||
|
MessageType_NostrPubkey = 2002,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrSignEvent)
|
||||||
|
MessageType_NostrSignEvent = 2003,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrEventSignature)
|
||||||
|
MessageType_NostrEventSignature = 2004,
|
||||||
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_BenchmarkListNames)
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_BenchmarkListNames)
|
||||||
MessageType_BenchmarkListNames = 9100,
|
MessageType_BenchmarkListNames = 9100,
|
||||||
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_BenchmarkNames)
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_BenchmarkNames)
|
||||||
@ -776,6 +784,10 @@ impl ::protobuf::Enum for MessageType {
|
|||||||
903 => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress),
|
903 => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress),
|
||||||
904 => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx),
|
904 => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx),
|
||||||
905 => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature),
|
905 => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature),
|
||||||
|
2001 => ::std::option::Option::Some(MessageType::MessageType_NostrGetPubkey),
|
||||||
|
2002 => ::std::option::Option::Some(MessageType::MessageType_NostrPubkey),
|
||||||
|
2003 => ::std::option::Option::Some(MessageType::MessageType_NostrSignEvent),
|
||||||
|
2004 => ::std::option::Option::Some(MessageType::MessageType_NostrEventSignature),
|
||||||
9100 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkListNames),
|
9100 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkListNames),
|
||||||
9101 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkNames),
|
9101 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkNames),
|
||||||
9102 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkRun),
|
9102 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkRun),
|
||||||
@ -1029,6 +1041,10 @@ impl ::protobuf::Enum for MessageType {
|
|||||||
"MessageType_SolanaAddress" => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress),
|
"MessageType_SolanaAddress" => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress),
|
||||||
"MessageType_SolanaSignTx" => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx),
|
"MessageType_SolanaSignTx" => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx),
|
||||||
"MessageType_SolanaTxSignature" => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature),
|
"MessageType_SolanaTxSignature" => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature),
|
||||||
|
"MessageType_NostrGetPubkey" => ::std::option::Option::Some(MessageType::MessageType_NostrGetPubkey),
|
||||||
|
"MessageType_NostrPubkey" => ::std::option::Option::Some(MessageType::MessageType_NostrPubkey),
|
||||||
|
"MessageType_NostrSignEvent" => ::std::option::Option::Some(MessageType::MessageType_NostrSignEvent),
|
||||||
|
"MessageType_NostrEventSignature" => ::std::option::Option::Some(MessageType::MessageType_NostrEventSignature),
|
||||||
"MessageType_BenchmarkListNames" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkListNames),
|
"MessageType_BenchmarkListNames" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkListNames),
|
||||||
"MessageType_BenchmarkNames" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkNames),
|
"MessageType_BenchmarkNames" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkNames),
|
||||||
"MessageType_BenchmarkRun" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkRun),
|
"MessageType_BenchmarkRun" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkRun),
|
||||||
@ -1281,6 +1297,10 @@ impl ::protobuf::Enum for MessageType {
|
|||||||
MessageType::MessageType_SolanaAddress,
|
MessageType::MessageType_SolanaAddress,
|
||||||
MessageType::MessageType_SolanaSignTx,
|
MessageType::MessageType_SolanaSignTx,
|
||||||
MessageType::MessageType_SolanaTxSignature,
|
MessageType::MessageType_SolanaTxSignature,
|
||||||
|
MessageType::MessageType_NostrGetPubkey,
|
||||||
|
MessageType::MessageType_NostrPubkey,
|
||||||
|
MessageType::MessageType_NostrSignEvent,
|
||||||
|
MessageType::MessageType_NostrEventSignature,
|
||||||
MessageType::MessageType_BenchmarkListNames,
|
MessageType::MessageType_BenchmarkListNames,
|
||||||
MessageType::MessageType_BenchmarkNames,
|
MessageType::MessageType_BenchmarkNames,
|
||||||
MessageType::MessageType_BenchmarkRun,
|
MessageType::MessageType_BenchmarkRun,
|
||||||
@ -1539,10 +1559,14 @@ impl ::protobuf::EnumFull for MessageType {
|
|||||||
MessageType::MessageType_SolanaAddress => 240,
|
MessageType::MessageType_SolanaAddress => 240,
|
||||||
MessageType::MessageType_SolanaSignTx => 241,
|
MessageType::MessageType_SolanaSignTx => 241,
|
||||||
MessageType::MessageType_SolanaTxSignature => 242,
|
MessageType::MessageType_SolanaTxSignature => 242,
|
||||||
MessageType::MessageType_BenchmarkListNames => 243,
|
MessageType::MessageType_NostrGetPubkey => 243,
|
||||||
MessageType::MessageType_BenchmarkNames => 244,
|
MessageType::MessageType_NostrPubkey => 244,
|
||||||
MessageType::MessageType_BenchmarkRun => 245,
|
MessageType::MessageType_NostrSignEvent => 245,
|
||||||
MessageType::MessageType_BenchmarkResult => 246,
|
MessageType::MessageType_NostrEventSignature => 246,
|
||||||
|
MessageType::MessageType_BenchmarkListNames => 247,
|
||||||
|
MessageType::MessageType_BenchmarkNames => 248,
|
||||||
|
MessageType::MessageType_BenchmarkRun => 249,
|
||||||
|
MessageType::MessageType_BenchmarkResult => 250,
|
||||||
};
|
};
|
||||||
Self::enum_descriptor().value_by_index(index)
|
Self::enum_descriptor().value_by_index(index)
|
||||||
}
|
}
|
||||||
@ -1561,7 +1585,7 @@ impl MessageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\xe8U\
|
\n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\x86W\
|
||||||
\n\x0bMessageType\x12(\n\x16MessageType_Initialize\x10\0\x1a\x0c\x80\xa6\
|
\n\x0bMessageType\x12(\n\x16MessageType_Initialize\x10\0\x1a\x0c\x80\xa6\
|
||||||
\x1d\x01\xb0\xb5\x18\x01\x90\xb5\x18\x01\x12\x1e\n\x10MessageType_Ping\
|
\x1d\x01\xb0\xb5\x18\x01\x90\xb5\x18\x01\x12\x1e\n\x10MessageType_Ping\
|
||||||
\x10\x01\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12%\n\x13MessageType_S\
|
\x10\x01\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12%\n\x13MessageType_S\
|
||||||
@ -1838,15 +1862,19 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||||||
\x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\x87\x07\x1a\x04\x98\xb5\
|
\x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\x87\x07\x1a\x04\x98\xb5\
|
||||||
\x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\x88\x07\x1a\x04\x90\xb5\
|
\x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\x88\x07\x1a\x04\x90\xb5\
|
||||||
\x18\x01\x12(\n\x1dMessageType_SolanaTxSignature\x10\x89\x07\x1a\x04\x98\
|
\x18\x01\x12(\n\x1dMessageType_SolanaTxSignature\x10\x89\x07\x1a\x04\x98\
|
||||||
\xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkListNames\x10\x8cG\x1a\x04\
|
\xb5\x18\x01\x12%\n\x1aMessageType_NostrGetPubkey\x10\xd1\x0f\x1a\x04\
|
||||||
\x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\x10\x8dG\x1a\x04\
|
\x90\xb5\x18\x01\x12\"\n\x17MessageType_NostrPubkey\x10\xd2\x0f\x1a\x04\
|
||||||
\x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\x8eG\x1a\x04\x80\
|
\x98\xb5\x18\x01\x12%\n\x1aMessageType_NostrSignEvent\x10\xd3\x0f\x1a\
|
||||||
\xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\x10\x8fG\x1a\x04\x80\
|
\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_NostrEventSignature\x10\xd4\
|
||||||
\xa6\x1d\x01\x1a\x04\xc8\xf3\x18\x01\"\x04\x08Z\x10\\\"\x04\x08G\x10J\"\
|
\x0f\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkListNames\
|
||||||
\x04\x08r\x10z\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\xe0\x01\x10\xe0\
|
\x10\x8cG\x1a\x04\x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\
|
||||||
\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\x10\xb8\x02\"\x06\
|
\x10\x8dG\x1a\x04\x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\
|
||||||
\x08\xe8\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.protobufB\rTrezo\
|
\x8eG\x1a\x04\x80\xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\x10\
|
||||||
rMessage\x80\xa6\x1d\x01\
|
\x8fG\x1a\x04\x80\xa6\x1d\x01\x1a\x04\xc8\xf3\x18\x01\"\x04\x08Z\x10\\\"\
|
||||||
|
\x04\x08G\x10J\"\x04\x08r\x10z\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\
|
||||||
|
\xe0\x01\x10\xe0\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\x10\
|
||||||
|
\xb8\x02\"\x06\x08\xe8\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.pr\
|
||||||
|
otobufB\rTrezorMessage\x80\xa6\x1d\x01\
|
||||||
";
|
";
|
||||||
|
|
||||||
/// `FileDescriptorProto` object which was a source for this generated file
|
/// `FileDescriptorProto` object which was a source for this generated file
|
||||||
|
1155
rust/trezor-client/src/protos/generated/messages_nostr.rs
generated
Normal file
1155
rust/trezor-client/src/protos/generated/messages_nostr.rs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -33,6 +33,7 @@ mod generated {
|
|||||||
"eos" => messages_eos
|
"eos" => messages_eos
|
||||||
"monero" => messages_monero
|
"monero" => messages_monero
|
||||||
"nem" => messages_nem
|
"nem" => messages_nem
|
||||||
|
"nostr" => messages_nostr
|
||||||
"ripple" => messages_ripple
|
"ripple" => messages_ripple
|
||||||
"solana" => messages_solana
|
"solana" => messages_solana
|
||||||
"stellar" => messages_stellar
|
"stellar" => messages_stellar
|
||||||
|
0
tests/device_tests/nostr/__init__.py
Normal file
0
tests/device_tests/nostr/__init__.py
Normal file
129
tests/device_tests/nostr/test_nostr.py
Normal file
129
tests/device_tests/nostr/test_nostr.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# This file is part of the Trezor project.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2025 SatoshiLabs and contributors
|
||||||
|
#
|
||||||
|
# This library is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License version 3
|
||||||
|
# as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# 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 License along with this library.
|
||||||
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||||
|
|
||||||
|
import json
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from ecdsa import SECP256k1, VerifyingKey
|
||||||
|
from six import b
|
||||||
|
|
||||||
|
from trezorlib import messages, nostr
|
||||||
|
from trezorlib.tools import parse_path
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.altcoin, pytest.mark.models("core")]
|
||||||
|
|
||||||
|
# test data from NIP-06: https://github.com/nostr-protocol/nips/blob/master/06.md
|
||||||
|
|
||||||
|
LEAD_MONKEY_MNEMONIC = (
|
||||||
|
"leader monkey parrot ring guide accident before fence cannon height naive bean"
|
||||||
|
)
|
||||||
|
LEAD_MONKEY_PUBKEY_HEX = (
|
||||||
|
"17162c921dc4d2518f9a101db33695df1afb56ab82f5ff3e5da6eec3ca5cd917"
|
||||||
|
)
|
||||||
|
|
||||||
|
WHAT_BLEAK_MNEMONIC = "what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade"
|
||||||
|
WHAT_BLEAK_PUBKEY_HEX = (
|
||||||
|
"d41b22899549e1f3d335a31002cfd382174006e166d3e658e3a5eecdb6463573"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
pytestmark_lead_monkey = pytest.mark.setup_client(mnemonic=LEAD_MONKEY_MNEMONIC)
|
||||||
|
pytestmark_what_bleak = pytest.mark.setup_client(mnemonic=WHAT_BLEAK_MNEMONIC)
|
||||||
|
|
||||||
|
VECTORS = [
|
||||||
|
pytest.param(LEAD_MONKEY_PUBKEY_HEX, marks=pytestmark_lead_monkey),
|
||||||
|
pytest.param(WHAT_BLEAK_PUBKEY_HEX, marks=pytestmark_what_bleak),
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_EVENT = {
|
||||||
|
"created_at": 1737396950,
|
||||||
|
"kind": 1,
|
||||||
|
"tags": [
|
||||||
|
[
|
||||||
|
"e",
|
||||||
|
"5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36",
|
||||||
|
"wss://nostr.example.com",
|
||||||
|
],
|
||||||
|
["p", "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca"],
|
||||||
|
[
|
||||||
|
"a",
|
||||||
|
"30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd",
|
||||||
|
"wss://nostr.example.com",
|
||||||
|
],
|
||||||
|
["alt", "reply"],
|
||||||
|
],
|
||||||
|
"content": "Hello, world",
|
||||||
|
}
|
||||||
|
|
||||||
|
SIGN_TEST_EVENT = messages.NostrSignEvent(
|
||||||
|
address_n=parse_path("m/44h/1237h/0h/0/0"),
|
||||||
|
created_at=TEST_EVENT["created_at"],
|
||||||
|
kind=TEST_EVENT["kind"],
|
||||||
|
content=TEST_EVENT["content"],
|
||||||
|
tags=[
|
||||||
|
messages.NostrTag(key=t[0], value=t[1] if len(t) > 1 else None, extra=t[2:])
|
||||||
|
for t in TEST_EVENT["tags"]
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("pubkey_hex", VECTORS)
|
||||||
|
def test_get_pubkey(client, pubkey_hex):
|
||||||
|
response = nostr.get_pubkey(
|
||||||
|
client,
|
||||||
|
n=parse_path("m/44h/1237h/0h/0/0"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response == bytes.fromhex(pubkey_hex)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("pubkey_hex", VECTORS)
|
||||||
|
def test_sign_event(client, pubkey_hex):
|
||||||
|
response = nostr.sign_event(client, SIGN_TEST_EVENT)
|
||||||
|
|
||||||
|
assert response.pubkey == bytes.fromhex(pubkey_hex)
|
||||||
|
|
||||||
|
expected_id = (
|
||||||
|
sha256(
|
||||||
|
json.dumps(
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
pubkey_hex,
|
||||||
|
TEST_EVENT["created_at"],
|
||||||
|
TEST_EVENT["kind"],
|
||||||
|
TEST_EVENT["tags"],
|
||||||
|
TEST_EVENT["content"],
|
||||||
|
],
|
||||||
|
separators=(",", ":"),
|
||||||
|
).encode()
|
||||||
|
)
|
||||||
|
.digest()
|
||||||
|
.hex()
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.id == expected_id
|
||||||
|
|
||||||
|
vk = VerifyingKey.from_string(
|
||||||
|
b("\x03") + bytes.fromhex(pubkey_hex),
|
||||||
|
curve=SECP256k1,
|
||||||
|
# this is a pretty silly way to tell VerifyingKey
|
||||||
|
# that we do not want the message to be hashed
|
||||||
|
# when verifying the signature!
|
||||||
|
hashfunc=lambda x: type("h", (), {"digest": lambda: x}),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert vk.verify(bytes.fromhex(response.signature), bytes.fromhex(response.id))
|
Loading…
Reference in New Issue
Block a user