tekartik / sembast.dart

Simple io database
BSD 2-Clause "Simplified" License
780 stars 64 forks source link

Can encrypted database be exported? #307

Closed jasonkaruza closed 2 years ago

jasonkaruza commented 2 years ago

Right now the exportDatabase() functionality only seems to support exporting the contents of the database in an unencrypted format. Can the encrypted database be exported in an encrypted state to later be imported, also in an encrypted manner using the same code that was used to initially encrypt it?

Thank you!

alextekartik commented 2 years ago

There is not direct support for that. The export is a json encodable map so any encryption can be done on it

You could re-use the sembast codec encryption used in the database or use another one. The encryption of the database and the encryption of the export are independent.

The SembastCodec object has not useful export for encoding data directly but we can use its internal codec.

Let's assume you use an encryption codec such as one in https://github.com/tekartik/sembast.dart/blob/master/sembast_test/lib/encrypt_codec.dart

// Define (or not) a codec to encrypt the database
var sembastCodec = getEncryptSembastCodec(password: 'my_password');
// The export codec could be the same or could be a different one
var exportCodec = getEncryptSembastCodec(password: 'my_export_password');

You can convert any map with it using its internal codec (SembastCodec.codec)

var map = {'test': 1};
print('map: $map');
print('encrypted map: ${exportCodec.codec!.encode({'test': 1})}');

which should give something like (encryption changes each time using this codec)

map: {test: 1}
encrypted map: nFLl/6RIfyw=qvnreCPoboYkvQ==

Let's create a simple encrypted database with a single record

// test in memory for simplicity
var factory = newDatabaseFactoryMemory();
// The database could use the codec or not
var db = await factory.openDatabase('test.db', codec: sembastCodec);
await intMapStoreFactory.store().add(db, {'test': 1});

The export is a map that you can encrypt using your own codec or using a sembast codec:

var exportMap = await exportDatabase(db);
print('export: $exportMap');

var encryptedExport = exportCodec.codec!.encode(exportMap);
print('encrypted export: $encryptedExport');

var decryptededExport = exportCodec.codec!.decode(encryptedExport) as Map;
print('decrypted export: $decryptededExport');
export: {sembast_export: 1, version: 1, stores: [{name: _main, keys: [1], values: [{test: 1}]}]}
encrypted export: Snkfa0C778c=Rg6GGbDwEvPBlMyLtdhJTWwh00J3YbSAG33DNDm8s0dOKx6Avb7AG+wBttBDO9Rdiskl3IOjWIPoUMTZC9phi1i87mBICTki/4EBb5F7+yJS8wdzeeo0lpF5K7rh
decrypted export: {sembast_export: 1, version: 1, stores: [{name: _main, keys: [1], values: [{test: 1}]}]}

You need to import the decrypted content and specify the codec to use in the database so that the database itself is encrypted

db = await importDatabase(decryptededExport, factory, 'test_import.db', codec: sembastCodec);
print (await intMapStoreFactory.store().query().getSnapshots(db));
[Record(_main, 1) {test: 1}]
jasonkaruza commented 2 years ago

@alextekartik you are a rockstar and legend! Thank you for your help :-) This might be another useful example for the docs, but really appreciate it. Was missing the use of codec with the importDatabase() call, too. Works like a charm 💯