diff --git a/apps/mobile-app/android/app/build.gradle b/apps/mobile-app/android/app/build.gradle index 5343a0748..40fb95ec8 100644 --- a/apps/mobile-app/android/app/build.gradle +++ b/apps/mobile-app/android/app/build.gradle @@ -186,6 +186,9 @@ dependencies { // Add vector drawable support for SVG implementation("com.caverock:androidsvg-aar:1.4") + // Add Argon2 library for password key derivation + implementation("com.lambdapioneer.argon2kt:argon2kt:1.4.0") + // Test dependencies testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.0.0' diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt index 674380ae6..1033645b8 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/nativevaultmanager/NativeVaultManager.kt @@ -778,6 +778,27 @@ class NativeVaultManager(reactContext: ReactApplicationContext) : } } + /** + * Derive a key from a password using Argon2Id. + * @param password The password to derive from + * @param salt The salt to use + * @param encryptionType The type of encryption (should be "Argon2Id") + * @param encryptionSettings JSON string with encryption parameters + * @param promise The promise to resolve + */ + @ReactMethod + override fun deriveKeyFromPassword(password: String, salt: String, encryptionType: String, encryptionSettings: String, promise: Promise) { + try { + val derivedKey = vaultStore.deriveKeyFromPassword(password, salt, encryptionType, encryptionSettings) + // Return as base64 string + val base64Key = android.util.Base64.encodeToString(derivedKey, android.util.Base64.NO_WRAP) + promise.resolve(base64Key) + } catch (e: Exception) { + Log.e(TAG, "Error deriving key from password", e) + promise.reject("ERR_DERIVE_KEY", "Failed to derive key from password: ${e.message}", e) + } + } + /** * Open the autofill settings page. * @param promise The promise to resolve diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt index 7c87fb8b7..8b1561d0c 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt @@ -6,6 +6,9 @@ import android.os.Handler import android.os.Looper import android.util.Base64 import android.util.Log +import com.lambdapioneer.argon2kt.Argon2Kt +import com.lambdapioneer.argon2kt.Argon2Mode +import com.lambdapioneer.argon2kt.Argon2Version import net.aliasvault.app.vaultstore.interfaces.CredentialOperationCallback import net.aliasvault.app.vaultstore.interfaces.CryptoOperationCallback import net.aliasvault.app.vaultstore.keystoreprovider.KeystoreOperationCallback @@ -240,6 +243,43 @@ class VaultStore( return this.storageProvider.getKeyDerivationParams() } + /** + * Derive a key from a password using Argon2Id. + * @param password The password to derive from + * @param salt The salt to use + * @param encryptionType The type of encryption (should be "Argon2Id") + * @param encryptionSettings JSON string with encryption parameters + * @return The derived key as a ByteArray + */ + fun deriveKeyFromPassword(password: String, salt: String, encryptionType: String, encryptionSettings: String): ByteArray { + if (encryptionType != "Argon2Id") { + throw IllegalArgumentException("Unsupported encryption type: $encryptionType") + } + + // Parse encryption settings JSON + val settings = JSONObject(encryptionSettings) + val iterations = settings.getInt("Iterations") + val memorySize = settings.getInt("MemorySize") + val parallelism = settings.getInt("DegreeOfParallelism") + + // Create Argon2 instance + val argon2 = Argon2Kt() + + // Hash the password using Argon2Id + val hashResult = argon2.hash( + mode = Argon2Mode.ARGON2_ID, + password = password.toByteArray(Charsets.UTF_8), + salt = salt.toByteArray(Charsets.UTF_8), + tCostInIterations = iterations, + mCostInKibibyte = memorySize, + parallelism = parallelism, + hashLengthInBytes = 32, + version = Argon2Version.V13, + ) + + return hashResult.rawHashAsByteArray() + } + /** * Store the encrypted database in the storage provider. * @param encryptedData The encrypted database as a base64 encoded string