Dateiformat der Archive
Dieses Kapitel beschreibt das interne Speicherformat der Vanderplanki-Archive.
Eine Beispielimplementierung, die Vanderplanki-Archive praktisch ohne Vanderplanki lesen kann, ist der Vanderplanki Reader.
Grundsätzlicher Aufbau
Item Lists, Thumbnails und Dateiinhalte
Auf der obersten Ebene arbeitet Vanderplanki mit Item Lists. Diese werden gemeinsam mit Thumbnails im CAS für Metaobjekte gespeichert. Dateinhalte werden in einem gesonderten CAS für Objekte gespeichert.
Item Lists sind JSON-codierte Objekte, die ein Array mit Items beinhalten. Items können dabei zum Beispiel Ordner oder Dateien sein. Der genaue Aufbau von Item Lists und Items wird weiter unten beschrieben.
Content Addressed Storage (CAS)
Ein CAS speichert beliebig viele Objekte anhand der SHA-256-Hashwerte ihrer Inhalte. Jedes Vanderplanki-Archiv hat sowohl ein CAS für Objekte als auch eines für Metaobjekte. Objekte innerhalb eines CAS können eine beliebige Größe haben.
Chunks
Die Objekte, die in einem der beiden CAS gespeichert sind, werden nicht unmittelbar in den Dateisystemen der Archivspeicher abgelegt, sondern in Form von Chunks. Kleine Objekte werden in einem einzelnen Chunk gespeichert. Große Objekte werden in mehrere Chunks aufgeteilt und es wird ein zusätzlicher Chunk angelegt, welcher eine Liste der einzelnen Fragment-Chunks beinhaltet. Dies wird detaillierter unter Content Addressable Storage beschrieben.
Chunks werden von Vanderplanki ggf. komprimiert, um Speicherplatz zu sparen.
Chunk Storage
Auch Chunks werden nicht unmittelbar in den Dateisystemen der Archivspeicher abgelegt, sondern zu Chunk Packs zusammengefasst. Die maximale Größe der Chunk Packs hängt vom Typ des CAS ab.
Damit Vanderplanki weiß, welcher Chunk sich an welcher Stelle welches Chunk Packs befindet, gibt es eine Chunk Map, welche aus einer oder mehreren Chunk Map Segments besteht.
Chunk Packs und Chunk Map Segments bilden zusammen den Chunk Storage. Dieser wird detaillierter unten auf dieser Seite beschrieben.
Archive Version
Eine Archive Version ist sozusagen der Einstiegspunkt. Die Archive Version beinhaltet unter anderem für das Objekte-CAS als auch für das Metaobjekte-CAS jeweils eine Liste aller zugehörigen Chunk Packs und Chunk Map Segments, sowie der SHA-256-Hash der Stamm-Item List der jeweiligen Archivversion. Der Aufbau der Archive Version ist weiter unten beschrieben.
Blob Files
Sowohl die Archive Version als auch die zugehörigen Chunk Packs und Chunk Map Segments werden als Blob-Dateien in den Archivspeichern gespeichert. Diese tragen als Namen den SHA-256-Hashwert ihrer Inhalte. Dies erleichtert eine Prüfung auf mögliche Fehler erheblich.
Archive Store
In einem Archivspeicher werden abgelegt:
- Die Blob-Dateien der einzelnen Archive, die auf dem Archivspeicher gespeichert sind
- Verschlüsselte Datenbanksicherungen
- Eine Datei store.txt, die im Wesentlichen die UUID des Archivspeichers beinhaltet. Dies erleichtert die Identifikation des Archivspeichers.
Verzeichnisbaum eines Archive Stores
Directoryblobs
Directoryef094cbe-6169-4acd-ad35-c171dcc343d3
Directory2b
- 2b958309de842686b53db4fdbe5bd388ac426e23589defcf18172e31c9d57b98
Directorya2
- a228effb2c209f473e1ff245c90a30cf6d8e29617c37734ae484e7d900f46e71
- a237b2a474fa47f687d86ed377e5c4d278dd5a973425a39a4b15bf476e9193cd
- a29f31a2333af5bad9dcbab03b22b6bb13eb3d2bfcc91cfbeffa1ce8b6c83127
Directoryc3
- c34d8ff3e5361afa32a8763e4c57c6749f6635e2400dca289f7c37d34ac0724b
- c3a326e7826df18994e3cb44b8838b526bb3a0cee42d5508f11296018531caa2
Directorydb-backups
Directory2024
Directory2024-03
- encrypted-db-backup-2024-03-10-085256101.txt
- encrypted-db-backup-2024-03-10-090412106.txt
- store.txt
Schlüssel ermitteln, die zur Entschlüsselung der Archivversion und der CAS-Inhalte benötigt werden
Datenbank oder Datenbanksicherung lesen
Sowohl Datenbanken als auch Datenbanksicherungen sind JavaScript-Dateien, welche eine Sektion beinhalten, die die Datenbank im JSON-Format enthalten. Die eigentlichen JSON-Daten sind umschlossen von Zeilen mit dem folgenden Inhalt:
// ===== BEGIN JSON =====
und
// ===== END JSON =====
Die eigentlichen JSON-Daten sind umschlossen von einem JSON-Umschlag (“JSON Envelope”). Dessen Nutzdaten sind entweder im Klartext verfügbar (body
) oder verschlüsselt. Im letzteren Fall befinden sich die verschlüsselten Nutzdaten BASE64-codiert in der Eigenschaft encryptedBody
.
Sind die Nutzdaten verschlüsselt, ist der zur Entschlüsselung zu verwendende Schlüssel zunächst über die Schlüsselableitungsfunktion Scrypt abzuleiten. Die hierfür erforderlichen Parameter salt
, keyLength
, cost
, blockSize
und parallelization
sind genauso wie der verschlüsselte Schlüssel encryptionKey
in keyProtectors
-> password
bzw. recoveryKey
zu finden.
Innerhalb der Nutzdaten befindet sich eine Eigenschaft archives
, welche ein JSON-Array beinhaltet, das in der Datenbank verzeichneten Archive auflistet. Die wichtigen Eigenschaften sind hier archiveId
, welche die ID des Archivs und damit auch den Namen des Unterverzeichnisses der Archivspeicher angibt, der Name des Archivs name
sowie archiveVersionKey
und casKey
. Letztere beinhalten die Schlüssel, die zur Entschlüsselung der Archivspeicher erforderlich sind.
Im Falle von archiveVersionKey
ist dies immer ein hexadezimal codierter String. Der casKey
ist ein JSON-Umschlag wie oben beschrieben. Hier ist der Schlüssel verschlüsselt, wenn das Archiv mit einem zusätzlichen Kennwort geschützt wurde. (Der Schutz von Archiven mit einem zusätzlichen Kennwort ist ein Feature, das in der aktuellen Version von Vanderplanki noch nicht generell verfügbar ist.)
Die gewünschte Archivversion kann über das Array versions
ermittelt werden (archiveVersionHash
). Die Archivversion mit dem höchsten Zeitstempel (created
) ist die aktuelle. Beinhaltet der zu entschlüsselnde Archivspeicher nicht die aktuelle Archivversion, kann diese über das Array replicas
ermittelt werden.
Datentypen und Datenstrukturen (Englisch)
Data Types
Binary Data Types
bool
0 - false (byte) 1 - true (byte)
byte
One byte.
int32
Little endian-encoded signed 32-bit integer (4 bytes).
int64
Little endian-encoded signed 64-bit integer (8 bytes).
UUID
16 bytes.
Hash
SHA256 hash (32 bytes)
UntypedBlobKey
- Hash (see above)
- Length (int32)
Arrays (<datatype>[])
- Count (int32)
- Item 1 .. Item n
Key (of encrypted data)
- Auth key (16 bytes)
- Cipher key (16 byte)
Encrypted data
- Initialization vector (IV) (16 bytes)
- Encrypted data (AES-256-CBC)
- Message authentication code (HMAC-SHA-256) of IV and encrypted data (32 bytes)
Compressed data
Data is compressed using GZIP.
JSON Data Types
int32
Signed 32-bit integers are written as a JSON number.
int64
Signed 64-bit integers are written as a JSON string because they can potentially exceed JavaScript’s Number.MAX_SAFE_INTEGER
(which is 9007199254740991
) on the positive side and Number.MIN_SAFE_INTEGER
(which is -9007199254740991
) on the negative side.
Hash
SHA256 hashes (32 bytes) are written as hexadecimal-encoded strings (0-9, a-f).
Instant
Instants (timestamps) are written as a JSON string in simplified extended ISO format (ISO 8601), which is always 24 characters long (YYYY-MM-DDTHH:mm:ss.sssZ
). The timezone is always zero UTC offset (suffix “Z”).
Archive Version
The archive version contents are encrypted with AES-256 as a whole, using the archive version key of the archive.
Archive Version Contents
1d 05 00
- Signature01
- Version- ID of the archive (UUID)
- Creation time of this archive version (Instant)
- Parent archive version hash (Hash). If there is no parent archive version, a hash only consisting of zeros is written.
- Root meta object hash (Hash)
- Meta objects - Map segment keys (UntypedBlobKey[]), sorted by hash
- Meta objects - Pack keys (UntypedBlobKey[]), sorted by hash
- Objects - Map segment keys (UntypedBlobKey[]), sorted by hash
- Objects - Pack keys (UntypedBlobKey[]), sorted by hash
Chunk Storage
Map
The map consists of one or more map segments, whose contents are encrypted with AES-256 as a whole, using the CAS key of the archive.
Map Segment Contents
1d 05 01
- Signature01
- Version- ChunkInfo[] - sorted by chunk key hash, then chunk key type
- UntypedBlobKey[] - pack keys used in the chunk infos - sorted
Chunk Info
- 32 Chunk key hash (hash)
- 1 Chunk key type (byte)
- 1 Chunk key subtype (byte)
- 1 Reserved - always 0 (byte)
- 1 Reserved - always 0 (byte)
- 4 Index of pack key (see map segment contents) (int32)
- 4 Offset within pack (int32)
- 4 Length within pack (int32)
Packs
Packs consist of
- A fixed-length header, encrypted with AES-256, always 64 bytes (encrypted length)
- A variable-length directory, encrypted with AES-256
- One or more chunks, each encrypted with AES-256
For encryption, the CAS key of the archive is used.
Header
1d 05 02
- Signature01
- Version- Encrypted directory length (int32)
Directory
1d 05 03
- Signature01
- Version- DirectoryEntry[] - sorted by offset
Directory Entry
- Offset (int32)
- Length (int32)
Chunk
1d 05 04
- Signature01
- Version- Chunk key hash (hash)
- Chunk type (byte)
- Chunk subtype (byte)
- Is the chunk’s contents compressed? (bool)
- Does the content’s uncompressed hash equal the chunk key hash? (bool)
- If it doesn’t:
- The content’s uncompressed hash (hash)
- If the contents is compressed:
- The hash of the compressed contents (hash)
- Length of compressed (if compressed) or uncompressed contents (int32)
- The compressed (if compressed) or uncompressed contents
Content Addressable Storage
The content addressable storage (CAS) can store any number of objects with arbitrary length. The CAS internally uses the (encrypted) chunk storage as follows:
Small objects
Small objects are written to the chunk store as a single chunk with
- Chunk key hash: The hash of the object
- Chunk type: CAS object (0)
- Chunk subtype: CAS small object (0)
Large objects
For large objects, a large object descriptor is written (see below) with
- Chunk key hash: The hash of the object
- Chunk type: CAS object (0)
- Chunk subtype: CAS large object fragment list (1)
For each fragment of the large object, a chunk is written
- Chunk key hash: The hash of the fragment
- Chunk type: CAS large object fragment (1)
- Chunk subtype: None (0)
Large object fragment list
1d 05 05
- Signature01
- Version- The hashes of the object’s fragments (hash[])
Item List
Item lists stored in the CAS always begin with {"$":"itemList@
(ASCII). They don’t have a UTF-8 BOM.
Item
Items can have the following type:
- fileArchive
- snapshot
- folder
- file
File Archive Item
- type:
fileArchive
- name:
FileArchive
- timestamp
- salt
- parent (optional, hash of the previous root meta object, null = empty root meta object)
- children
Snapshot Item (named Top-Level Folders in the User Interface)
- type:
snapshot
- name:
snapshot-group-name@instant
- source (optional)
- type:
local
- path
- type:
- size (int64)
- folders (int32)
- files (int32)
- children
- childrenModified (optional, default: false)
Folder Item
- type:
folder
- name
- size (int64)
- folders (int32)
- files (int32)
- children
File Item
- type:
file
- name
- size
- created (optional)
- modified (optional)
- contents
- thumbnail (optional)