UNPKG

age-encryption

Version:

<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/FiloSottile/age/blob/main/logo/logo_white.svg"> <source media="(prefers-color-scheme: light)" srcset="https://github.com/FiloSottile/a

64 lines (63 loc) 2.74 kB
import { chacha20poly1305 } from "@noble/ciphers/chacha"; const chacha20poly1305Overhead = 16; const chunkSize = /* @__PURE__ */ (() => 64 * 1024)(); const chunkSizeWithOverhead = /* @__PURE__ */ (() => chunkSize + chacha20poly1305Overhead)(); export function decryptSTREAM(key, ciphertext) { const streamNonce = new Uint8Array(12); const incNonce = () => { for (let i = streamNonce.length - 2; i >= 0; i--) { streamNonce[i]++; if (streamNonce[i] !== 0) break; } }; const chunkCount = Math.ceil(ciphertext.length / chunkSizeWithOverhead); const overhead = chunkCount * chacha20poly1305Overhead; const plaintext = new Uint8Array(ciphertext.length - overhead); let plaintextSlice = plaintext; while (ciphertext.length > chunkSizeWithOverhead) { const chunk = chacha20poly1305(key, streamNonce).decrypt(ciphertext.subarray(0, chunkSizeWithOverhead)); plaintextSlice.set(chunk); plaintextSlice = plaintextSlice.subarray(chunk.length); ciphertext = ciphertext.subarray(chunkSizeWithOverhead); incNonce(); } streamNonce[11] = 1; // Last chunk flag. const chunk = chacha20poly1305(key, streamNonce).decrypt(ciphertext); plaintextSlice.set(chunk); if (chunk.length === 0 && plaintext.length !== 0) { throw Error("empty final chunk"); } if (plaintextSlice.length !== chunk.length) { throw Error("stream: internal error: didn't fill expected plaintext buffer"); } return plaintext; } export function encryptSTREAM(key, plaintext) { const streamNonce = new Uint8Array(12); const incNonce = () => { for (let i = streamNonce.length - 2; i >= 0; i--) { streamNonce[i]++; if (streamNonce[i] !== 0) break; } }; const chunkCount = plaintext.length === 0 ? 1 : Math.ceil(plaintext.length / chunkSize); const overhead = chunkCount * chacha20poly1305Overhead; const ciphertext = new Uint8Array(plaintext.length + overhead); let ciphertextSlice = ciphertext; while (plaintext.length > chunkSize) { const chunk = chacha20poly1305(key, streamNonce).encrypt(plaintext.subarray(0, chunkSize)); ciphertextSlice.set(chunk); ciphertextSlice = ciphertextSlice.subarray(chunk.length); plaintext = plaintext.subarray(chunkSize); incNonce(); } streamNonce[11] = 1; // Last chunk flag. const chunk = chacha20poly1305(key, streamNonce).encrypt(plaintext); ciphertextSlice.set(chunk); if (ciphertextSlice.length !== chunk.length) { throw Error("stream: internal error: didn't fill expected ciphertext buffer"); } return ciphertext; }