Skip to content

Latest commit

 

History

History
121 lines (92 loc) · 4.22 KB

codec.md

File metadata and controls

121 lines (92 loc) · 4.22 KB

Codec and encryption

Sembast supports using a user-defined codec to encode/decode data when read/written to disk. It provides a way to support encryption. Encryption itself is not part of sembast but an example of a simple encryption algorithm (Salsa20 and SHA256 used from external packages) is provided in the test folder.

In pubspec.yaml:

dependencies:
  # Dependencies for encryption example
  encrypt: '>=3.1.0'
  crypto: '>=2.0.6'
// Initialize the encryption codec with a user password
var codec = getEncryptSembastCodec(password: '[your_user_password]');

// Open the database with the codec
Database db = await factory.openDatabase(dbPath, codec: codec);

// ...your database is ready to use

If you create multiple records, the content of the database will look like this where each record is encrypted:

"version":1,"sembast":1,"codec":"ZddmpxjrwUgJk7YnlC0lDhz7S2Iqcp8="}
Zdd+qwanmg1Qw6VkwnolQkWrRnctJMD9
Zdd+qwanmg5Qw6VkwnolQkWrQX0zNcLsqMobWlZppdFrayfLvTE2Q129UKIWbkdxrezXtzmQGajd+39xMhMe5w==
Zdd+qwanmg9Qw6VkwnolQkWrQXoxIpaiug==
Zdd+qwanmghQw6VkwnolQkWrQX0zNcL2otYFH0gmudNtdnXd+CYxUE29U6MbdkdypevetyCeAKjd7Xl1PwIc9y0ovYrPnatrpqeL

The header of the database will contain a signature encoded by the codec itself so that a database cannot be opened if the password is wrong.

Any other custom encryption/codec can be used as long as you provide a way to encode/decode a json encodable object (Map, List, num, String, bool and num) to/from a single line String.

Create an asynchronous codec

The codec must be able to handle the conversion of a json encodable data to a single line string. (i.e. json being one). If your codec requires calling a plugin or an asynchronous operation you have to create an asynchronous codec. One solution is to subclass AsyncContentCodecBase (which is a Codec<Object?, String> but with the synchronous encoding/decoding disabled) where you just have to implement:

  • decodeAsync
  • encodeAsync
/// My simple asynchronous codec.
class MyAsyncCodec extends AsyncContentCodecBase {
  String _reverseString(String text) => text.split('').reversed.join();

  @override
  Future<Object?> decodeAsync(String encoded) async {
    // Simple demo, just reverse the json asynchronously.
    return jsonDecode(_reverseString(encoded));
  }

  @override
  Future<String> encodeAsync(Object? input) async {
    // Simple demo, just reverse the json asynchronously.
    return _reverseString(jsonEncode(input));
  }
}

You have to specify a unique signature (not private) when opening the database with an sync codec:

// Open the database
var db = await factory.openDatabase('my_encoded_db.db',
    codec: SembastCodec(signature: 'my_codec', codec: MyAsyncCodec()));

Encrypting an existing database

It is unfortunately not possible to encrypt an existing database in place as a database format is tight to the codec used. You have to create a new one and copy the data. You can use databaseMerge to copy the data in a single operation/transaction.

Below is an example of how to convert an existing non-encrypted database to an encrypted one:

// Encryption codec to use (to set as you wish)
SembastCodec? encryptionCodec;

// Existing db name, to convert and remove if it exists
var nonEncryptedDbName = 'my_database.db';
var encryptedDbName = 'my_database_encrypted.db';

late Database db;
// Check if non encrypted database exists
if (await factory.databaseExists(nonEncryptedDbName)) {
// This should only happen once but could be a lengthy operation

  // Open the non encrypted database
  var nonEncryptedDb = await factory.openDatabase(nonEncryptedDbName);

  // Create a new encrypted database and copy the existing content
  await factory.deleteDatabase(encryptedDbName);
  db = await factory.openDatabase(encryptedDbName,
    codec: encryptionCodec);
  // Copy the content
  await databaseMerge(db, sourceDatabase: nonEncryptedDb);

  // Close and delete the non encrypted database
  await nonEncryptedDb.close();
  await factory.deleteDatabase(nonEncryptedDbName);
} else {
// Open the encrypted database
db = await factory.openDatabase(encryptedDbName,
    codec: encryptionCodec);
}