NIP-44

Encrypted Payloads (Versioned)

final encryption

NIP-44 provides secure end-to-end encryption for Nostr messages using modern cryptographic primitives. It addresses the security weaknesses of NIP-04 and introduces versioning for future upgrades.

Author
paulmillr
Last Updated
31 January 2026
Official Spec
View on GitHub →

NIP-44: Encrypted Payloads (Versioned)

Status: Final Author: paulmillr Category: Encryption


Overview

NIP-44 is the modern encryption standard for Nostr, designed to replace the original NIP-04 encrypted direct messages. It provides:

  • Stronger encryption using ChaCha20-Poly1305
  • Proper key derivation with HKDF
  • Padding to hide message length
  • Version field for future upgrades
  • Authentication preventing message tampering

If you’re building a new Nostr application, use NIP-44 instead of NIP-04 for all encrypted content.


Why NIP-44 Matters

Problems with NIP-04

NIP-04 had several security weaknesses:

  1. No message padding - Message length was visible
  2. Weak nonce generation - Using AES-CBC with random IVs
  3. No authentication - Messages could be modified
  4. No versioning - Impossible to upgrade encryption

NIP-44 Improvements

  • ChaCha20-Poly1305 - Modern authenticated encryption
  • HKDF key derivation - Cryptographically sound key generation
  • Message padding - Hides exact message length
  • Version byte - Allows future algorithm upgrades
  • Conversation keys - Efficient multi-message encryption

How It Works

Encryption Flow

  1. Generate conversation key from sender private key and recipient public key using ECDH + HKDF
  2. Generate nonce - 32 bytes of random data
  3. Derive message keys using HKDF with nonce
  4. Pad message to hide length
  5. Encrypt with ChaCha20-Poly1305
  6. Concatenate version + nonce + ciphertext

Encrypted Payload Structure

version (1 byte) | nonce (32 bytes) | ciphertext (variable) | auth tag (16 bytes)

Current version is 0x02.

Key Derivation

// Generate shared secret using ECDH
const sharedX = getSharedSecret(senderPrivKey, recipientPubKey).x;

// Derive conversation key using HKDF
const conversationKey = hkdf(sha256, sharedX, salt, info, 32);

Example Implementation

Encrypting a Message

import { nip44 } from 'nostr-tools';

const senderPrivateKey = '...';  // Your nsec (hex)
const recipientPublicKey = '...'; // Their npub (hex)

const message = "Hello, this is a secret message!";

// Encrypt the message
const ciphertext = nip44.encrypt(senderPrivateKey, recipientPublicKey, message);

// Result is base64-encoded
console.log(ciphertext);

Decrypting a Message

const recipientPrivateKey = '...'; // Your nsec (hex)
const senderPublicKey = '...';      // Their npub (hex)

// Decrypt the message
const plaintext = nip44.decrypt(recipientPrivateKey, senderPublicKey, ciphertext);

console.log(plaintext); // "Hello, this is a secret message!"

Event Format

Encrypted content using NIP-44 is typically stored in events with:

  • kind: 4 (for DMs) or other encrypted event kinds
  • content: Base64-encoded NIP-44 payload
  • tags: [“p”, recipientPubkey] for DMs
{
  "kind": 4,
  "pubkey": "sender_pubkey",
  "tags": [["p", "recipient_pubkey"]],
  "content": "AhZIThqMbm..."
}

Security Properties

What NIP-44 Protects

  • Confidentiality - Only sender and recipient can read
  • Integrity - Tampering is detected
  • Authenticity - Message provably from sender
  • Length hiding - Padding obscures message size

What NIP-44 Does NOT Protect

  • Metadata - Who’s messaging whom is visible
  • Timing - When messages are sent is visible
  • Forward secrecy - Compromised key reveals past messages

For stronger privacy, consider additional layers like Tor for network anonymity.


Migrating from NIP-04

For Developers

  1. Check version byte - NIP-44 starts with 0x02
  2. Support both - Decrypt NIP-04 for old messages
  3. Encrypt with NIP-44 - Use NIP-44 for new messages
  4. Update UI - Show encryption strength indicators

For Users

  • Old NIP-04 messages remain readable
  • New clients will automatically use NIP-44
  • No action required from users

Client Support

NIP-44 is supported by modern clients:

ClientNIP-44 Support
DamusFull
AmethystFull
PrimalFull
SnortFull
CoracleFull
noStrudelFull
0xchatFull

  • NIP-04 - Original encrypted DMs (deprecated)
  • NIP-17 - Private direct messages (gift wrapping)
  • NIP-46 - Nostr Connect (remote signing)
  • NIP-59 - Gift Wrap (metadata hiding)

Technical Details

Cryptographic Primitives

  • ECDH - secp256k1 key exchange
  • HKDF-SHA256 - Key derivation
  • ChaCha20-Poly1305 - Authenticated encryption
  • XChaCha20 - Extended nonce variant

Padding Scheme

Messages are padded to the next power of 2 (minimum 32 bytes) to hide exact length. This prevents length analysis attacks.

Version History

VersionDescription
0x02Current - ChaCha20-Poly1305
0x01Reserved
0x00Reserved

For Developers

Libraries

  • nostr-tools (JavaScript) - nip44.encrypt() / nip44.decrypt()
  • rust-nostr (Rust) - Full NIP-44 support
  • python-nostr (Python) - NIP-44 implementation

Testing

The NIP-44 specification includes test vectors for validating implementations. Ensure your implementation passes all test cases.


Summary

NIP-44 is the recommended encryption standard for Nostr:

  • Use NIP-44 for all new encrypted content
  • Support NIP-04 for backward compatibility
  • Modern cryptography ensures strong security
  • Versioning allows future improvements

Last updated: January 2026 Official specification: GitHub

Client Support

This NIP is supported by the following clients:

damus primal amethyst snort coracle nostrudel 0xchat
View all clients →

Related NIPs

NIP-04 NIP-17 NIP-46 NIP-59
← Browse All NIPs