vultisig / vultisig-android

Vultisig Android App
Apache License 2.0
4 stars 2 forks source link

BUG: Can't import encrypted backup vault generated by IOS #516

Closed johnnyluo closed 8 hours ago

johnnyluo commented 4 days ago

Describe the bug If I generated a backup vault with password protection , it can't be imported to android, also android generated backup with password protection can't be imported into IOS

To Reproduce Steps to reproduce the behavior:

  1. Backup a vault , with password protection
  2. Import the backup file in IOS app

Expected behavior Password protected vault backup can be imported on both Android and Ios

Additional context The encryption/decryption in android doesn't compatible with IOS. I asked copilot , encryption/decryption compatible with those used in IOS should look like this

import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import android.util.Base64

fun decrypt(data: String, password: String): ByteArray? {
    try {
        val keyBytes = MessageDigest.getInstance("SHA-256").digest(password.toByteArray(Charsets.UTF_8))
        val keySpec = SecretKeySpec(keyBytes, "AES")

        // Assuming data is Base64 encoded as AES/GCM/NoPadding produces binary output
        val cipherData = Base64.decode(data, Base64.DEFAULT)
        val cipher = Cipher.getInstance("AES/GCM/NoPadding")

        // Extract IV which is fixed size of 12 bytes
        val iv = cipherData.copyOfRange(0, 12)
        val encryptedData = cipherData.copyOfRange(12, cipherData.size)

        cipher.init(Cipher.DECRYPT_MODE, keySpec, GCMParameterSpec(128, iv))
        return cipher.doFinal(encryptedData)
    } catch (e: Exception) {
        println("Error decrypting data: ${e.localizedMessage}")
        return null
    }
}
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import android.util.Base64

fun encrypt(data: ByteArray, password: String): String? {
    try {
        val keyBytes = MessageDigest.getInstance("SHA-256").digest(password.toByteArray(Charsets.UTF_8))
        val keySpec = SecretKeySpec(keyBytes, "AES")

        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        val iv = ByteArray(12) // GCM recommended size
        java.security.SecureRandom().nextBytes(iv) // SecureRandom IV for GCM
        val parameterSpec = GCMParameterSpec(128, iv) // 128 bit auth tag length

        cipher.init(Cipher.ENCRYPT_MODE, keySpec, parameterSpec)
        val encryptedData = cipher.doFinal(data)

        // Prepend IV to the encrypted data to use it during decryption
        val combined = iv + encryptedData

        // Return as Base64 encoded string to easily store or transmit
        return Base64.encodeToString(combined, Base64.DEFAULT)
    } catch (e: Exception) {
        println("Error encrypting data: ${e.localizedMessage}")
        return null
    }
}
johnnyluo commented 4 days ago