diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt index 02c3e96658..f803011415 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt @@ -54,25 +54,28 @@ data class SecretStorageKeyContent( /** Currently support m.secret_storage.v1.curve25519-aes-sha2 */ @Json(name = "algorithm") val algorithm: String? = null, @Json(name = "name") val name: String? = null, - @Json(name = "passphrase") val passphrase: SSSSPassphrase? = null, + @Json(name = "passphrase") val passphrase: SsssPassphrase? = null, @Json(name = "pubkey") val publicKey: String? = null, - @Json(name = "signatures") - var signatures: Map>? = null + @Json(name = "signatures") val signatures: Map>? = null ) { private fun signalableJSONDictionary(): Map { - val map = HashMap() - algorithm?.let { map["algorithm"] = it } - name?.let { map["name"] = it } - publicKey?.let { map["pubkey"] = it } - passphrase?.let { ssspp -> - map["passphrase"] = mapOf( - "algorithm" to ssspp.algorithm, - "iterations" to ssspp.salt, - "salt" to ssspp.salt - ) + return mutableMapOf().apply { + algorithm + ?.let { this["algorithm"] = it } + name + ?.let { this["name"] = it } + publicKey + ?.let { this["pubkey"] = it } + passphrase + ?.let { ssssPassphrase -> + this["passphrase"] = mapOf( + "algorithm" to ssssPassphrase.algorithm, + "iterations" to ssssPassphrase.salt, + "salt" to ssssPassphrase.salt + ) + } } - return map } fun canonicalSignable(): String { @@ -93,7 +96,7 @@ data class SecretStorageKeyContent( } @JsonClass(generateAdapter = true) -data class SSSSPassphrase( +data class SsssPassphrase( @Json(name = "algorithm") val algorithm: String?, @Json(name = "iterations") val iterations: Int, @Json(name = "salt") val salt: String? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt index f882375e5c..e1a216ab37 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.api.session.securestorage sealed class SharedSecretStorageError(message: String?) : Throwable(message) { - data class UnknownSecret(val secretName: String) : SharedSecretStorageError("Unknown Secret $secretName") data class UnknownKey(val keyId: String) : SharedSecretStorageError("Unknown key $keyId") data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt index 02ccc11026..35579f756c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt @@ -108,5 +108,5 @@ interface SharedSecretStorageService { * */ @Throws - fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback) + fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt index 9e61f7f8ff..90dcb92449 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt @@ -21,11 +21,11 @@ import im.vector.matrix.android.internal.crypto.keysbackup.deriveKey import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey /** Tag class */ -interface SSSSKeySpec +interface SsssKeySpec data class Curve25519AesSha2KeySpec( val privateKey: ByteArray -) : SSSSKeySpec { +) : SsssKeySpec { companion object { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt index 37b29047fc..4bc68f86c2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt @@ -25,8 +25,8 @@ import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent import im.vector.matrix.android.api.session.securestorage.KeyInfo import im.vector.matrix.android.api.session.securestorage.KeyInfoResult import im.vector.matrix.android.api.session.securestorage.KeySigner -import im.vector.matrix.android.api.session.securestorage.SSSSKeySpec -import im.vector.matrix.android.api.session.securestorage.SSSSPassphrase +import im.vector.matrix.android.api.session.securestorage.SsssKeySpec +import im.vector.matrix.android.api.session.securestorage.SsssPassphrase import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageError import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService @@ -34,15 +34,21 @@ import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2 import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWithPassword import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey +import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption import im.vector.matrix.android.internal.crypto.tools.withOlmEncryption import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import org.matrix.olm.OlmPkDecryption import org.matrix.olm.OlmPkMessage import javax.inject.Inject +private data class Key( + val publicKey: String, + @Suppress("ArrayInDataClass") + val privateKey: ByteArray +) + internal class DefaultSharedSecretStorageService @Inject constructor( private val accountDataService: AccountDataService, private val coroutineDispatchers: MatrixCoroutineDispatchers, @@ -54,25 +60,22 @@ internal class DefaultSharedSecretStorageService @Inject constructor( keySigner: KeySigner, callback: MatrixCallback) { cryptoCoroutineScope.launch(coroutineDispatchers.main) { - val pkDecryption = OlmPkDecryption() - val pubKey: String - val privateKey: ByteArray - try { - pubKey = pkDecryption.generateKey() - privateKey = pkDecryption.privateKey() - } catch (failure: Throwable) { - return@launch Unit.also { - callback.onFailure(failure) + val key = try { + withOlmDecryption { olmPkDecryption -> + val pubKey = olmPkDecryption.generateKey() + val privateKey = olmPkDecryption.privateKey() + Key(pubKey, privateKey) } - } finally { - pkDecryption.releaseDecryption() + } catch (failure: Throwable) { + callback.onFailure(failure) + return@launch } val storageKeyContent = SecretStorageKeyContent( name = keyName, algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2, passphrase = null, - publicKey = pubKey + publicKey = key.publicKey ) val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let { @@ -93,7 +96,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor( callback.onSuccess(SsssKeyCreationInfo( keyId = keyId, content = storageKeyContent, - recoveryKey = computeRecoveryKey(privateKey) + recoveryKey = computeRecoveryKey(key.privateKey) )) } } @@ -110,21 +113,18 @@ internal class DefaultSharedSecretStorageService @Inject constructor( cryptoCoroutineScope.launch(coroutineDispatchers.main) { val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener) - val pkDecryption = OlmPkDecryption() - val pubKey: String - try { - pubKey = pkDecryption.setPrivateKey(privatePart.privateKey) - } catch (failure: Throwable) { - return@launch Unit.also { - callback.onFailure(failure) + val pubKey = try { + withOlmDecryption { olmPkDecryption -> + olmPkDecryption.setPrivateKey(privatePart.privateKey) } - } finally { - pkDecryption.releaseDecryption() + } catch (failure: Throwable) { + callback.onFailure(failure) + return@launch } val storageKeyContent = SecretStorageKeyContent( algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2, - passphrase = SSSSPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt), + passphrase = SsssPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt), publicKey = pubKey ) @@ -192,10 +192,9 @@ internal class DefaultSharedSecretStorageService @Inject constructor( cryptoCoroutineScope.launch(coroutineDispatchers.main) { val encryptedContents = HashMap() try { - if (keys == null || keys.isEmpty()) { + if (keys.isNullOrEmpty()) { // use default key - val key = getDefaultKey() - when (key) { + when (val key = getDefaultKey()) { is KeyInfoResult.Success -> { if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) { val encryptedResult = withOlmEncryption { olmEncrypt -> @@ -222,8 +221,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor( keys.forEach { val keyId = it // encrypt the content - val key = getKey(keyId) - when (key) { + when (val key = getKey(keyId)) { is KeyInfoResult.Success -> { if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) { val encryptedResult = withOlmEncryption { olmEncrypt -> @@ -279,7 +277,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor( return results } - override fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback) { + override fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback) { val accountData = accountDataService.getAccountDataEvent(name) ?: return Unit.also { callback.onFailure(SharedSecretStorageError.UnknownSecret(name)) } @@ -306,20 +304,16 @@ internal class DefaultSharedSecretStorageService @Inject constructor( } cryptoCoroutineScope.launch(coroutineDispatchers.main) { kotlin.runCatching { - // decryt from recovery key - val keyBytes = keySpec.privateKey - val decryption = OlmPkDecryption() - try { - decryption.setPrivateKey(keyBytes) - decryption.decrypt(OlmPkMessage().apply { - mCipherText = secretContent.ciphertext - mEphemeralKey = secretContent.ephemeral - mMac = secretContent.mac - }) - } catch (failure: Throwable) { - throw failure - } finally { - decryption.releaseDecryption() + // decrypt from recovery key + withOlmDecryption { olmPkDecryption -> + olmPkDecryption.setPrivateKey(keySpec.privateKey) + olmPkDecryption.decrypt(OlmPkMessage() + .apply { + mCipherText = secretContent.ciphertext + mEphemeralKey = secretContent.ephemeral + mMac = secretContent.mac + } + ) } }.foldToCallback(callback) }