Expand description
RFC-007 D3.3: NIP-44 v2 encrypted payloads — the Nostr DM encryption.
Consumes RFC-006’s reserved enc slot with a vetted spec instead of
bespoke crypto (the reuse > build principle). NIP-44 v2 encrypts between
two secp256k1 keys — here, the D3.1 Nostr transport keys — so a wire DM
sent over Nostr is confidential to the relay.
§Construction (NIP-44 v2)
- conversation key =
HKDF-Extract(salt = "nip44-v2", IKM = ecdh_x)whereecdh_xis the x-coordinate of the secp256k1 ECDH point between my secret and their (x-only, even-y) public key. Symmetric: both parties derive the same key. - per-message keys =
HKDF-Expand(conversation_key, info = nonce, 76)→chacha_key[32] ‖ chacha_nonce[12] ‖ hmac_key[32]. - padding — the plaintext is length-prefixed (2-byte BE) and zero-padded to a power-of-two-ish boundary so ciphertext length leaks only a coarse bucket, not the exact message size.
- cipher = ChaCha20 (stream) over the padded plaintext.
- MAC =
HMAC-SHA256(hmac_key, nonce ‖ ciphertext), verified in constant time before decryption. - payload =
base64(0x02 ‖ nonce[32] ‖ ciphertext ‖ mac[32]).
Interop: validated byte-exact against the official NIP-44 v2 vectors
(testdata/nip44_official_vectors.json — the valid subset from the
reference implementation, public domain). All 35 conversation-key, 10
encrypt/decrypt (including exact ciphertext payloads), and 24 padded-length
cases pass, so wire’s NIP-44 interoperates with other implementations — plus
round-trip / ECDH-symmetry / tamper / wrong-key unit tests.
Enums§
Functions§
- calc_
padded_ len - NIP-44 padded length for an unpadded plaintext length (excludes the 2-byte length prefix). Powers-of-two-ish bucketing so the ciphertext size leaks only a coarse bucket.
- conversation_
key - Derive the symmetric conversation key between my secret key and their x-only
public key.
HKDF-Extract(salt="nip44-v2", IKM = ecdh_x). - decrypt
- Decrypt a base64 NIP-44 payload under
conversation_key. Constant-time MAC check before decryption; fail-closed on any structural / MAC / padding error. - encrypt
- Encrypt
plaintextunderconversation_keywith a fresh random nonce. - encrypt_
with_ nonce - Encrypt
plaintextunderconversation_keywith an explicit 32-bytenonce. Returns the base64 NIP-44 payload. (Production callers useencrypt, which supplies a random nonce.)