check / check (push) Successful in 16s
downloadFile, shared by quak get, the content cache and backup, hashes the decrypted bytes (unkeyed BLAKE2b-512, standard base64) and stores nothing on a mismatch, failing with an error naming the file ID. A live photo ZIP is unpacked as it streams with fflate's Unzip, in small slices so memory stays bounded however far an entry expands, and its image and video hashed separately as <imageHash>:<videoHash>. decryptFile reads the older imageHash/videoHash fields for live photos. A file with no recorded hash is stored unchecked. Model: opus-5-5
23 lines
959 B
TypeScript
23 lines
959 B
TypeScript
import sodium, { type StateAddress } from "libsodium-wrappers-sumo";
|
|
import { toBase64 } from "./encoding.js";
|
|
|
|
// The content hash an uploading client records in a file's metadata: unkeyed
|
|
// BLAKE2b with a 64-byte output over the original's bytes, fed in chunks, as
|
|
// standard base64 with padding. Named after the upstream client's functions.
|
|
// The output length is read at call time for the same reason as
|
|
// `streamTagFinal` in stream.ts: libsodium sets its constants only once ready.
|
|
|
|
export const chunkHashInit = (): StateAddress =>
|
|
sodium.crypto_generichash_init(null, sodium.crypto_generichash_BYTES_MAX);
|
|
|
|
export const chunkHashUpdate = (state: StateAddress, chunk: Uint8Array): void =>
|
|
sodium.crypto_generichash_update(state, chunk);
|
|
|
|
export const chunkHashFinal = (state: StateAddress): string =>
|
|
toBase64(
|
|
sodium.crypto_generichash_final(
|
|
state,
|
|
sodium.crypto_generichash_BYTES_MAX,
|
|
),
|
|
);
|