All sizes below are bytes. Integers use unsigned big-endian encoding.
| Offset | Size | Meaning |
|---|---|---|
| 0 | 8 | ASCII VAULTG01: format and algorithm suite identifier |
| 8 | 16 | Random password-derivation salt and vault identity |
| 24 | 12 | Random AES-GCM nonce, regenerated on every write |
| 36 | 8 | Plaintext/ciphertext length, at most 67,108,864 |
| 44 | length | AES-256-GCM ciphertext |
| 44 + length | 16 | GCM authentication tag |
The entire 44-byte header is GCM additional authenticated data. Trailing bytes, truncation, unsupported identifiers, and inconsistent lengths are rejected. No padding, compression, optional fields, or host-endian structures are used.
Derive the 32-byte AES key using OpenSSL scrypt with the exact password bytes,
the 16-byte salt, and N=131072, r=8, p=1. The implementation supplies a 256 MiB
memory ceiling. Passwords are not normalized or NUL-terminated for derivation.
These parameters are fixed by G01; changing them requires a new suite identifier.
| Offset | Size | Meaning |
|---|---|---|
| 0 | 8 | ASCII VAULTT01 |
| 8 | 16 | Vault salt |
| 24 | 32 | Raw derived AES key |
Tokens are exactly 56 bytes. They are unencrypted bearer credentials, not signatures or password hashes. Salt equality is an early binding check; successful GCM verification is the actual key-authentication check. Updates authenticate the existing ciphertext before writing and retain the salt/key while refreshing the nonce. There is no nonce in the token, allowing it to remain valid across updates.
Random 96-bit nonces have a birthday collision bound. Applications must not perform billions of writes under one key; recreate under a fresh salt/key well before that scale. Recreating also changes token identity. This format has no replay/rollback counter: an older authentic copy of a file remains authentic.
test/fixture.h contains a deterministic file generated through Node.js's crypto
API, independently of the C++ implementation. It uses password fixture password,
salt bytes 00..0f, nonce bytes 10..1b, and plaintext 00 01 ff 00 2a 80.
Reproduce it using crypto.scryptSync with the G01 parameters and a 256 MiB
maxmem, then crypto.createCipheriv('aes-256-gcm', key, nonce) with the header
passed to setAAD. Concatenate header, ciphertext, and getAuthTag().
The fixed salt and nonce are test-only values; production always generates them.