Repository navigation
Encryption and Keys
Any JAZMIN file can be encrypted with AES-256-GCM under a key or a password. Everything is encrypted and authenticated: the rows, column names and types, metadata, indexes and embedded files. Reading an encrypted file is about as fast as reading a plain one.
| Key | Password | |
|---|---|---|
| What it is | 32 random bytes, written as text: jzk1- + base64url + checksum |
Text a person can remember |
| Made with |
JazminKey.generate() / JazminKey.Generate()
|
Your user |
| Turned into the file key by | Used directly | PBKDF2-SHA256, 600,000 iterations (deliberately slow) |
| Best for | Services, pipelines, anything automated | People opening files by hand |
| A typo gives | Key checksum mismatch (the checksum catches it) | Decryption failed |
import { JazminKey, JazminKeyError, inspect, open, rotateKey, write } from '@smithsoft-studios/jazmin';
const rows = [{ id: 1, salary: '52000.00' }, { id: 2, salary: '61000.00' }];
const key = JazminKey.generate();
const keyText = key.export(); // "jzk1-...": store it in a secret manager, never in logs or source control
write('payroll.jzm', rows, { key, metadata: { department: 'finance' } });
const { fileId, ...visible } = inspect('payroll.jzm'); // what anyone can see without a key (fileId is random)
console.log(visible);
const attempt = (label, options) => {
try {
const reader = open('payroll.jzm', options);
console.log(label.padEnd(18), 'opened:', reader.rowCount, 'rows,', JSON.stringify(reader.metadata));
reader.close();
} catch (e) {
console.log(label.padEnd(18), e instanceof JazminKeyError ? 'JazminKeyError:' : e.name, e.message);
}
};
attempt('no key', {});
attempt('the key text', { key: keyText });
attempt('a typo', { key: keyText.slice(0, 10) + (keyText[10] === 'A' ? 'B' : 'A') + keyText.slice(11) });
attempt('another key', { key: JazminKey.generate() });
// The key leaked? Re-lock the file under a new key, without decoding its rows
const newKey = JazminKey.generate();
rotateKey('payroll.jzm', { key, newKey });
attempt('old key, rotated', { key: keyText });
attempt('new key, rotated', { key: newKey });
// A password instead of a key
write('notes.jzm', [{ note: 'hello' }], { password: 'correct horse battery staple' });
console.log(inspect('notes.jzm').passwordProtected, open('notes.jzm', { password: 'correct horse battery staple' }).get(0));{
version: '1.0',
encrypted: true,
passwordProtected: false,
accessControlled: false,
appended: false
}
no key JazminKeyError: This file is encrypted - supply a key or password
the key text opened: 2 rows, {"department":"finance"}
a typo JazminKeyError: Key checksum mismatch - the key text is mistyped or corrupted
another key JazminKeyError: Decryption failed - wrong key/password or the data was tampered with
old key, rotated JazminKeyError: Decryption failed - wrong key/password or the data was tampered with
new key, rotated opened: 2 rows, {"department":"finance"}
true { note: 'hello' }
- What stays visible without the key: that the file is JAZMIN, its format version, whether it is encrypted or password-protected, its total size and the size of each section. Nothing else.
-
A file id (
inspect().fileId) is readable without a key, so a key service can tell files apart. - Tampering is detected: changing bytes, swapping chunks, copying sections from another file, or stripping the encryption all fail with Decryption failed or a signature error.
using Jazmin;
using Jazmin.Serialization;
var key = JazminKey.Generate();
string keyText = key.Export(); // store in a secret manager; in production: JazminKey.Parse(secretFromVault)
var settings = new JazminSerializerSettings { Key = JazminKey.Parse(keyText) };
new JazminSerializer(settings).Serialize("payroll.jzm", new[] { new Payslip { Id = 1, Salary = 52000m } });
try { JazminReader.Open("payroll.jzm"); }
catch (JazminKeyException e) { Console.WriteLine(e.Message); }
using (var reader = JazminReader.Open("payroll.jzm", new JazminReadOptions { Key = key }))
Console.WriteLine($"{reader.RowCount} row, encrypted: {reader.IsEncrypted}");
var newKey = JazminKey.Generate();
JazminFile.RotateKey("payroll.jzm", new JazminKeyRotation { Key = key, NewKey = newKey });
using (var reader = JazminReader.Open("payroll.jzm", new JazminReadOptions { Key = newKey }))
Console.WriteLine($"rotated: {reader.Query<Payslip>(p => p.Id == 1).Single().Salary}");
public class Payslip
{
public int Id { get; set; }
public decimal Salary { get; set; }
}This file is encrypted - supply a key or password
1 row, encrypted: True
rotated: 52000
- Generate keys with the library. Don't derive them from names, dates or other guessable data.
-
Store
export()text in a secret manager (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault). Never next to the file, in source control or in logs. -
Use
export(), nottoString(), wherever you mean to save or send a key. Until 2.0,toString()returns the same secret text, so a key that ends up in a log line or error message leaks. From 2.0, an access key'stoString()prints only its id. - Lost key, lost data. There is no recovery and no back door.
-
Passwords: PBKDF2 slows down guessing, but a weak password is still weak. Keep
kdfIterationsat the default (600,000) in production; readers refuse files outside 1,000 to 10,000,000. - Rotation is fast because rows aren't decoded: each section is decrypted and encrypted again as stored. On a 176 MB file it took 0.7 s, against 14–33 s for a full rewrite. Afterwards nothing is shared with the old version: new master key, file id, salt and section keys.
- Rotation can't undo a leak: anyone who held the old key and a copy of the old file can still read that copy.
-
Keys and passwords can change places:
rotateKey(path, { key, newPassword })or the reverse. - A file with appends is compacted first, so earlier versions under the old key aren't kept inside it.
Compression can leak information through size: if an attacker can put text of their choosing into a file that also
holds a secret, and can see the file's size, they may learn something about the secret by watching the size change.
If that describes your data, write it with codec: 'none'.
One key opens everything. To give different people different rows or columns of the same file, use an owner key and
access keys: see Access Control. To change a shared file's owner key, use rotateOwnerKey, which
also issues every person a new access key.
JAZMIN 1.2 · file format 1.0 · Repository · npm · NuGet · MIT license · Examples in this wiki are run and checked by tools/check-examples.mjs
Start
Using files
- Writing Files
- Nested Columns
- Reading and Querying
- Filter Language
- .NET Serializer and LINQ
- Updating and Appending
- Several Tables
- Converting JSON, CSV, XML
- Export Shapes
Security and sharing
- Encryption and Keys
- Access Control
- Masked Columns
- Time-Limited Access
- Sending Records Back
- Security Model
Documents and apps
Performance
Reference