Check downloaded originals against their recorded content hash (closes #68)
check / check (push) Successful in 16s
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
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
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,
|
||||
),
|
||||
);
|
||||
Reference in New Issue
Block a user