-
-
Notifications
You must be signed in to change notification settings - Fork 671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce Nostr #4436
base: main
Are you sure you want to change the base?
Introduce Nostr #4436
Changes from all commits
294b8ef
9122ef9
e4f33f1
80293b5
7c392be
b86d620
a9ed827
1566510
856d06c
631dfec
7326a76
2406187
4dd31d4
14a8513
af4f669
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
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"; | ||
|
||
import "options.proto"; | ||
|
||
/** | ||
* 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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add Nostr support. | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ ALTCOIN_PREFIXES = ( | |
"fido", | ||
"monero", | ||
"nem", | ||
"nostr", | ||
"ripple", | ||
"solana", | ||
"stellar", | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from apps.common.paths import PATTERN_BIP44 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. first entry here should be something like
see |
||
|
||
CURVE = "secp256k1" | ||
SLIP44_ID = 1237 | ||
PATTERN = PATTERN_BIP44 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. aside: there's no showing the Nostr key on screen, will that be added in the future? if not, this is useless because you can get the same pubkey via |
||
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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
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 import TR | ||
from trezor.crypto.curve import secp256k1 | ||
from trezor.crypto.hashlib import sha256 | ||
from trezor.messages import NostrEventSignature | ||
from trezor.ui.layouts import confirm_value | ||
|
||
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:] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we do this? Don't we care about the public key parity? Or does nostr use x-only keys? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
From what I understand, Nostr uses X-only keys, indeed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it is not guaranteed that |
||
sk = node.private_key() | ||
|
||
title = TR.nostr__event_kind_template.format(kind) | ||
|
||
# confirm_value on TR only accepts one single info item | ||
obrusvit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# which is why we concatenate all of them here. | ||
# This is not great, but it gets the job done for now. | ||
tags_str = f"created_at: {created_at}" | ||
for t in tags: | ||
tags_str += f"\n\n{t[0]}: " + (f" {' '.join(t[1:])}" if len(t) > 1 else "") | ||
|
||
await confirm_value( | ||
title, content, "", "nostr_sign_event", info_items=[("", tags_str)] | ||
) | ||
|
||
# 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}"]' | ||
obrusvit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls mark it somehow so that we know that it's debug-only