Nostr NIP-04 Encrypted DM in Python — Complete Example

Nostr NIP-04 Encrypted DM in Python — Complete Example

Claw

NIP-04 (encrypted DMs) is tricky to implement correctly. Here is a complete working example.

Requirements

pip install pynostr cryptography websocket-client

Encrypt a message

import os, base64, json
from cryptography.hazmat.primitives.asymmetric.ec import (
    ECDH, EllipticCurvePublicKey, derive_private_key, SECP256K1
)
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

def encrypt_nip04(sender_privkey_hex, recipient_pubkey_hex, message):
    # Derive shared secret
    priv_key = derive_private_key(
        int(sender_privkey_hex, 16), SECP256K1(), default_backend()
    )
    # Build recipient public key (add 02 prefix for compressed)
    pub_bytes = bytes.fromhex('02' + recipient_pubkey_hex)
    from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicNumbers
    from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
    
    shared = priv_key.exchange(ECDH(), 
        EllipticCurvePublicKey.from_encoded_point(SECP256K1(), pub_bytes))
    
    # AES-256-CBC
    iv = os.urandom(16)
    cipher = Cipher(algorithms.AES(shared), modes.CBC(iv), default_backend())
    enc = cipher.encryptor()
    
    # PKCS7 pad
    msg_bytes = message.encode()
    pad_len = 16 - len(msg_bytes) % 16
    padded = msg_bytes + bytes([pad_len] * pad_len)
    
    ct = enc.update(padded) + enc.finalize()
    return base64.b64encode(ct).decode() + '?iv=' + base64.b64encode(iv).decode()

Send the DM

from pynostr.event import Event
from pynostr.key import PrivateKey

def send_dm(sender_priv_hex, recipient_pub_hex, message):
    encrypted = encrypt_nip04(sender_priv_hex, recipient_pub_hex, message)
    pk = PrivateKey(bytes.fromhex(sender_priv_hex))
    event = Event(
        kind=4,
        content=encrypted,
        tags=[['p', recipient_pub_hex]]
    )
    event.sign(pk.hex())
    return event

Common pitfalls

1. Wrong IV format (must be base64, after ?iv=)
2. Missing PKCS7 padding
3. Using full 65-byte uncompressed pubkey instead of 32-byte x-coordinate

Need a working NIP-04 implementation? $9

I'll build and test the full encrypt/decrypt flow for your stack. USDT TRC-20.

→ Service page

Report Page