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

55 lines (54 loc) 1.92 kB
import { base64 } from "@scure/base"; /** * Encode an age encrypted file using the ASCII armor format, a strict subset of * PEM that starts with `-----BEGIN AGE ENCRYPTED FILE-----`. * * @param file - The raw encrypted file (returned by {@link Encrypter.encrypt}). * * @returns The ASCII armored file, with a final newline. */ export function encode(file) { const lines = []; lines.push("-----BEGIN AGE ENCRYPTED FILE-----\n"); for (let i = 0; i < file.length; i += 48) { let end = i + 48; if (end > file.length) end = file.length; lines.push(base64.encode(file.subarray(i, end)) + "\n"); } lines.push("-----END AGE ENCRYPTED FILE-----\n"); return lines.join(""); } /** * Decode an age encrypted file from the ASCII armor format, a strict subset of * PEM that starts with `-----BEGIN AGE ENCRYPTED FILE-----`. * * Extra whitespace before and after the file is ignored, and newlines can be * CRLF or LF, but otherwise the format is parsed strictly. * * @param file - The ASCII armored file. * * @returns The raw encrypted file (to be passed to {@link Decrypter.decrypt}). */ export function decode(file) { const lines = file.trim().replaceAll("\r\n", "\n").split("\n"); if (lines.shift() !== "-----BEGIN AGE ENCRYPTED FILE-----") { throw Error("invalid header"); } if (lines.pop() !== "-----END AGE ENCRYPTED FILE-----") { throw Error("invalid footer"); } function isLineLengthValid(i, l) { if (i === lines.length - 1) { return l.length > 0 && l.length <= 64 && l.length % 4 === 0; } return l.length === 64; } if (!lines.every((l, i) => isLineLengthValid(i, l))) { throw Error("invalid line length"); } if (!lines.every((l) => /^[A-Za-z0-9+/=]+$/.test(l))) { throw Error("invalid base64"); } return base64.decode(lines.join("")); }