move bcrypt operations to webworkers

This commit is contained in:
PhilReact 2024-11-17 21:40:16 +02:00
parent 9ef74d7a42
commit 8679911c65
8 changed files with 128 additions and 6 deletions

View File

@ -36,6 +36,9 @@ dependencies {
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android') implementation project(':capacitor-android')
implementation "org.mindrot:jbcrypt:0.4"
implementation "at.favre.lib:bcrypt:0.10.2"
implementation 'com.password4j:password4j:1.8.2'
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"

View File

@ -1,5 +1,15 @@
package com.github.Qortal.qortalMobile; package com.github.Qortal.qortalMobile;
import com.getcapacitor.BridgeActivity; import com.getcapacitor.BridgeActivity;
import com.github.Qortal.qortalMobile.NativeBcrypt;
import android.os.Bundle;
public class MainActivity extends BridgeActivity {} public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(NativeBcrypt.class);
super.onCreate(savedInstanceState);
}
}

View File

@ -0,0 +1,62 @@
package com.github.Qortal.qortalMobile;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import android.os.Process;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.JSObject;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import org.mindrot.jbcrypt.BCrypt;
@CapacitorPlugin(name = "NativeBcrypt")
public class NativeBcrypt extends Plugin {
// Use a fixed thread pool with the number of CPU cores
private final ExecutorService executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setPriority(Thread.MAX_PRIORITY); // Set thread priority to high
return thread;
}
}
);
@PluginMethod
public void hashPassword(PluginCall call) {
String password = call.getString("password");
String salt = call.getString("salt");
if (password == null || salt == null) {
call.reject("Password or salt is missing");
return;
}
executor.execute(() -> {
try {
// Perform bcrypt hashing
String hash = BCrypt.hashpw(password, salt);
// Prepare the result
JSObject result = new JSObject();
result.put("hash", hash);
// Resolve the call on the main thread
getActivity().runOnUiThread(() -> call.resolve(result));
} catch (Exception e) {
// Reject the call on the main thread in case of an error
getActivity().runOnUiThread(() -> call.reject("Hashing failed: " + e.getMessage()));
}
});
}
@Override
public void handleOnDestroy() {
super.handleOnDestroy();
executor.shutdown(); // Shutdown the executor to release resources
}
}

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "qortal-go", "name": "qortal-go",
"version": "0.3.1", "version": "0.3.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "qortal-go", "name": "qortal-go",
"version": "0.3.1", "version": "0.3.2",
"dependencies": { "dependencies": {
"@capacitor/android": "^6.1.2", "@capacitor/android": "^6.1.2",
"@capacitor/app": "^6.0.1", "@capacitor/app": "^6.0.1",

View File

@ -23,7 +23,7 @@ import { set } from "lodash";
import { cleanUrl, isUsingLocal } from "../background"; import { cleanUrl, isUsingLocal } from "../background";
export const manifestData = { export const manifestData = {
version: '0.3.2' version: '0.3.2.1'
} }
export const NotAuthenticated = ({ export const NotAuthenticated = ({

View File

@ -0,0 +1,12 @@
import bcrypt from 'bcryptjs'
self.onmessage = function (e) {
const { hashBase64, salt } = e.data;
try {
const result = bcrypt.hashSync(hashBase64, salt);
self.postMessage({ result });
} catch (error) {
self.postMessage({ error: error.message });
}
};

View File

@ -3,7 +3,11 @@
import {bytes_to_base64 as bytesToBase64, Sha512} from 'asmcrypto.js' import {bytes_to_base64 as bytesToBase64, Sha512} from 'asmcrypto.js'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import utils from '../utils/utils' import utils from '../utils/utils'
// import NativeBcrypt from '../utils/nativebcrypt'
import BcryptWorker from './bcryptworker.worker.js?worker';
import { crypto } from '../constants/decryptWallet' import { crypto } from '../constants/decryptWallet'
const stringtoUTF8Array = (message)=> { const stringtoUTF8Array = (message)=> {
if (typeof message === 'string') { if (typeof message === 'string') {
var s = unescape(encodeURIComponent(message)) // UTF-8 var s = unescape(encodeURIComponent(message)) // UTF-8
@ -15,6 +19,27 @@ const stringtoUTF8Array = (message)=> {
return message return message
} }
const bcryptInWorker = (hashBase64, salt) => {
return new Promise((resolve, reject) => {
const worker = new BcryptWorker()
worker.onmessage = (e) => {
const { result, error } = e.data;
if (error) {
reject(error);
} else {
resolve(result);
}
worker.terminate();
};
worker.onerror = (err) => {
reject(err.message);
worker.terminate();
};
worker.postMessage({ hashBase64, salt });
});
};
const stringToUTF8Array=(message)=> { const stringToUTF8Array=(message)=> {
if (typeof message !== 'string') return message; // Assuming you still want to pass through non-string inputs unchanged if (typeof message !== 'string') return message; // Assuming you still want to pass through non-string inputs unchanged
const encoder = new TextEncoder(); // TextEncoder defaults to UTF-8 const encoder = new TextEncoder(); // TextEncoder defaults to UTF-8
@ -23,10 +48,11 @@ const stringToUTF8Array=(message)=> {
const computekdf = async (req)=> { const computekdf = async (req)=> {
const { salt, key, nonce, staticSalt, staticBcryptSalt } = req const { salt, key, nonce, staticSalt, staticBcryptSalt } = req
const combinedBytes = utils.appendBuffer(new Uint8Array([]), stringToUTF8Array(`${staticSalt}${key}${nonce}`)) const combinedBytes = utils.appendBuffer(new Uint8Array([]), stringToUTF8Array(`${staticSalt}${key}${nonce}`))
const sha512Hash = new Sha512().process(combinedBytes).finish().result const sha512Hash = new Sha512().process(combinedBytes).finish().result
const sha512HashBase64 = bytesToBase64(sha512Hash) const sha512HashBase64 = bytesToBase64(sha512Hash)
const result = bcrypt.hashSync(sha512HashBase64.substring(0, 72), staticBcryptSalt)
const result = await bcryptInWorker(sha512HashBase64.substring(0, 72), staticBcryptSalt);
return { key, nonce, result } return { key, nonce, result }
} }

View File

@ -0,0 +1,9 @@
import { registerPlugin } from '@capacitor/core';
export interface NativeBcryptPlugin {
hashPassword(options: { password: string; salt: string }): Promise<{ hash: string }>;
}
const NativeBcrypt = registerPlugin<NativeBcryptPlugin>('NativeBcrypt');
export default NativeBcrypt;