| 95 | * Output format: base64(0x02 || nonce[32] || ciphertext || mac[32]) |
| 96 | */ |
| 97 | export function nip44Encrypt(plaintext: string, conversationKey: Buffer, nonce: Buffer = randomBytes(32)): string { |
| 98 | const { chachaKey, chachaNonce, hmacKey } = getMessageKeys(conversationKey, nonce) |
| 99 | const padded = pad(plaintext) |
| 100 | |
| 101 | // ChaCha20: OpenSSL expects a 16-byte IV = [counter_le32=0][nonce_96bit] |
| 102 | const iv = Buffer.concat([Buffer.alloc(4), chachaNonce]) |
| 103 | const cipher = createCipheriv('chacha20', chachaKey, iv) |
| 104 | const ciphertext = Buffer.concat([cipher.update(padded), cipher.final()]) |
| 105 | |
| 106 | // MAC = HMAC-SHA256(hmacKey, nonce || ciphertext) |
| 107 | const mac = createHmac('sha256', hmacKey).update(nonce).update(ciphertext).digest() |
| 108 | |
| 109 | return Buffer.concat([Buffer.from([0x02]), nonce, ciphertext, mac]).toString('base64') |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Decrypt a NIP-44 v2 payload. |