Check downloaded originals against their recorded content hash (closes #68)
check / check (push) Successful in 2m40s
check / check (push) Successful in 2m40s
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 with fflate 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,33 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user