check / check (push) Successful in 35s
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
34 lines
1.0 KiB
TypeScript
34 lines
1.0 KiB
TypeScript
import { beforeAll, describe, expect, it } from "vitest";
|
|
import {
|
|
chunkHashFinal,
|
|
chunkHashInit,
|
|
chunkHashUpdate,
|
|
init,
|
|
} from "../../src/crypto/index.js";
|
|
|
|
beforeAll(async () => {
|
|
await init();
|
|
});
|
|
|
|
describe("content hash", () => {
|
|
// RFC 7693 Appendix A: BLAKE2b-512 of "abc".
|
|
const abc = Buffer.from(
|
|
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1" +
|
|
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
|
|
"hex",
|
|
).toString("base64");
|
|
|
|
it("is unkeyed BLAKE2b-512 in standard base64", () => {
|
|
const state = chunkHashInit();
|
|
chunkHashUpdate(state, new TextEncoder().encode("abc"));
|
|
expect(chunkHashFinal(state)).toBe(abc);
|
|
});
|
|
|
|
it("gives the same hash when the input arrives in chunks", () => {
|
|
const state = chunkHashInit();
|
|
chunkHashUpdate(state, new TextEncoder().encode("a"));
|
|
chunkHashUpdate(state, new TextEncoder().encode("bc"));
|
|
expect(chunkHashFinal(state)).toBe(abc);
|
|
});
|
|
});
|