NAME
Net::Nostr::Encryption - NIP-44 versioned encrypted payloads
SYNOPSIS
use Net::Nostr::Encryption;
use Net::Nostr::Key;
my $alice = Net::Nostr::Key->new;
my $bob = Net::Nostr::Key->new;
# Calculate shared conversation key
my $conv_key = Net::Nostr::Encryption->get_conversation_key(
$alice->privkey_hex, $bob->pubkey_hex,
);
# Encrypt a message
my $payload = Net::Nostr::Encryption->encrypt('Hello, Bob!', $conv_key);
# Bob decrypts using the same conversation key
my $conv_key2 = Net::Nostr::Encryption->get_conversation_key(
$bob->privkey_hex, $alice->pubkey_hex,
);
my $plaintext = Net::Nostr::Encryption->decrypt($payload, $conv_key2);
# $plaintext is 'Hello, Bob!'
DESCRIPTION
Implements NIP-44 version 2 encrypted payloads using secp256k1 ECDH, HKDF-SHA256, ChaCha20, and HMAC-SHA256. This module provides the encryption primitives - it does not define any event kinds.
The encryption is symmetric: get_conversation_key(a_priv, B_pub) produces the same key as get_conversation_key(b_priv, A_pub).
METHODS
get_conversation_key
my $key = Net::Nostr::Encryption->get_conversation_key($privkey_hex, $pubkey_hex);
Computes the shared conversation key between two users via ECDH and HKDF-extract. Both keys are 64-character hex strings. The private key is a secp256k1 scalar, the public key is a 32-byte x-only coordinate. Returns 32 raw bytes.
my $conv = Net::Nostr::Encryption->get_conversation_key(
$my_key->privkey_hex, $their_pubkey_hex,
);
get_message_keys
my ($chacha_key, $chacha_nonce, $hmac_key) =
Net::Nostr::Encryption->get_message_keys($conversation_key, $nonce);
Derives per-message keys from a conversation key and nonce using HKDF-expand. Both arguments are 32 raw bytes. Returns three raw byte strings: ChaCha20 key (32 bytes), ChaCha20 nonce (12 bytes), and HMAC key (32 bytes).
calc_padded_len
my $padded = Net::Nostr::Encryption->calc_padded_len($unpadded_len);
Calculates the padded length for a given plaintext length. The padding scheme uses power-of-two-based chunking with a minimum padded size of 32.
Net::Nostr::Encryption->calc_padded_len(1); # 32
Net::Nostr::Encryption->calc_padded_len(32); # 32
Net::Nostr::Encryption->calc_padded_len(33); # 64
Net::Nostr::Encryption->calc_padded_len(257); # 320
encrypt
my $payload = Net::Nostr::Encryption->encrypt($plaintext, $conversation_key);
my $payload = Net::Nostr::Encryption->encrypt($plaintext, $conversation_key, $nonce);
Encrypts a plaintext string using the NIP-44 v2 scheme. The conversation key is 32 raw bytes (from get_conversation_key). An optional 32-byte nonce can be provided for deterministic encryption (useful for testing); otherwise a cryptographically random nonce is generated.
Returns a base64-encoded payload string. The plaintext is UTF-8 encoded before encryption and UTF-8 decoded after decryption. Croaks if the plaintext is empty or exceeds 65535 bytes (after UTF-8 encoding).
my $payload = Net::Nostr::Encryption->encrypt('secret message', $conv_key);
decrypt
my $plaintext = Net::Nostr::Encryption->decrypt($payload, $conversation_key);
Decrypts a NIP-44 payload. The payload is the base64 string from encrypt. Croaks on invalid version, bad MAC, invalid padding, or malformed payload.
my $msg = Net::Nostr::Encryption->decrypt($payload, $conv_key);