NAME

Net::Nostr::KeyEncrypt - NIP-49 private key encryption

SYNOPSIS

use Net::Nostr::KeyEncrypt qw(encrypt_private_key decrypt_private_key);

# Encrypt a private key with a password
my $ncryptsec = encrypt_private_key(
    privkey_hex => 'aa' x 32,
    password    => 'my-strong-password',
    log_n       => 16,
);
# ncryptsec1...

# Decrypt an encrypted private key
my $privkey_hex = decrypt_private_key($ncryptsec, 'my-strong-password');
# 'aa' x 32

# Specify key security level
my $ncryptsec = encrypt_private_key(
    privkey_hex  => $privkey_hex,
    password     => $password,
    log_n        => 20,
    key_security => 0x01,  # not known to have been handled insecurely
);

DESCRIPTION

Implements NIP-49 private key encryption. Encrypts a user's private key with a password using scrypt key derivation and XChaCha20-Poly1305 AEAD encryption. The output is a bech32-encoded ncryptsec string that can be stored or transferred safely.

The password is Unicode-normalized to NFKC form before use, ensuring that the same password entered on different systems produces the same encryption key.

FUNCTIONS

All functions are exportable. None are exported by default.

encrypt_private_key

my $ncryptsec = encrypt_private_key(
    privkey_hex  => $hex_privkey,
    password     => $password,
    log_n        => $log_n,
    key_security => $byte,   # optional, defaults to 0x02
);

Encrypts a private key with a password. Returns a bech32-encoded ncryptsec string. Croaks if any argument is missing, out of range, or malformed.

Arguments:

privkey_hex - 64-char lowercase hex private key (required)
password - encryption password (required, non-empty)
log_n - scrypt cost parameter as a power of 2 (required, 1-22). Higher values use more memory and time but are more resistant to brute force. Recommended: 16 (64 MiB, ~100ms) for interactive use, 20+ for long-term storage.
key_security - one of 0x00 (key known to have been handled insecurely), 0x01 (key not known insecure), or 0x02 (unknown). Defaults to 0x02. This byte is included as associated data in the AEAD encryption and stored in the payload.
my $ncryptsec = encrypt_private_key(
    privkey_hex => 'aa' x 32,
    password    => 'my-strong-password',
    log_n       => 16,
);

decrypt_private_key

my $hex = decrypt_private_key($ncryptsec, $password);
my $hex = decrypt_private_key($ncryptsec, $password, log_n => $n);

Decrypts an ncryptsec string with a password. Returns the private key as a 64-char lowercase hex string. Validates the bech32 encoding, ncryptsec prefix, version byte (must be 0x02), and payload size (must be 91 bytes). Croaks on wrong password, corrupted data, or invalid format.

The log_n parameter is optional. If omitted, the value embedded in the ncryptsec payload is used. Providing log_n explicitly overrides the embedded value, which is useful when you know the cost parameter in advance.

my $hex = decrypt_private_key(
    'ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p',
    'nostr',
);
# '3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683'

SEE ALSO

NIP-49, Net::Nostr, Net::Nostr::Key