UNPKG

@akala/core

Version:
276 lines (242 loc) 8.44 kB
/// /// Native/portable equivalents with the same public surface /// import { IsomorphicBuffer } from "./helpers.js"; // --- Base64 utils --- /** * Calculates the decoded byte length of a Base64 string. * @param sBase64 The Base64 encoded string. * @param nBlocksSize Optional size to round up to (for buffer alignment). * @returns The decoded byte length. */ export function base64ByteLength(sBase64: string, nBlocksSize: number = 0): number { // Keep original semantics: strip everything except Base64 alphabet (no '=') const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ""); const nInLen = sB64Enc.length; // Original MDN math: floor((nInLen * 3 + 1) / 4) const bytes = (nInLen * 3 + 1) >> 2; return nBlocksSize ? Math.ceil(bytes / nBlocksSize) * nBlocksSize : bytes; } const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * Decodes a Base64 string into a Uint8Array. * @param sBase64 The Base64 encoded string. * @returns The decoded byte array. */ export function base64DecToArr(sBase64: string) { const clean = sBase64.replace(/[^A-Za-z0-9+/]/g, ""); if (typeof Buffer !== "undefined") return Buffer.from(clean, "base64"); else { const length = Math.floor(clean.length * 3 / 4); const out = new Uint8Array(length); let j = 0; for (let i = 0; i < clean.length; i += 4) { const n0 = BASE64_CHARS.indexOf(clean[i]); const n1 = BASE64_CHARS.indexOf(clean[i + 1]); const n2 = BASE64_CHARS.indexOf(clean[i + 2]); const n3 = BASE64_CHARS.indexOf(clean[i + 3]); const triplet = (n0 << 18) | (n1 << 12) | ((n2 & 63) << 6) | (n3 & 63); if (j < out.length) out[j++] = (triplet >> 16) & 0xFF; if (j < out.length) out[j++] = (triplet >> 8) & 0xFF; if (j < out.length) out[j++] = triplet & 0xFF; } return out; } } /** * Encodes an Uint8Array to Base64URL format. * @param aBytes The byte array to encode. * @returns The Base64URL encoded string. */ export function base64UrlEncArr(aBytes: Uint8Array): string { const s = base64EncArr(aBytes).replace(/\+/g, "-").replace(/\//g, "_"); return s.replace(/=+$/g, ""); } /** * Encodes an ArrayBuffer to Base64URL format. * @param aBytes The ArrayBuffer to encode. * @returns The Base64URL encoded string. */ export function base64UrlEncArrBuff(aBytes: ArrayBuffer): string { const s = base64EncArrBuff(aBytes).replace(/\+/g, "-").replace(/\//g, "_"); return s.replace(/=+$/g, ""); } /** * Encodes an ArrayBuffer/Uint8Array to Base64URL format. * @param aBytes The byte array to encode. * @returns The Base64URL encoded string. */ export function base64UrlEncIsomorphicBuffer(aBytes: IsomorphicBuffer): string { let s = base64EncIsomorphicBuffer(aBytes).replace(/\+/g, '-').replace(/\//g, '_'); while (s.endsWith('=')) s = s.substring(0, s.length - 1); return s; } /** * Decodes a Base64URL string into an ArrayBuffer/Uint8Array. * @param s The Base64URL encoded string. * @returns The decoded byte array. */ export function base64UrlDecToArr(s: string) { let t = s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s+/g, ""); const rem = t.length % 4; if (rem === 2) t += "=="; else if (rem === 3) t += "="; else if (rem === 1) throw new Error("Invalid Base64URL length"); return base64DecToArr(t); } // --- PEM helpers (same names/signatures) --- /** * Extracts the binary content from a PEM-formatted private key. * @param pem The PEM string containing the private key. * @returns The decoded binary content of the private key. */ export function extractPrivateKey(pem: string) { const pemHeader = "-----BEGIN PRIVATE KEY-----"; const pemFooter = "-----END PRIVATE KEY-----"; const start = pem.indexOf(pemHeader); const end = pem.indexOf(pemFooter); if (start < 0 || end < 0 || end <= start) throw new Error("Invalid PRIVATE KEY PEM"); const body = pem.slice(start + pemHeader.length, end).replace(/\s+/g, ""); return base64DecToArr(body); } /** * Extracts the binary content from a PEM-formatted public key. * @param pem The PEM string containing the public key. * @returns The decoded binary content of the public key. */ export function extractPublicKey(pem: string) { const pemHeader = "-----BEGIN PUBLIC KEY-----"; const pemFooter = "-----END PUBLIC KEY-----"; const start = pem.indexOf(pemHeader); const end = pem.indexOf(pemFooter); if (start < 0 || end < 0 || end <= start) throw new Error("Invalid PUBLIC KEY PEM"); const body = pem.slice(start + pemHeader.length, end).replace(/\s+/g, ""); return base64DecToArr(body); } /** * Encodes an ArrayBuffer/Uint8Array to Base64 string. * @param aBytes The byte array to encode. * @param nBlocksSize Optional size for line breaks in the output. * @returns The Base64 encoded string. */ export function base64EncArr(aBytes: Uint8Array, nBlocksSize?: number): string { let out: string; if (typeof Buffer !== "undefined") // Node, Bun out = Buffer.from(aBytes).toString("base64"); else { out = ""; let i = 0; while (i < aBytes.length) { const start = i; const byte1 = aBytes[i++]!; const byte2 = i < aBytes.length ? aBytes[i++]! : 0; const byte3 = i < aBytes.length ? aBytes[i++]! : 0; const triplet = (byte1 << 16) | (byte2 << 8) | byte3; const bytesRead = i - start; out += BASE64_CHARS[(triplet >> 18) & 63]; out += BASE64_CHARS[(triplet >> 12) & 63]; out += bytesRead > 1 ? BASE64_CHARS[(triplet >> 6) & 63] : "="; out += bytesRead > 2 ? BASE64_CHARS[triplet & 63] : "="; } } if (nBlocksSize && nBlocksSize > 0) { let s = ""; for (let j = 0; j < out.length; j += nBlocksSize) { s += out.slice(j, j + nBlocksSize) + "\n"; } out = s.trimEnd(); } return out; } /** * Encodes an ArrayBuffer/Uint8Array to Base64 string. * @param aBytes The byte array to encode. * @param nBlocksSize Optional size for line breaks in the output. * @returns The Base64 encoded string. */ export function base64EncArrBuff(aBytes: ArrayBuffer, nBlocksSize?: number): string { return base64EncArr(new Uint8Array(aBytes), nBlocksSize); } /** * Encodes an ArrayBuffer/Uint8Array to Base64 string. * @param aBytes The byte array to encode. * @param nBlocksSize Optional size for line breaks in the output. * @returns The Base64 encoded string. */ export function base64EncIsomorphicBuffer(aBytes: IsomorphicBuffer, nBlocksSize?: number): string { return base64EncArr(aBytes.toArray(), nBlocksSize); } // --- UTF-8 (native encoders/decoders) --- /* UTF-8 array to JS string and vice versa */ /** * Converts a UTF-8 encoded byte array to a JavaScript string. * @param aBytes The UTF-8 encoded byte array. * @returns The decoded string. */ export function UTF8ArrToStr(aBytes: Uint8Array): string { return new TextDecoder("utf-8").decode(aBytes); } /** * IsomorphicBuffer UTF-8 -> string (same name/signature). */ /* UTF-8 array to JS string and vice versa */ /** * Converts a UTF-8 encoded byte array to a JavaScript string. * @param aBytes The UTF-8 encoded byte array. * @returns The decoded string. */ export function UTF8IsomorphicBufferToStr(aBytes: IsomorphicBuffer): string { return UTF8ArrToStr(aBytes.toArray()); } /** * Compute UTF-8 byte length for a JS string (same name/signature). * Uses correct UTF-8 ranges (1–4 bytes). */ export function strUTF8ByteLength(sDOMStr: string): number { let bytes = 0; for (let i = 0; i < sDOMStr.length; i++) { const cp = sDOMStr.codePointAt(i)!; if (cp <= 0x7F) bytes += 1; else if (cp <= 0x7FF) bytes += 2; else if (cp <= 0xFFFF) bytes += 3; else { bytes += 4; i++; } // surrogate pair consumed } return bytes; } /** * Converts a JavaScript string to a UTF-8 encoded ArrayBuffer. * @param sDOMStr The input string to encode. * @returns The UTF-8 encoded ArrayBuffer. */ export function strToUTF8Arr(sDOMStr: string) { return new TextEncoder().encode(sDOMStr) as Uint8Array<ArrayBuffer>; }