Skip to main content

Module nip44

Module nip44 

Source
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) where ecdh_x is 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§

Nip44Error

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 plaintext under conversation_key with a fresh random nonce.
encrypt_with_nonce
Encrypt plaintext under conversation_key with an explicit 32-byte nonce. Returns the base64 NIP-44 payload. (Production callers use encrypt, which supplies a random nonce.)