UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

260 lines (259 loc) • 9.78 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var aes_exports = {}; __export(aes_exports, { assertTagLength: () => assertTagLength, createAesApi: () => createAesApi, toBytes: () => toBytes }); module.exports = __toCommonJS(aes_exports); var import_bytes = require("./bytes.cjs"); const KEY_SIZES = [ 128, 192, 256 ]; const TAG_LENGTHS = [ 16, 15, 14, 13, 12, 8, 4 ]; const DEFAULT_IV_LENGTH = 12; const DEFAULT_TAG_LENGTH = 16; function assertTagLength(tagLength, verb) { if (!TAG_LENGTHS.includes(tagLength)) { throw new Error(`${verb}: tagLength must be one of: ${TAG_LENGTHS.join(", ")}`); } } function toBytes(input, verb) { if (typeof input === "string") return (0, import_bytes.base64ToBytes)(input, verb); if (input instanceof ArrayBuffer) return new Uint8Array(input.slice(0)); if (ArrayBuffer.isView(input) && input.buffer instanceof ArrayBuffer) { return (0, import_bytes.exactBytes)(new Uint8Array(input.buffer, input.byteOffset, input.byteLength)); } throw new TypeError(`${verb}: expected a base64 string, Uint8Array, or ArrayBuffer`); } const sealedParts = /* @__PURE__ */ new WeakMap(); const keyBytes = /* @__PURE__ */ new WeakMap(); function encodeParts(bytes, encoding) { return encoding === "base64" ? (0, import_bytes.bytesToBase64)(bytes) : (0, import_bytes.exactBytes)(bytes); } function createAesApi(engine) { class AESEncryptionKey { size; constructor(bytes) { if (!KEY_SIZES.includes(bytes.length * 8)) { throw new Error(`Crypto.AESEncryptionKey: key must be 128, 192, or 256 bits, got ${bytes.length * 8}`); } this.size = bytes.length * 8; keyBytes.set(this, (0, import_bytes.exactBytes)(bytes)); } static async generate(size = 256) { if (!KEY_SIZES.includes(size)) { throw new Error(`Crypto.AESEncryptionKey.generate: size must be one of: ${KEY_SIZES.join(", ")}`); } const bytes = new Uint8Array(size / 8); engine.random(bytes); return new AESEncryptionKey(bytes); } static async import(input, encoding) { const verb = "Crypto.AESEncryptionKey.import"; let bytes; if (typeof input === "string") { if (encoding === "hex") bytes = (0, import_bytes.hexToBytes)(input, verb); else if (encoding === "base64") bytes = (0, import_bytes.base64ToBytes)(input, verb); else throw new Error(`${verb}: encoding must be 'hex' or 'base64'`); } else if (input instanceof Uint8Array) { bytes = (0, import_bytes.exactBytes)(input); } else { throw new TypeError(`${verb}: expected bytes or an encoded string`); } if (!KEY_SIZES.includes(bytes.length * 8)) { throw new Error(`${verb}: key must be 128, 192, or 256 bits, got ${bytes.length * 8}`); } return new AESEncryptionKey(bytes); } async bytes() { return (0, import_bytes.exactBytes)(keyBytes.get(this)); } async encoded(encoding) { const bytes = keyBytes.get(this); if (encoding === "hex") return (0, import_bytes.bytesToHex)(bytes); if (encoding === "base64") return (0, import_bytes.bytesToBase64)(bytes); throw new Error(`Crypto.AESEncryptionKey.encoded: encoding must be 'hex' or 'base64'`); } } class AESSealedData { constructor(parts) { if (parts.iv.length === 0) throw new Error("Crypto.AESSealedData: iv must not be empty"); assertTagLength(parts.tag.length, "Crypto.AESSealedData"); sealedParts.set(this, parts); } static fromCombined(combined, config = {}) { const verb = "Crypto.AESSealedData.fromCombined"; const ivLength = config.ivLength ?? DEFAULT_IV_LENGTH; const tagLength = config.tagLength ?? DEFAULT_TAG_LENGTH; assertTagLength(tagLength, verb); if (!Number.isInteger(ivLength) || ivLength <= 0) { throw new Error(`${verb}: ivLength must be a positive integer`); } const bytes = toBytes(combined, verb); if (bytes.length < ivLength + tagLength) { throw new Error(`${verb}: combined data is shorter than its iv and tag`); } return new AESSealedData({ iv: (0, import_bytes.exactBytes)(bytes.subarray(0, ivLength)), ciphertext: (0, import_bytes.exactBytes)(bytes.subarray(ivLength, bytes.length - tagLength)), tag: (0, import_bytes.exactBytes)(bytes.subarray(bytes.length - tagLength)) }); } static fromParts(ivInput, ciphertextInput, tagOrLength) { const verb = "Crypto.AESSealedData.fromParts"; const iv = toBytes(ivInput, verb); const ciphertext = toBytes(ciphertextInput, verb); if (tagOrLength === void 0 || typeof tagOrLength === "number") { const tagLength = tagOrLength ?? DEFAULT_TAG_LENGTH; assertTagLength(tagLength, verb); if (ciphertext.length < tagLength) { throw new Error(`${verb}: ciphertext is shorter than its tag`); } return new AESSealedData({ iv, ciphertext: (0, import_bytes.exactBytes)(ciphertext.subarray(0, ciphertext.length - tagLength)), tag: (0, import_bytes.exactBytes)(ciphertext.subarray(ciphertext.length - tagLength)) }); } const tag = toBytes(tagOrLength, verb); assertTagLength(tag.length, verb); return new AESSealedData({ iv, ciphertext, tag }); } static fromSeal(iv, ciphertextWithTag, tagLength) { return new AESSealedData({ iv, ciphertext: (0, import_bytes.exactBytes)(ciphertextWithTag.subarray(0, ciphertextWithTag.length - tagLength)), tag: (0, import_bytes.exactBytes)(ciphertextWithTag.subarray(ciphertextWithTag.length - tagLength)) }); } get ivSize() { return sealedParts.get(this).iv.length; } get tagSize() { return sealedParts.get(this).tag.length; } get combinedSize() { const parts = sealedParts.get(this); return parts.iv.length + parts.ciphertext.length + parts.tag.length; } async iv(encoding) { return encodeParts(sealedParts.get(this).iv, encoding); } async tag(encoding) { return encodeParts(sealedParts.get(this).tag, encoding); } async ciphertext(options) { const parts = sealedParts.get(this); const bytes = options?.includeTag ? (0, import_bytes.concatBytes)([parts.ciphertext, parts.tag]) : parts.ciphertext; return encodeParts(bytes, options?.encoding); } async combined(encoding) { const parts = sealedParts.get(this); return encodeParts((0, import_bytes.concatBytes)([ parts.iv, parts.ciphertext, parts.tag ]), encoding); } } async function keyMaterial(key, verb) { const own = keyBytes.get(key); if (own) return (0, import_bytes.exactBytes)(own); if (key && typeof key.bytes === "function") return (0, import_bytes.exactBytes)(await key.bytes()); throw new TypeError(`${verb}: expected an AESEncryptionKey`); } async function partsOf(sealed, verb) { const own = sealedParts.get(sealed); if (own) { return { iv: (0, import_bytes.exactBytes)(own.iv), ciphertext: (0, import_bytes.exactBytes)(own.ciphertext), tag: (0, import_bytes.exactBytes)(own.tag) }; } if (sealed && typeof sealed.iv === "function" && typeof sealed.combined === "function") { const iv = (0, import_bytes.exactBytes)(await sealed.iv()); const tag = (0, import_bytes.exactBytes)(await sealed.tag()); const ciphertext = (0, import_bytes.exactBytes)(await sealed.ciphertext({ encoding: "bytes", includeTag: false })); return { iv, ciphertext, tag }; } throw new TypeError(`${verb}: expected an AESSealedData`); } async function aesEncrypt(plaintext, key, options = {}) { const verb = "Crypto.aesEncrypt"; const tagLength = options.tagLength ?? DEFAULT_TAG_LENGTH; assertTagLength(tagLength, verb); let iv; const nonce = options.nonce; if (nonce && "bytes" in nonce && nonce.bytes !== void 0) { iv = toBytes(nonce.bytes, verb); if (iv.length === 0) throw new Error(`${verb}: nonce bytes must not be empty`); } else { const length = nonce && "length" in nonce && nonce.length !== void 0 ? nonce.length : DEFAULT_IV_LENGTH; if (!Number.isInteger(length) || length <= 0) { throw new Error(`${verb}: nonce length must be a positive integer`); } iv = new Uint8Array(length); engine.random(iv); } const additionalData = options.additionalData === void 0 ? void 0 : toBytes(options.additionalData, verb); const plainBytes = toBytes(plaintext, verb); const sealed = await engine.seal(await keyMaterial(key, verb), iv, plainBytes, additionalData, tagLength); const actualTag = sealed.length - plainBytes.length; return AESSealedData.fromSeal(iv, sealed, actualTag > 0 ? actualTag : tagLength); } async function aesDecrypt(sealedData, key, options = {}) { const verb = "Crypto.aesDecrypt"; const parts = await partsOf(sealedData, verb); const additionalData = options.additionalData === void 0 ? void 0 : toBytes(options.additionalData, verb); const plaintext = await engine.open(await keyMaterial(key, verb), parts.iv, (0, import_bytes.concatBytes)([parts.ciphertext, parts.tag]), additionalData, parts.tag.length); return options.output === "base64" ? (0, import_bytes.bytesToBase64)(plaintext) : plaintext; } return { AESEncryptionKey, AESSealedData, aesEncrypt, aesDecrypt }; }