Skip to content

Commit

Permalink
Setup encryption by passing key
Browse files Browse the repository at this point in the history
  • Loading branch information
mugikhan committed Nov 26, 2024
1 parent c135361 commit 10d8451
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 35 deletions.
2 changes: 1 addition & 1 deletion demos/react-supabase-todolist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@powersync/web": "workspace:*",
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
"@journeyapps/wa-sqlite": "0.0.0-dev-20241126093237",
"@journeyapps/wa-sqlite": "0.0.0-dev-20241126145151",
"@mui/icons-material": "^5.15.12",
"@mui/material": "^5.15.12",
"@mui/x-data-grid": "^6.19.6",
Expand Down
4 changes: 2 additions & 2 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"author": "JOURNEYAPPS",
"license": "Apache-2.0",
"peerDependencies": {
"@journeyapps/wa-sqlite": "0.0.0-dev-20241126093237",
"@journeyapps/wa-sqlite": "0.0.0-dev-20241126145151",
"@powersync/common": "workspace:^1.21.0"
},
"dependencies": {
Expand All @@ -72,7 +72,7 @@
"js-logger": "^1.6.1"
},
"devDependencies": {
"@journeyapps/wa-sqlite": "0.0.0-dev-20241126093237",
"@journeyapps/wa-sqlite": "0.0.0-dev-20241126145151",
"@types/uuid": "^9.0.6",
"@vitest/browser": "^2.1.4",
"crypto-browserify": "^3.12.0",
Expand Down
25 changes: 20 additions & 5 deletions packages/web/src/db/PowerSyncDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,31 @@ type WithWebSyncOptions<Base> = Base & {
sync?: WebSyncOptions;
};

export interface WebEncryptionOptions {
/**
* Encryption key for the database.
* If set, the database will be encrypted using Multiple Ciphers.
*/
encryptionKey?: string;
}

type WithWebEncryptionOptions<Base> = Base & {
encryptionKey?: string;
};

export type WebPowerSyncDatabaseOptionsWithAdapter = WithWebSyncOptions<
WithWebFlags<PowerSyncDatabaseOptionsWithDBAdapter>
WithWebFlags<WithWebEncryptionOptions<PowerSyncDatabaseOptionsWithDBAdapter>>
>;
export type WebPowerSyncDatabaseOptionsWithOpenFactory = WithWebSyncOptions<
WithWebFlags<PowerSyncDatabaseOptionsWithOpenFactory>
WithWebFlags<WithWebEncryptionOptions<PowerSyncDatabaseOptionsWithOpenFactory>>
>;
export type WebPowerSyncDatabaseOptionsWithSettings = WithWebSyncOptions<
WithWebFlags<PowerSyncDatabaseOptionsWithSettings>
WithWebFlags<WithWebEncryptionOptions<PowerSyncDatabaseOptionsWithSettings>>
>;

export type WebPowerSyncDatabaseOptions = WithWebSyncOptions<WithWebFlags<PowerSyncDatabaseOptions>>;
export type WebPowerSyncDatabaseOptions = WithWebSyncOptions<
WithWebFlags<WithWebEncryptionOptions<PowerSyncDatabaseOptions>>
>;

export const DEFAULT_POWERSYNC_FLAGS: Required<WebPowerSyncFlags> = {
...DEFAULT_WEB_SQL_FLAGS,
Expand Down Expand Up @@ -120,7 +134,8 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
protected openDBAdapter(options: WebPowerSyncDatabaseOptionsWithSettings): DBAdapter {
const defaultFactory = new WASQLiteOpenFactory({
...options.database,
flags: resolveWebPowerSyncFlags(options.flags)
flags: resolveWebPowerSyncFlags(options.flags),
encryptionKey: options.encryptionKey
});
return defaultFactory.openDB();
}
Expand Down
8 changes: 7 additions & 1 deletion packages/web/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ export interface WASQLiteDBAdapterOptions extends Omit<PowerSyncOpenFactoryOptio
workerPort?: MessagePort;

worker?: string | URL | ((options: ResolvedWebSQLOpenOptions) => Worker | SharedWorker);

/**
* Encryption key for the database.
* If set, the database will be encrypted using SQLCipher.
*/
encryptionKey?: string;
}

/**
Expand Down Expand Up @@ -111,7 +117,7 @@ export class WASQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement

return;
}
this.methods = await _openDB(this.options.dbFilename, { useWebWorker: false });
this.methods = await _openDB(this.options.dbFilename, this.options.encryptionKey, { useWebWorker: false });
this.methods.registerOnTableChange((event) => {
this.iterateListeners((cb) => cb.tablesUpdated?.(event));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
protected openAdapter(): DBAdapter {
return new WASQLiteDBAdapter({
...this.options,
flags: this.resolvedFlags
flags: this.resolvedFlags,
encryptionKey: this.options.encryptionKey
});
}
}
6 changes: 6 additions & 0 deletions packages/web/src/db/adapters/web-sql-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export interface WebSQLOpenFactoryOptions extends SQLOpenOptions {
* or a factory method that returns a worker.
*/
worker?: string | URL | ((options: ResolvedWebSQLOpenOptions) => Worker | SharedWorker);

/**
* Encryption key for the database.
* If set, the database will be encrypted using Multiple Ciphers.
*/
encryptionKey?: string;
}

export function isServerSide() {
Expand Down
39 changes: 21 additions & 18 deletions packages/web/src/shared/open-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,48 @@ let nextId = 1;

export async function _openDB(
dbFileName: string,
encryptionKey?: string,
options: { useWebWorker: boolean } = { useWebWorker: true }
): Promise<DBFunctionsInterface> {
const { default: moduleFactory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs');
let moduleFactory;
if (encryptionKey) {
console.log('Using encrypted database');
moduleFactory = (await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs')).default;
} else {
console.log('Using unencrypted database');
moduleFactory = (await import('@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs')).default;
}
// const { default: moduleFactory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs');
const module = await moduleFactory();
const sqlite3 = SQLite.Factory(module);
console.log('sqlite3', sqlite3);

/**
* Register the PowerSync core SQLite extension
*/
module.ccall('powersync_init_static', 'int', []);

console.log('powerync init');

const { IDBBatchAtomicVFS } = await import('@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js');
console.log('DbFileName', dbFileName);
// @ts-expect-error The types for this static method are missing upstream
const vfs = await IDBBatchAtomicVFS.create(dbFileName, module, {
lockPolicy: 'exclusive'
});

console.log('vfs before', vfs);
sqlite3.vfs_register(vfs, true);

const createResult = module.ccall('sqlite3mc_vfs_create', 'int', ['string', 'int'], [dbFileName, 1]);
console.log('result from creation', createResult);
if (createResult !== 0) {
throw new Error('Failed to create multiple cipher vfs');
if (encryptionKey) {
const createResult = module.ccall('sqlite3mc_vfs_create', 'int', ['string', 'int'], [dbFileName, 1]);
if (createResult !== 0) {
throw new Error('Failed to create multiple cipher vfs');
}
}
console.log('vfs after', vfs);

const db = await sqlite3.open_v2(dbFileName);
console.log('db', db);

// const pragma = await sqlite3.exec(db, 'PRAGMA key = "key"');
// console.log('pragma', pragma);
// if (pragma !== SQLite.SQLITE_OK) {
// throw new Error('Failed to set key');
// }
if (encryptionKey) {
const pragma = await sqlite3.exec(db, `PRAGMA key = "${encryptionKey}"`);
if (pragma !== SQLite.SQLITE_OK) {
throw new Error('Failed to set encryption key');
}
}

const statementMutex = new Mutex();

Expand Down
28 changes: 21 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 10d8451

Please sign in to comment.