const ZW_VERSION = 1 const KEY_SEED = 'JGJS2026::ZW::ARCHIVE::V1::DO_NOT_TAMPER' const MAGIC_BYTES = new Uint8Array([0x4a, 0x47, 0x4a, 0x53, 0x5a, 0x57]) // JGJSZW export const ZW_FILE_EXTENSION = '.zw' const encoder = new TextEncoder() const decoder = new TextDecoder() let cachedKeyPromise: Promise | null = null const toArrayBuffer = (bytes: Uint8Array): ArrayBuffer => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer const getArchiveKey = async (): Promise => { if (!cachedKeyPromise) { cachedKeyPromise = (async () => { const hash = await crypto.subtle.digest('SHA-256', encoder.encode(KEY_SEED)) return crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']) })() } return cachedKeyPromise } export const encodeZwArchive = async (payload: unknown): Promise => { const key = await getArchiveKey() const iv = crypto.getRandomValues(new Uint8Array(12)) const ivBuffer = toArrayBuffer(iv) const plainText = encoder.encode(JSON.stringify(payload)) const cipherBuffer = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: ivBuffer }, key, toArrayBuffer(plainText) ) const cipher = new Uint8Array(cipherBuffer) const result = new Uint8Array(MAGIC_BYTES.length + 1 + 1 + iv.length + cipher.length) let offset = 0 result.set(MAGIC_BYTES, offset) offset += MAGIC_BYTES.length result[offset] = ZW_VERSION offset += 1 result[offset] = iv.length offset += 1 result.set(iv, offset) offset += iv.length result.set(cipher, offset) return result } export const decodeZwArchive = async (raw: ArrayBuffer | Uint8Array): Promise => { const bytes = raw instanceof Uint8Array ? raw : new Uint8Array(raw) const minLength = MAGIC_BYTES.length + 1 + 1 + 1 + 16 if (bytes.length < minLength) { throw new Error('INVALID_ZW_PAYLOAD') } let offset = 0 for (let i = 0; i < MAGIC_BYTES.length; i += 1) { if (bytes[offset + i] !== MAGIC_BYTES[i]) { throw new Error('INVALID_ZW_HEADER') } } offset += MAGIC_BYTES.length const version = bytes[offset] offset += 1 if (version !== ZW_VERSION) { throw new Error('INVALID_ZW_VERSION') } const ivLength = bytes[offset] offset += 1 if (ivLength <= 0 || bytes.length <= offset + ivLength) { throw new Error('INVALID_ZW_PAYLOAD') } const iv = bytes.slice(offset, offset + ivLength) offset += ivLength const cipher = bytes.slice(offset) if (cipher.length < 16) { throw new Error('INVALID_ZW_PAYLOAD') } const key = await getArchiveKey() const ivBuffer = toArrayBuffer(iv) const cipherBuffer = toArrayBuffer(cipher) let plainBuffer: ArrayBuffer try { plainBuffer = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: ivBuffer }, key, cipherBuffer) } catch { throw new Error('INVALID_ZW_TAMPERED') } try { return JSON.parse(decoder.decode(plainBuffer)) as T } catch { throw new Error('INVALID_ZW_CONTENT') } }