nip-44: remove author names and arbitrary line-breaks.

This commit is contained in:
fiatjaf 2023-12-20 13:56:03 -03:00
parent 822b70a565
commit 4199f20236
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1

66
44.md
View File

@ -2,7 +2,7 @@
## Encrypted Payloads (Versioned) ## Encrypted Payloads (Versioned)
`optional` `author:paulmillr` `author:staab` `optional`
The NIP introduces a new data format for keypair-based encryption. This NIP is versioned The NIP introduces a new data format for keypair-based encryption. This NIP is versioned
to allow multiple algorithm choices to exist simultaneously. to allow multiple algorithm choices to exist simultaneously.
@ -22,12 +22,11 @@ The scheme has a number of important shortcomings:
- Limited message size leak: padding only partially obscures true message length - Limited message size leak: padding only partially obscures true message length
- No attachments: they are not supported - No attachments: they are not supported
Lack of forward secrecy is partially mitigated: 1) the messages Lack of forward secrecy is partially mitigated by these two factors:
should only be stored on relays, specified by the user, instead of a set of 1. the messages should only be stored on relays, specified by the user, instead of a set of all public relays.
all public relays 2) the relays are supposed to regularly delete older messages. 2. the relays are supposed to regularly delete older messages.
For risky situations, users should chat in specialized E2EE messaging software and limit use For risky situations, users should chat in specialized E2EE messaging software and limit use of nostr to exchanging contacts.
of nostr to exchanging contacts.
## Dependence on NIP-01 ## Dependence on NIP-01
@ -35,14 +34,9 @@ It's not enough to use NIP-44 for encryption: the output must also be signed.
In nostr case, the payload is serialized and signed as per NIP-01 rules. In nostr case, the payload is serialized and signed as per NIP-01 rules.
The same event can be serialized in two different ways, The same event can be serialized in two different ways, resulting in two distinct signatures. So, it's important to ensure serialization rules, which are defined in NIP-01, are the same across different NIP-44 implementations.
resulting in two distinct signatures. So, it's important
to ensure serialization rules, which are defined in NIP-01,
are the same across different NIP-44 implementations.
After serialization, the event is signed by Schnorr signature over secp256k1, After serialization, the event is signed by Schnorr signature over secp256k1, defined in BIP340. It's important to ensure the key and signature validity as per BIP340 rules.
defined in BIP340. It's important to ensure the key and signature validity as
per BIP340 rules.
## Versions ## Versions
@ -56,18 +50,12 @@ Currently defined encryption algorithms:
The algorithm choices are justified in a following way: The algorithm choices are justified in a following way:
- Encrypt-then-mac-then-sign instead of encrypt-then-sign-then-mac: - Encrypt-then-mac-then-sign instead of encrypt-then-sign-then-mac: only events wrapped in NIP-01 signed envelope are currently accepted by nostr.
only events wrapped in NIP-01 signed envelope are currently accepted by nostr. - ChaCha instead of AES: it's faster and has [better security against multi-key attacks](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aead-limits/)
- ChaCha instead of AES: it's faster and has - ChaCha instead of XChaCha: XChaCha has not been standardized. Also, we don't need xchacha's improved collision resistance of nonces: every message has a new (key, nonce) pair.
[better security against multi-key attacks](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aead-limits/) - HMAC-SHA256 instead of Poly1305: polynomial MACs are much easier to forge SHA256 instead of SHA3 or BLAKE: it is already used in nostr. Also blake's
- ChaCha instead of XChaCha: XChaCha has not been standardized. Also, we don't need xchacha's improved speed advantage is smaller in non-parallel environments - Custom padding instead of padmé: better leakage reduction for small messages
collision resistance of nonces: every message has a new (key, nonce) pair. - Base64 encoding instead of an other compression algorithm: it is widely available, and is already used in nostr
- HMAC-SHA256 instead of Poly1305: polynomial MACs are much easier to forge
- SHA256 instead of SHA3 or BLAKE: it is already used in nostr. Also blake's
speed advantage is smaller in non-parallel environments
- Custom padding instead of padmé: better leakage reduction for small messages
- Base64 encoding instead of an other compression algorithm: it is widely available,
and is already used in nostr
### Functions and operations ### Functions and operations
@ -77,12 +65,7 @@ The algorithm choices are justified in a following way:
comprised of methods `hkdf_extract(IKM, salt)` and `hkdf_expand(OKM, info, L)` comprised of methods `hkdf_extract(IKM, salt)` and `hkdf_expand(OKM, info, L)`
- `chacha20(key, nonce, data)` is ChaCha20 [(RFC 8439)](https://datatracker.ietf.org/doc/html/rfc8439), with starting counter set to 0 - `chacha20(key, nonce, data)` is ChaCha20 [(RFC 8439)](https://datatracker.ietf.org/doc/html/rfc8439), with starting counter set to 0
- `hmac_sha256(key, message)` is HMAC [(RFC 2104)](https://datatracker.ietf.org/doc/html/rfc2104) - `hmac_sha256(key, message)` is HMAC [(RFC 2104)](https://datatracker.ietf.org/doc/html/rfc2104)
- `secp256k1_ecdh(priv_a, pub_b)` is multiplication of point B by - `secp256k1_ecdh(priv_a, pub_b)` is multiplication of point B by scalar a (`a ⋅ B`), defined in [BIP340](https://github.com/bitcoin/bips/blob/e918b50731397872ad2922a1b08a5a4cd1d6d546/bip-0340.mediawiki). The operation produces shared point, and we encode the shared point's 32-byte x coordinate, using method `bytes(P)` from BIP340. Private and public keys must be validated as per BIP340: pubkey must be a valid, on-curve point, and private key must be a scalar in range `[1, secp256k1_order - 1]`
scalar a (`a ⋅ B`), defined in
[BIP340](https://github.com/bitcoin/bips/blob/e918b50731397872ad2922a1b08a5a4cd1d6d546/bip-0340.mediawiki).
The operation produces shared point, and we encode the shared point's 32-byte x coordinate,
using method `bytes(P)` from BIP340. Private and public keys must be validated
as per BIP340: pubkey must be a valid, on-curve point, and private key must be a scalar in range `[1, secp256k1_order - 1]`
- Operators - Operators
- `x[i:j]`, where `x` is a byte array and `i, j <= 0`, - `x[i:j]`, where `x` is a byte array and `i, j <= 0`,
returns a `(j - i)`-byte array with a copy of the `i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x` returns a `(j - i)`-byte array with a copy of the `i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`
@ -225,15 +208,11 @@ def decrypt(payload, conversation_key):
- Validate that AAD (nonce) is 32 bytes - Validate that AAD (nonce) is 32 bytes
7. Base64-encode (with padding) params: `concat(version, nonce, ciphertext, mac)` 7. Base64-encode (with padding) params: `concat(version, nonce, ciphertext, mac)`
After encryption, it's necessary to sign it. Use NIP-01 to serialize the event, After encryption, it's necessary to sign it. Use NIP-01 to serialize the event, with result base64 assigned to event's `content`. Then, use NIP-01 to sign the event using schnorr signature scheme over secp256k1.
with result base64 assigned to event's `content`. Then, use NIP-01 to sign
the event using schnorr signature scheme over secp256k1.
#### Decryption #### Decryption
Before decryption, it's necessary to validate the message's pubkey and signature. Before decryption, it's necessary to validate the message's pubkey and signature. The public key must be a valid non-zero secp256k1 curve point, and signature must be valid secp256k1 schnorr signature. For exact validation rules, refer to BIP-340.
The public key must be a valid non-zero secp256k1 curve point, and signature must be valid
secp256k1 schnorr signature. For exact validation rules, refer to BIP-340.
1. Check if first payload's character is `#` 1. Check if first payload's character is `#`
- `#` is an optional future-proof flag that means non-base64 encoding is used - `#` is an optional future-proof flag that means non-base64 encoding is used
@ -260,11 +239,9 @@ secp256k1 schnorr signature. For exact validation rules, refer to BIP-340.
## Tests and code ## Tests and code
A collection of implementations in different languages is A collection of implementations in different languages is available at https://github.com/paulmillr/nip44.
available [on GitHub](https://github.com/paulmillr/nip44).
We publish extensive test vectors. Instead of having it in the We publish extensive test vectors. Instead of having it in the document directly, a sha256 checksum of vectors is provided:
document directly, a sha256 checksum of vectors is provided:
269ed0f69e4c192512cc779e78c555090cebc7c785b609e338a62afc3ce25040 nip44.vectors.json 269ed0f69e4c192512cc779e78c555090cebc7c785b609e338a62afc3ce25040 nip44.vectors.json
@ -286,11 +263,8 @@ The file also contains intermediate values. A quick guidance with regards to its
- `valid.get_conversation_key`: calculate conversation_key from secret key sec1 and public key pub2 - `valid.get_conversation_key`: calculate conversation_key from secret key sec1 and public key pub2
- `valid.get_message_keys`: calculate chacha_key, chacha_nocne, hmac_key from conversation_key and nonce - `valid.get_message_keys`: calculate chacha_key, chacha_nocne, hmac_key from conversation_key and nonce
- `valid.calc_padded_len`: take unpadded length (first value), calculate padded length (second value) - `valid.calc_padded_len`: take unpadded length (first value), calculate padded length (second value)
- `valid.encrypt_decrypt`: emulate real conversation. Calculate - `valid.encrypt_decrypt`: emulate real conversation. Calculate pub2 from sec2, verify conversation_key from (sec1, pub2), encrypt, verify payload, then calculate pub1 from sec1, verify conversation_key from (sec2, pub1), decrypt, verify plaintext.
pub2 from sec2, verify conversation_key from (sec1, pub2), encrypt, verify payload, - `valid.encrypt_decrypt_long_msg`: same as previous step, but instead of a full plaintext and payload, their checksum is provided.
then calculate pub1 from sec1, verify conversation_key from (sec2, pub1), decrypt, verify plaintext.
- `valid.encrypt_decrypt_long_msg`: same as previous step, but instead of a full plaintext and payload,
their checksum is provided.
- `invalid.encrypt_msg_lengths` - `invalid.encrypt_msg_lengths`
- `invalid.get_conversation_key`: calculating converastion_key must throw an error - `invalid.get_conversation_key`: calculating converastion_key must throw an error
- `invalid.decrypt`: decrypting message content must throw an error - `invalid.decrypt`: decrypting message content must throw an error