diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/CryptoConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/CryptoConstants.kt index 37b9ac379e285f9814f675d72b8525f40c92cd78..3c530ee06fd79a46e5059179a97a72f86595fd36 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/CryptoConstants.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/CryptoConstants.kt @@ -31,6 +31,8 @@ const val MXCRYPTO_ALGORITHM_MEGOLM = "m.megolm.v1.aes-sha2" */ const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-sha2" +const val BCRYPT_ALGORITHM_BACKUP = "m.bcrypt" + /** * Secured Shared Storage algorithm constant. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt index 0d40490c3e6858a290d21c0937d828cb687ce1b1..8cdd69f7ff92cfd8974cafbf8a8e1f4ea30a12b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt @@ -118,6 +118,9 @@ interface KeysBackupService { progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) + + fun prepareBcryptKeysBackupVersion(userName:String, password: String, + callback: MatrixCallback<MegolmBackupCreationInfo>) /** * Delete a keys backup version. It will delete all backed up keys on the server, and the backup itself. * If we are backing up to this version. Backup will be stopped. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/BCryptManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/BCryptManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..378cc2457917c3c3d63dd3f798dd29e85fb24db0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/BCryptManager.kt @@ -0,0 +1,27 @@ +package org.matrix.android.sdk.internal.crypto.keysbackup + +import at.favre.lib.crypto.bcrypt.BCrypt +import java.security.MessageDigest + +internal object BCryptManager { + + private const val iterations = 14 + private const val saltLength = 16 + + + fun generateBcryptPrivateKeyWithPassword( + userName: String, + password: String + ): GeneratePrivateKeyResult { + val salt = userName.sha256().substring(0, saltLength).toByteArray() + val privateKey = BCrypt.withDefaults().hash(iterations, salt, password.toByteArray()) + return GeneratePrivateKeyResult(privateKey, salt.toString(), iterations) + } + + + private fun String.sha256(): String = MessageDigest + .getInstance("SHA-256") + .digest(this.toByteArray()) + .fold("") { str, it -> str + "%02x".format(it) } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 5ea4695da29b4bcf73a372cc338c6b88909db9b2..4dabc4d7352551a8e59dd55dfb54d5e62021fcd0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.crypto.BCRYPT_ALGORITHM_BACKUP import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError @@ -233,6 +234,57 @@ internal class DefaultKeysBackupService @Inject constructor( } } + override fun prepareBcryptKeysBackupVersion(userName:String, password: String, + callback: MatrixCallback<MegolmBackupCreationInfo>) { + cryptoCoroutineScope.launch(coroutineDispatchers.io) { + try { + val generatePrivateKeyResult = BCryptManager.generateBcryptPrivateKeyWithPassword(userName,password) + val signalableBackupAuthData = SignalableMegolmBackupAuthData( + publicKey = generatePrivateKeyResult.privateKey.toString(), + privateKeySalt = generatePrivateKeyResult.salt, + privateKeyIterations = generatePrivateKeyResult.iterations + ) + + val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableBackupAuthData.signalableJSONDictionary()) + + val signatures = mutableMapOf<String, MutableMap<String, String>>() + + val deviceSignature = objectSigner.signObject(canonicalJson) + deviceSignature.forEach { (userID, content) -> + signatures[userID] = content.toMutableMap() + } + + // If we have cross signing add signature, will throw if cross signing not properly configured + try { + val crossSign = crossSigningOlm.signObject(CrossSigningOlm.KeyType.MASTER, canonicalJson) + signatures[credentials.userId]?.putAll(crossSign) + } catch (failure: Throwable) { + // ignore and log + Timber.w(failure, "prepareKeysBackupVersion: failed to sign with cross signing keys") + } + + val signedBackupAuthData = MegolmBackupAuthData( + publicKey = signalableBackupAuthData.publicKey, + privateKeySalt = signalableBackupAuthData.privateKeySalt, + privateKeyIterations = signalableBackupAuthData.privateKeyIterations, + signatures = signatures + ) + val creationInfo = MegolmBackupCreationInfo( + algorithm = BCRYPT_ALGORITHM_BACKUP, + authData = signedBackupAuthData, + recoveryKey = generatePrivateKeyResult.privateKey.toString() + ) + uiHandler.post { + callback.onSuccess(creationInfo) + } + } catch (failure: Throwable) { + uiHandler.post { + callback.onFailure(failure) + } + } + } + } + override fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo, callback: MatrixCallback<KeysVersion>) { @Suppress("UNCHECKED_CAST")