SQLite.Encrypted for Cordova and Capacitor

@sqlite-encrypted/cordova retains the cordova-sqlite-storage JavaScript API and replaces its Android and iOS implementations with SQLite.Encrypted. The same plugin is compatible with Capacitor; there is no separate Capacitor package.

Core rule: Wait for deviceready, activate SEE, then open with key.

Codec support: SQLite.Encrypted currently supports only aes256ofb. Pass a null codec to use aes256ofb; any codec supplied to key() or rekey() must be aes256ofb. Keyed opens use the same aes256ofb default.

Install in Cordova

Configure the SQLite.Encrypted npm scope in the project's .npmrc:

@sqlite-encrypted:registry=https://packages.sourcegear.com/npm/

Then install the plugin:

cordova plugin add @sqlite-encrypted/cordova
cordova prepare

The Cordova plugin ID is sqlite-encrypted-cordova.

Install in Capacitor

npm install @sqlite-encrypted/cordova
npx cap sync

Use a native Android or iOS Capacitor build. Browser, macOS, and Windows implementations are not included.

Activate and open

document.addEventListener('deviceready', async () => {
  await new Promise((resolve, reject) => {
    window.sqlitePlugin.activateSEE(
      activationKey,
      resolve,
      reject
    );
  });

  const database = window.sqlitePlugin.openDatabase({
    name: 'application.db',
    location: 'default',
    key: databaseKey,
  });
});

The keyed open routes through the native key implementation and reports a key-loading failure through the open callback. Omit key for a plaintext database. Opening without key and immediately calling database.key() is the preferred alternative when the key is not available at open time.

Open with PRAGMA

Open without a key, then execute both statements before schema access. Verify that the key PRAGMA returns one row containing ok.

document.addEventListener('deviceready', async () => {
  const database = window.sqlitePlugin.openDatabase({
    name: 'application.db',
    location: 'default',
  });

  const execute = sql => new Promise((resolve, reject) => {
    database.executeSql(sql, [], resolve, reject);
  });

  await execute("PRAGMA activate_extensions = 'see-<activation-key>'");
  const result = await execute("PRAGMA key = '<database-key>'");
  const row = result.rows.length ? result.rows.item(0) : null;
  if (!row || Object.values(row)[0] !== 'ok') {
    throw new Error('SEE did not accept the database key');
  }
});

On a build without encryption support, or when SEE cannot load the key, the key PRAGMA returns no row. Ignoring that result can leave the application using a connection whose encryption key was never applied. Prefer keyed open or database.key() for new integrations.

Create and query a table

The ordinary cordova-sqlite-storage database and transaction API is unchanged.

database.transaction((transaction) => {
  transaction.executeSql(
    'CREATE TABLE IF NOT EXISTS notes (' +
      'id INTEGER PRIMARY KEY, body TEXT NOT NULL)'
  );
  transaction.executeSql(
    'INSERT INTO notes(body) VALUES (?)',
    ['hello']
  );
  transaction.executeSql(
    'SELECT id, body FROM notes ORDER BY id',
    [],
    (_tx, result) => {
      for (let index = 0; index < result.rows.length; index += 1) {
        console.log(result.rows.item(index));
      }
    }
  );
});

Text, binary, and null keys

The open database adds callback-based key and rekey methods. The key may be a string, Uint8Array, or null.

const call = (operation) => new Promise((resolve, reject) => {
  operation(resolve, reject);
});

await call((ok, fail) =>
  database.key(null, 'initial text key', null, ok, fail)
);

const binaryKey = Uint8Array.from([0x01, 0x00, 0x7f, 0xff]);
await call((ok, fail) =>
  database.rekey(null, binaryKey, null, ok, fail)
);

await call((ok, fail) =>
  database.rekey(null, 'replacement text key', null, ok, fail)
);

await call((ok, fail) =>
  database.rekey(null, null, null, ok, fail)
);

Null removes encryption. Null database and codec values select the main database and the aes256ofb default.