/ 01The at-rest model
Encryption is applied to the SQLite database within the ZIP, not to the ZIP itself. The manifest, schema, oplog, and audit log remain readable — they carry no business data on their own, and keeping them in clear allows integrity verification and recovery tooling to work even when the password is wrong.
If a file is encrypted, manifest.extensions.encryption is populated. Readers that see this block without a password decline to open the SQLite blob and surface the file as locked.
/ 02Key derivation (PBKDF2)
Passwords are stretched into a 256-bit key using PBKDF2-SHA512 with 256,000 iterations and a per-file salt.
algorithmrequiredCurrently alwayspbkdf2-sha512. Future formats may add Argon2.iterations≥ 100,000Default 256,000. The reader honours whatever is in the manifest; writers always emit the current default.salt16-byte randomGenerated at file-create or rekey. Stored hex-encoded.key_length32 bytesFixed at 256 bits.
pub fn derive_key(password: &str, salt: &str, iterations: u32) -> String { // 32-byte hex-encoded key }
/ 03AES-256-GCM envelope
The derived key is used as the AES-256-GCM key. The nonce is 12 bytes of fresh randomness, generated at encrypt-time and prepended to the ciphertext. Authentication tag (16 bytes) is appended.
encrypted_blob = nonce (12 bytes) || ciphertext || auth_tag (16 bytes)
Tampering with any byte — ZIP CRC included — fails the GCM tag check on decrypt. The reader surfaces this as IntegrityCheckFailed in the audit log and refuses to open.
Never extract the database from a fieldbook, modify it externally, and re-pack. The integrity hashes in manifest.json will mismatch and the file will be treated as tampered with. Use the CLI or Tauri app for any mutation.
/ 04Rekeying
Rekeying changes the password without re-deriving table or field IDs. The flow:
Snapshot
A snapshot with reason
BeforeRestoreis taken before any modification.Decrypt with old key
Old password is verified by decrypting and checking the integrity hashes.
Generate new salt
16-byte random salt. Iteration count is bumped to the current default if the file is below it.
Derive new key, encrypt
New key derived; SQLite blob re-encrypted with a fresh nonce.
Update manifest
extensions.encryption.salt,iterations, and integrity hashes updated. New audit entrySnapshotCreated+SnapshotRestored.
/ 05Recovery & loss policy
There is no key escrow. A lost password cannot be reset by Parelion or anyone else. This is a property, not a bug: encrypted Fieldbook files are customer-held secrets.
What is recoverable
- Manifest, schema, oplog, audit log — all clear text, always readable
- File identity, version, last modified, size
- Hash-chain integrity (provable tampering)
What is not
- SQLite contents — the actual data
- Embedded documents — they remain in clear text in the ZIP, but cell-level attachments referenced from the database are unreachable without the schema's table+record mapping
For team and enterprise deployments, see BYOK below — recovery is delegated to the customer's KMS.
/ 06BYOK (forthcoming)
Bring-Your-Own-Key support is on the Phase-2 roadmap. The model:
- The customer holds a Key Encryption Key (KEK) in their KMS — Azure Key Vault, AWS KMS, GCP KMS, or HSM-backed
- The per-file Data Encryption Key (DEK) is wrapped by the KEK and stored in
manifest.extensions.encryption.wrapped_dek - To open the file, the reader requests an unwrap from KMS; the unwrapped DEK is held in memory only
- Revoking the KEK in KMS cryptographically inactivates every fieldbook keyed off it
BYOK files retain a backup password path by default; enterprise tenants can disable this for strict mode.
