Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions 44.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ messaging software and limit use of nostr to exchanging contacts.
On its own, messages sent using this scheme have a number of important shortcomings:

- No deniability: it is possible to prove an event was signed by a particular key
- No forward secrecy: when a key is compromised, it is possible to decrypt all previous conversations
- No forward secrecy (if using default salt): when a key is compromised, it is possible to decrypt all previous conversations
- No post-compromise security: when a key is compromised, it is possible to decrypt all future conversations
- No post-quantum security: a powerful quantum computer would be able to decrypt the messages
- IP address leak: user IP may be seen by relays and all intermediaries between user and relay
Expand All @@ -64,13 +64,15 @@ NIP-44 version 2 has the following design characteristics:
is smaller in non-parallel environments.
- A custom padding scheme is used instead of padmé because it provides better leakage reduction for small messages.
- Base64 encoding is used instead of another compression algorithm because it is widely available, and is already used in nostr.
- Custom salt is allowed for conversation key calculation to enable forward secrecy.

### Encryption

1. Calculate a conversation key
- Execute ECDH (scalar multiplication) of public key B by private key A
Output `shared_x` must be unhashed, 32-byte encoded x coordinate of the shared point
- Use HKDF-extract with sha256, `IKM=shared_x` and `salt=utf8_encode('nip44-v2')`
- Use HKDF-extract with sha256, `IKM=shared_x` and `salt=utf8_encode(custom_string || 'nip44-v2')`
- Validate that salt is up to 32 bytes
- HKDF output will be a `conversation_key` between two users.
- It is always the same, when key roles are swapped: `conv(a, B) == conv(b, A)`
2. Generate a random 32-byte nonce
Expand Down Expand Up @@ -220,9 +222,11 @@ def hmac_aad(key, message, aad):
return hmac(sha256, key, concat(aad, message));

# Calculates long-term key between users A and B: `get_key(Apriv, Bpub) == get_key(Bpriv, Apub)`
def get_conversation_key(private_key_a, public_key_b):
def get_conversation_key(private_key_a, public_key_b, salt):
if (!salt) salt = utf8_encode('nip44-v2')
if len(salt) > 32: raise Exception('invalid salt length')
shared_x = secp256k1_ecdh(private_key_a, public_key_b)
return hkdf_extract(IKM=shared_x, salt=utf8_encode('nip44-v2'))
return hkdf_extract(IKM=shared_x, salt)

# Calculates unique per-message key
def get_message_keys(conversation_key, nonce):
Expand Down