import { describe, expect, it } from 'vitest' import { entschluesselnBinaer, istVerschluesselt, KOPF_LAENGE, verschluesselnBinaer, } from '@/lib/speicher/umschlag' /** * Der Umschlag um abgelegte Dateien. * * Der Dateitreiber hat ihn von Anfang an benutzt, der Objektspeicher nicht: * Dort stand nur ServerSideEncryption: 'AES256' - Verschluesselung mit dem * Schluessel des Anbieters, der beim Ausliefern selbst entschluesselt. * Gegen eine verlorene Platte im Rechenzentrum hilft das, gegen den * Anbieter und gegen gestohlene Zugangsdaten zum Eimer nicht. * * Seit v1.11.0 liegt derselbe Umschlag um beides. Diese Tests halten die * drei Eigenschaften fest, auf die es dabei ankommt: Er geht wieder auf, * er merkt Veraenderungen, und er bindet den Inhalt an seinen Platz. */ const INHALT = Buffer.from('Bescheid des Sozialamts vom 3. Maerz, Seite 1 von 2.', 'utf8') const ORT = 'betreute/abc123/dokumente/eins.pdf' describe('Umschlag um abgelegte Dateien', () => { it('geht wieder auf', () => { const zu = verschluesselnBinaer(INHALT, ORT) expect(entschluesselnBinaer(zu, ORT).equals(INHALT)).toBe(true) }) it('sieht dem Geheimtext den Klartext nicht an', () => { const zu = verschluesselnBinaer(INHALT, ORT) expect(zu.includes(Buffer.from('Sozialamt', 'utf8'))).toBe(false) expect(istVerschluesselt(zu)).toBe(true) expect(zu.length).toBe(KOPF_LAENGE + INHALT.length) }) it('erzeugt bei gleichem Inhalt zwei verschiedene Geheimtexte', () => { // Sonst liesse sich an zwei gleich aussehenden Objekten ablesen, dass // dasselbe Dokument zweimal in der Akte liegt. const a = verschluesselnBinaer(INHALT, ORT) const b = verschluesselnBinaer(INHALT, ORT) expect(a.equals(b)).toBe(false) expect(entschluesselnBinaer(a, ORT).equals(entschluesselnBinaer(b, ORT))).toBe(true) }) it('merkt, wenn am Geheimtext gedreht wurde', () => { const zu = verschluesselnBinaer(INHALT, ORT) const letzte = zu.length - 1 zu.writeUInt8(zu.readUInt8(letzte) ^ 0x01, letzte) expect(() => entschluesselnBinaer(zu, ORT)).toThrow() }) it('merkt, wenn am Kopf gedreht wurde', () => { const zu = verschluesselnBinaer(INHALT, ORT) // Ein Bit im IV. zu.writeUInt8(zu.readUInt8(8) ^ 0x01, 8) expect(() => entschluesselnBinaer(zu, ORT)).toThrow() }) describe('Bindung an den Ablageort', () => { it('laesst sich nicht unter einem anderen Namen auspacken', () => { // Der Angriff dahinter: Wer Schreibrechte auf den Eimer hat, legt die // Bytes von Dokument A unter dem Namen von Dokument B ab. Ohne die // Bindung lieferte die Anwendung anschliessend den falschen Bescheid // aus - und zwar mit gueltiger Pruefsumme fuer den falschen Inhalt. const zu = verschluesselnBinaer(INHALT, ORT) expect(() => entschluesselnBinaer(zu, 'betreute/abc123/dokumente/zwei.pdf'), ).toThrow() }) it('laesst sich nicht in eine fremde Akte verschieben', () => { const zu = verschluesselnBinaer(INHALT, ORT) expect(() => entschluesselnBinaer(zu, 'betreute/xyz789/dokumente/eins.pdf')).toThrow() }) }) describe('Rueckwaertsvertraeglichkeit', () => { it('reicht Inhalte ohne Umschlag unveraendert durch', () => { // Genau der Zustand, in dem die Objekte im Speicher liegen, bis der // Migrationslauf sie umgeschrieben hat. Wuerde hier geworfen, waere // nach dem Update kein einziges altes Dokument mehr abrufbar. const roh = Buffer.from('%PDF-1.4 ganz gewoehnlich', 'utf8') expect(istVerschluesselt(roh)).toBe(false) expect(entschluesselnBinaer(roh, ORT).equals(roh)).toBe(true) }) it('haelt auch etwas fuer unverpackt, das zufaellig kurz ist', () => { const winzig = Buffer.from('abc', 'utf8') expect(istVerschluesselt(winzig)).toBe(false) expect(entschluesselnBinaer(winzig, ORT).equals(winzig)).toBe(true) }) it('oeffnet einen Umschlag, der noch ohne Ortsbindung geschrieben wurde', () => { // So hat der Dateitreiber bis v1.10.2 geschrieben. Diese Dateien // muessen weiter aufgehen, sonst waere die Verbesserung ein // Datenverlust. const alt = verschluesselnBinaer(INHALT) expect(entschluesselnBinaer(alt, ORT).equals(INHALT)).toBe(true) expect(entschluesselnBinaer(alt).equals(INHALT)).toBe(true) }) }) it('kommt mit einer leeren Datei zurecht', () => { const leer = Buffer.alloc(0) const zu = verschluesselnBinaer(leer, ORT) expect(entschluesselnBinaer(zu, ORT).length).toBe(0) }) it('kommt mit einer groesseren Datei zurecht', () => { const gross = Buffer.alloc(3 * 1024 * 1024, 0x41) const zu = verschluesselnBinaer(gross, ORT) expect(entschluesselnBinaer(zu, ORT).equals(gross)).toBe(true) }) })