6f28ea0b16
ZIP .daagbok files use a compact manifest and binary KDAB blobs so large photo, voice, and GPS payloads no longer inflate in a single JSON file. Co-authored-by: Cursor <cursoragent@cursor.com>
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
import { base64ToBuffer, bufferToBase64 } from '../crypto.js'
|
|
|
|
export const ENC_MAGIC = new Uint8Array([0x4b, 0x44, 0x41, 0x42]) // KDAB
|
|
export const ENC_FORMAT_VERSION = 1
|
|
export const ENC_HEADER_SIZE = 33 // 4 + 1 + 12 + 16
|
|
|
|
export interface DexieEncFields {
|
|
encryptedData: string
|
|
iv: string
|
|
tag: string
|
|
}
|
|
|
|
export function encBytesFromDexieFields(fields: DexieEncFields): Uint8Array {
|
|
const iv = new Uint8Array(base64ToBuffer(fields.iv))
|
|
const tag = new Uint8Array(base64ToBuffer(fields.tag))
|
|
const ciphertext = new Uint8Array(base64ToBuffer(fields.encryptedData))
|
|
if (iv.length !== 12) throw new Error('BACKUP_INVALID_ENC')
|
|
if (tag.length !== 16) throw new Error('BACKUP_INVALID_ENC')
|
|
|
|
const out = new Uint8Array(ENC_HEADER_SIZE + ciphertext.length)
|
|
out.set(ENC_MAGIC, 0)
|
|
out[4] = ENC_FORMAT_VERSION
|
|
out.set(iv, 5)
|
|
out.set(tag, 17)
|
|
out.set(ciphertext, 33)
|
|
return out
|
|
}
|
|
|
|
export function dexieFieldsFromEncBytes(bytes: Uint8Array): DexieEncFields {
|
|
if (bytes.length < ENC_HEADER_SIZE) throw new Error('BACKUP_INVALID_ENC')
|
|
for (let i = 0; i < 4; i++) {
|
|
if (bytes[i] !== ENC_MAGIC[i]) throw new Error('BACKUP_INVALID_ENC')
|
|
}
|
|
if (bytes[4] !== ENC_FORMAT_VERSION) throw new Error('BACKUP_INVALID_ENC')
|
|
|
|
const iv = bufferToBase64(bytes.slice(5, 17).buffer)
|
|
const tag = bufferToBase64(bytes.slice(17, 33).buffer)
|
|
const ciphertext = bufferToBase64(bytes.slice(33).buffer)
|
|
return { encryptedData: ciphertext, iv, tag }
|
|
}
|
|
|
|
export function encByteLength(fields: DexieEncFields): number {
|
|
const ct = base64ToBuffer(fields.encryptedData).byteLength
|
|
return ENC_HEADER_SIZE + ct
|
|
}
|