From 4199f202365fc4c1a464b1033d040597bccbb08d Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 20 Dec 2023 13:56:03 -0300 Subject: [PATCH] nip-44: remove author names and arbitrary line-breaks. --- 44.md | 66 ++++++++++++++++++----------------------------------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/44.md b/44.md index 1282d261..e66a7b3c 100644 --- a/44.md +++ b/44.md @@ -2,7 +2,7 @@ ## Encrypted Payloads (Versioned) -`optional` `author:paulmillr` `author:staab` +`optional` The NIP introduces a new data format for keypair-based encryption. This NIP is versioned 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 - No attachments: they are not supported -Lack of forward secrecy is partially mitigated: 1) the messages -should only be stored on relays, specified by the user, instead of a set of -all public relays 2) the relays are supposed to regularly delete older messages. +Lack of forward secrecy is partially mitigated by these two factors: + 1. the messages should only be stored on relays, specified by the user, instead of a set of all public relays. + 2. the relays are supposed to regularly delete older messages. -For risky situations, users should chat in specialized E2EE messaging software and limit use -of nostr to exchanging contacts. +For risky situations, users should chat in specialized E2EE messaging software and limit use of nostr to exchanging contacts. ## 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. -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. +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. -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. +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. ## Versions @@ -56,18 +50,12 @@ Currently defined encryption algorithms: The algorithm choices are justified in a following way: -- 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. -- 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 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. -- 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 +- 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. +- 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 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. +- 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 @@ -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)` - `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) - - `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]` + - `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]` - Operators - `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` @@ -225,15 +208,11 @@ def decrypt(payload, conversation_key): - Validate that AAD (nonce) is 32 bytes 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, -with result base64 assigned to event's `content`. Then, use NIP-01 to sign -the event using schnorr signature scheme over secp256k1. +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. #### Decryption -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. +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. 1. Check if first payload's character is `#` - `#` 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 -A collection of implementations in different languages is -available [on GitHub](https://github.com/paulmillr/nip44). +A collection of implementations in different languages is available at https://github.com/paulmillr/nip44. -We publish extensive test vectors. Instead of having it in the -document directly, a sha256 checksum of vectors is provided: +We publish extensive test vectors. Instead of having it in the document directly, a sha256 checksum of vectors is provided: 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_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.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. -- `valid.encrypt_decrypt_long_msg`: same as previous step, but instead of a full plaintext and payload, - their checksum is provided. +- `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. +- `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.get_conversation_key`: calculating converastion_key must throw an error - `invalid.decrypt`: decrypting message content must throw an error