Here is the final report of the new native encryption project of GSoC 2024. The code has not been merged yet but I guess it will be merged soon. I will keep improving the related code before and after the PR is merged.
Patch for the performance test
diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts
index 9a56280a1..f2fc786ae 100644
--- a/packages/app-desktop/app.ts
+++ b/packages/app-desktop/app.ts
@@ -46,6 +46,7 @@ import { homedir } from 'os';
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
const electronContextMenu = require('./services/electron-context-menu');
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
+import { runIntegrationTests as runCryptoIntegrationTests } from '@joplin/lib/services/e2ee/cryptoTestUtils';
const commands = mainScreenCommands
.concat(noteEditorCommands)
@@ -750,7 +751,8 @@ class Application extends BaseApplication {
// });
// }, 2000);
- // await runIntegrationTests();
+ await new Promise(resolve => setTimeout(resolve, 20000)); // run performance test after init
+ await runCryptoIntegrationTests();
return null;
}
diff --git a/packages/app-mobile/android/gradle.properties b/packages/app-mobile/android/gradle.properties
index d30028f17..de76881c6 100644
--- a/packages/app-mobile/android/gradle.properties
+++ b/packages/app-mobile/android/gradle.properties
@@ -46,3 +46,8 @@ hermesEnabled=true
#
# https://github.com/robolectric/robolectric/issues/6521
android.jetifier.ignorelist=bcprov
+
+JOPLIN_RELEASE_STORE_FILE=my-upload-key.keystore
+JOPLIN_RELEASE_KEY_ALIAS=my-key-alias
+JOPLIN_RELEASE_STORE_PASSWORD=123456
+JOPLIN_RELEASE_KEY_PASSWORD=123456
\ No newline at end of file
diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx
index 35f1d9903..cc445a8bd 100644
--- a/packages/app-mobile/root.tsx
+++ b/packages/app-mobile/root.tsx
@@ -856,7 +856,6 @@ async function initialize(dispatch: Dispatch) {
} else {
logger.info('Skipping encryption tests -- not supported on web.');
}
- await runCryptoIntegrationTests();
await runOnDeviceFsDriverTests();
}
@@ -945,7 +944,9 @@ class AppComponent extends React.Component {
// https://discourse.joplinapp.org/t/webdav-config-encryption-config-randomly-lost-on-android/11364
// https://discourse.joplinapp.org/t/android-keeps-on-resetting-my-sync-and-theme/11443
public async componentDidMount() {
+ let runPerformanceTest = false;
if (this.props.appState === 'starting') {
+ runPerformanceTest = true;
this.props.dispatch({
type: 'APP_STATE_SET',
state: 'initializing',
@@ -1040,6 +1041,11 @@ class AppComponent extends React.Component {
// Setting.setValue('encryption.masterPassword', 'WRONG');
// setTimeout(() => NavService.go('EncryptionConfig'), 2000);
+
+ if (runPerformanceTest) {
+ await new Promise(resolve => setTimeout(resolve, 20000)); // run performance test after init
+ await runCryptoIntegrationTests();
+ }
}
public componentWillUnmount() {
diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts
index 30849bb3d..4983bbcde 100644
--- a/packages/lib/models/Setting.ts
+++ b/packages/lib/models/Setting.ts
@@ -945,7 +945,7 @@ class Setting extends BaseModel {
public static async saveAll() {
if (Setting.autoSaveEnabled && !this.saveTimeoutId_) return Promise.resolve();
- logger.debug('Saving settings...');
+ // logger.debug('Saving settings...');
shim.clearTimeout(this.saveTimeoutId_);
this.saveTimeoutId_ = null;
@@ -1016,7 +1016,7 @@ class Setting extends BaseModel {
}
}
- logger.debug('Settings have been saved.');
+ // logger.debug('Settings have been saved.');
}
public static scheduleChangeEvent() {
diff --git a/packages/lib/registry.ts b/packages/lib/registry.ts
index 12b09ccf2..3442dd999 100644
--- a/packages/lib/registry.ts
+++ b/packages/lib/registry.ts
@@ -126,7 +126,7 @@ class Registry {
// return;
}
- this.logger().debug('Scheduling sync operation...', delay);
+ // this.logger().debug('Scheduling sync operation...', delay);
const timeoutCallback = async () => {
this.timerCallbackCalls_.push(true);
diff --git a/packages/lib/services/e2ee/crypto.test.ts b/packages/lib/services/e2ee/crypto.test.ts
index ff39b3110..80e40ca7d 100644
--- a/packages/lib/services/e2ee/crypto.test.ts
+++ b/packages/lib/services/e2ee/crypto.test.ts
@@ -16,7 +16,7 @@ describe('e2ee/crypto', () => {
it('should encrypt and decrypt data from different devices', (async () => {
await expectNotThrow(async () => runIntegrationTests(true));
- }));
+ }), 150000);
it('should not generate new nonce if counter does not overflow', (async () => {
jest.useFakeTimers();
diff --git a/packages/lib/services/e2ee/cryptoTestUtils.ts b/packages/lib/services/e2ee/cryptoTestUtils.ts
index 0c56f9311..2dfbbd66b 100644
--- a/packages/lib/services/e2ee/cryptoTestUtils.ts
+++ b/packages/lib/services/e2ee/cryptoTestUtils.ts
@@ -74,8 +74,12 @@ export async function checkDecryptTestData(data: DecryptTestData, options: Check
for (const msg of messages) {
if (hasError) {
logger.warn(msg);
+ console.warn(msg);
} else {
- if (!options.silent) logger.info(msg);
+ if (!options.silent) {
+ logger.info(msg);
+ console.info(msg);
+ }
}
}
}
@@ -132,8 +136,12 @@ export async function testStringPerformance(method: EncryptionMethod, dataSize:
for (const msg of messages) {
if (hasError) {
logger.warn(msg);
+ console.warn(msg);
} else {
- if (!options.silent) logger.info(msg);
+ if (!options.silent) {
+ logger.info(msg);
+ console.info(msg);
+ }
}
}
}
@@ -191,8 +199,12 @@ export async function testFilePerformance(method: EncryptionMethod, dataSize: nu
for (const msg of messages) {
if (hasError) {
logger.warn(msg);
+ console.warn(msg);
} else {
- if (!options.silent) logger.info(msg);
+ if (!options.silent) {
+ logger.info(msg);
+ console.info(msg);
+ }
}
}
}
@@ -226,10 +238,11 @@ const decryptTestData: Record<string, DecryptTestData> = {
// This can be used to run integration tests directly on device. It will throw
// an error if something cannot be decrypted, or else print info messages.
-export const runIntegrationTests = async (silent = false, testPerformance = false) => {
+export const runIntegrationTests = async (silent = false, testPerformance = true) => {
const log = (s: string) => {
if (silent) return;
logger.info(s);
+ console.info(s);
};
log('Running integration tests...');
@@ -252,31 +265,35 @@ export const runIntegrationTests = async (silent = false, testPerformance = fals
if (testPerformance) {
log('Testing performance...');
if (shim.mobilePlatform() === '') {
- await testStringPerformance(EncryptionMethod.StringV1, 100, 1000);
- await testStringPerformance(EncryptionMethod.StringV1, 1000000, 10);
- await testStringPerformance(EncryptionMethod.StringV1, 5000000, 10);
- await testStringPerformance(EncryptionMethod.SJCL1a, 100, 1000);
- await testStringPerformance(EncryptionMethod.SJCL1a, 1000000, 10);
- await testStringPerformance(EncryptionMethod.SJCL1a, 5000000, 10);
+ await testStringPerformance(EncryptionMethod.StringV1, 100, 2400);
+ await testStringPerformance(EncryptionMethod.SJCL1a, 100, 5700);
+ await testStringPerformance(EncryptionMethod.StringV1, 10000, 1700);
+ await testStringPerformance(EncryptionMethod.SJCL1a, 10000, 1300);
+ await testStringPerformance(EncryptionMethod.StringV1, 1000000, 70);
+ await testStringPerformance(EncryptionMethod.SJCL1a, 1000000, 15);
+ await testStringPerformance(EncryptionMethod.StringV1, 5000000, 18);
+ await testStringPerformance(EncryptionMethod.SJCL1a, 5000000, 5);
await testFilePerformance(EncryptionMethod.FileV1, 100, 1000);
- await testFilePerformance(EncryptionMethod.FileV1, 1000000, 3);
- await testFilePerformance(EncryptionMethod.FileV1, 5000000, 3);
- await testFilePerformance(EncryptionMethod.SJCL1a, 100, 1000);
- await testFilePerformance(EncryptionMethod.SJCL1a, 1000000, 3);
+ await testFilePerformance(EncryptionMethod.SJCL1a, 100, 1500);
+ await testFilePerformance(EncryptionMethod.FileV1, 10000, 1000);
+ await testFilePerformance(EncryptionMethod.SJCL1a, 10000, 400);
+ await testFilePerformance(EncryptionMethod.FileV1, 1000000, 80);
+ await testFilePerformance(EncryptionMethod.SJCL1a, 1000000, 5);
+ await testFilePerformance(EncryptionMethod.FileV1, 5000000, 20);
await testFilePerformance(EncryptionMethod.SJCL1a, 5000000, 3);
} else {
- await testStringPerformance(EncryptionMethod.StringV1, 100, 100);
- await testStringPerformance(EncryptionMethod.StringV1, 500000, 3);
+ await testStringPerformance(EncryptionMethod.StringV1, 100, 120);
+ await testStringPerformance(EncryptionMethod.SJCL1a, 100, 120);
+ await testStringPerformance(EncryptionMethod.StringV1, 10000, 80);
+ await testStringPerformance(EncryptionMethod.SJCL1a, 10000, 40);
await testStringPerformance(EncryptionMethod.StringV1, 1000000, 3);
- await testStringPerformance(EncryptionMethod.SJCL1a, 100, 100);
- await testStringPerformance(EncryptionMethod.SJCL1a, 500000, 3);
await testStringPerformance(EncryptionMethod.SJCL1a, 1000000, 3);
- await testFilePerformance(EncryptionMethod.FileV1, 100, 100);
- await testFilePerformance(EncryptionMethod.FileV1, 100000, 3);
- await testFilePerformance(EncryptionMethod.FileV1, 500000, 3);
- await testFilePerformance(EncryptionMethod.SJCL1a, 100, 100);
- await testFilePerformance(EncryptionMethod.SJCL1a, 100000, 3);
- await testFilePerformance(EncryptionMethod.SJCL1a, 500000, 3);
+ await testFilePerformance(EncryptionMethod.FileV1, 100, 120);
+ await testFilePerformance(EncryptionMethod.SJCL1a, 100, 120);
+ await testFilePerformance(EncryptionMethod.FileV1, 10000, 70);
+ await testFilePerformance(EncryptionMethod.SJCL1a, 10000, 40);
+ await testFilePerformance(EncryptionMethod.FileV1, 1000000, 3);
+ await testFilePerformance(EncryptionMethod.SJCL1a, 1000000, 3);
}
}