mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Make initialize cross signing as a task
This commit is contained in:
parent
eb50256af7
commit
9e3011d4c8
@ -66,6 +66,7 @@ import im.vector.matrix.android.internal.crypto.tasks.DefaultDownloadKeysForUser
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultEncryptEventTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultGetDeviceInfoTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultGetDevicesTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultInitializeCrossSigningTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultSendToDeviceTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultSendVerificationMessageTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultSetDeviceNameTask
|
||||
@ -78,6 +79,7 @@ import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.EncryptEventTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.GetDeviceInfoTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.InitializeCrossSigningTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendVerificationMessageTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
|
||||
@ -245,4 +247,7 @@ internal abstract class CryptoModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindComputeShieldTrustTask(task: DefaultComputeTrustTask): ComputeTrustTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindInitializeCrossSigningTask(task: DefaultInitializeCrossSigningTask): InitializeCrossSigningTask
|
||||
}
|
||||
|
@ -17,22 +17,17 @@
|
||||
package im.vector.matrix.android.internal.crypto.crosssigning
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import dagger.Lazy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||
import im.vector.matrix.android.internal.crypto.model.KeyUsage
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UploadSignatureQueryBuilder
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
|
||||
import im.vector.matrix.android.internal.crypto.tasks.InitializeCrossSigningTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.UploadSignaturesTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.UploadSigningKeysTask
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
@ -53,12 +48,9 @@ import javax.inject.Inject
|
||||
internal class DefaultCrossSigningService @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val uploadSigningKeysTask: UploadSigningKeysTask,
|
||||
private val initializeCrossSigningTask: InitializeCrossSigningTask,
|
||||
private val uploadSignaturesTask: UploadSignaturesTask,
|
||||
private val computeTrustTask: ComputeTrustTask,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
@ -151,153 +143,31 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||
override fun initializeCrossSigning(authParams: UserPasswordAuth?, callback: MatrixCallback<Unit>?) {
|
||||
Timber.d("## CrossSigning initializeCrossSigning")
|
||||
|
||||
// =================
|
||||
// MASTER KEY
|
||||
// =================
|
||||
val masterPkOlm = OlmPkSigning()
|
||||
val masterKeyPrivateKey = OlmPkSigning.generateSeed()
|
||||
val masterPublicKey = masterPkOlm.initWithSeed(masterKeyPrivateKey)
|
||||
|
||||
Timber.v("## CrossSigning - masterPublicKey:$masterPublicKey")
|
||||
|
||||
// =================
|
||||
// USER KEY
|
||||
// =================
|
||||
val userSigningPkOlm = OlmPkSigning()
|
||||
val uskPrivateKey = OlmPkSigning.generateSeed()
|
||||
val uskPublicKey = userSigningPkOlm.initWithSeed(uskPrivateKey)
|
||||
|
||||
Timber.v("## CrossSigning - uskPublicKey:$uskPublicKey")
|
||||
|
||||
// Sign userSigningKey with master
|
||||
val signedUSK = CryptoCrossSigningKey.Builder(userId, KeyUsage.USER_SIGNING)
|
||||
.key(uskPublicKey)
|
||||
.build()
|
||||
.canonicalSignable()
|
||||
.let { masterPkOlm.sign(it) }
|
||||
|
||||
// =================
|
||||
// SELF SIGNING KEY
|
||||
// =================
|
||||
val selfSigningPkOlm = OlmPkSigning()
|
||||
val sskPrivateKey = OlmPkSigning.generateSeed()
|
||||
val sskPublicKey = selfSigningPkOlm.initWithSeed(sskPrivateKey)
|
||||
|
||||
Timber.v("## CrossSigning - sskPublicKey:$sskPublicKey")
|
||||
|
||||
// Sign userSigningKey with master
|
||||
val signedSSK = JsonCanonicalizer.getCanonicalJson(Map::class.java, CryptoCrossSigningKey.Builder(userId, KeyUsage.SELF_SIGNING)
|
||||
.key(sskPublicKey)
|
||||
.build().signalableJSONDictionary()).let { masterPkOlm.sign(it) }
|
||||
|
||||
// I need to upload the keys
|
||||
val mskCrossSigningKeyInfo = CryptoCrossSigningKey.Builder(userId, KeyUsage.MASTER)
|
||||
.key(masterPublicKey)
|
||||
.build()
|
||||
val params = UploadSigningKeysTask.Params(
|
||||
masterKey = mskCrossSigningKeyInfo,
|
||||
userKey = CryptoCrossSigningKey.Builder(userId, KeyUsage.USER_SIGNING)
|
||||
.key(uskPublicKey)
|
||||
.signature(userId, masterPublicKey, signedUSK)
|
||||
.build(),
|
||||
selfSignedKey = CryptoCrossSigningKey.Builder(userId, KeyUsage.SELF_SIGNING)
|
||||
.key(sskPublicKey)
|
||||
.signature(userId, masterPublicKey, signedSSK)
|
||||
.build(),
|
||||
userPasswordAuth = authParams
|
||||
val params = InitializeCrossSigningTask.Params(
|
||||
authParams = authParams
|
||||
)
|
||||
|
||||
this.masterPkSigning = masterPkOlm
|
||||
this.userPkSigning = userSigningPkOlm
|
||||
this.selfSigningPkSigning = selfSigningPkOlm
|
||||
|
||||
val crossSigningInfo = MXCrossSigningInfo(userId, listOf(params.masterKey, params.userKey, params.selfSignedKey))
|
||||
cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
|
||||
setUserKeysAsTrusted(userId, true)
|
||||
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey?.toBase64NoPadding(), uskPrivateKey?.toBase64NoPadding(), sskPrivateKey?.toBase64NoPadding())
|
||||
|
||||
uploadSigningKeysTask.configureWith(params) {
|
||||
this.executionThread = TaskThread.CRYPTO
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.i("## CrossSigning - Keys successfully uploaded")
|
||||
|
||||
// Sign the current device with SSK
|
||||
val uploadSignatureQueryBuilder = UploadSignatureQueryBuilder()
|
||||
|
||||
val myDevice = myDeviceInfoHolder.get().myDevice
|
||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary())
|
||||
val signedDevice = selfSigningPkOlm.sign(canonicalJson)
|
||||
val updateSignatures = (myDevice.signatures?.toMutableMap() ?: HashMap())
|
||||
.also {
|
||||
it[userId] = (it[userId]
|
||||
?: HashMap()) + mapOf("ed25519:$sskPublicKey" to signedDevice)
|
||||
}
|
||||
myDevice.copy(signatures = updateSignatures).let {
|
||||
uploadSignatureQueryBuilder.withDeviceInfo(it)
|
||||
}
|
||||
|
||||
// sign MSK with device key (migration) and upload signatures
|
||||
val message = JsonCanonicalizer.getCanonicalJson(Map::class.java, mskCrossSigningKeyInfo.signalableJSONDictionary())
|
||||
olmDevice.signMessage(message)?.let { sign ->
|
||||
val mskUpdatedSignatures = (mskCrossSigningKeyInfo.signatures?.toMutableMap()
|
||||
?: HashMap()).also {
|
||||
it[userId] = (it[userId]
|
||||
?: HashMap()) + mapOf("ed25519:${myDevice.deviceId}" to sign)
|
||||
}
|
||||
mskCrossSigningKeyInfo.copy(
|
||||
signatures = mskUpdatedSignatures
|
||||
).let {
|
||||
uploadSignatureQueryBuilder.withSigningKeyInfo(it)
|
||||
}
|
||||
}
|
||||
|
||||
resetTrustOnKeyChange()
|
||||
uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadSignatureQueryBuilder.build())) {
|
||||
// this.retryCount = 3
|
||||
this.executionThread = TaskThread.CRYPTO
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.i("## CrossSigning - signatures successfully uploaded")
|
||||
callback?.onSuccess(Unit)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// Clear
|
||||
Timber.e(failure, "## CrossSigning - Failed to upload signatures")
|
||||
clearSigningKeys()
|
||||
}
|
||||
}
|
||||
}.executeBy(taskExecutor)
|
||||
initializeCrossSigningTask.configureWith(params) {
|
||||
this.callbackThread = TaskThread.CRYPTO
|
||||
this.callback = object : MatrixCallback<InitializeCrossSigningTask.Result> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## CrossSigning - Failed to upload signing keys")
|
||||
clearSigningKeys()
|
||||
callback?.onFailure(failure)
|
||||
override fun onSuccess(data: InitializeCrossSigningTask.Result) {
|
||||
val crossSigningInfo = MXCrossSigningInfo(userId, listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo))
|
||||
cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
|
||||
setUserKeysAsTrusted(userId, true)
|
||||
cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK)
|
||||
masterPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
|
||||
userPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
|
||||
selfSigningPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
|
||||
|
||||
callback?.onSuccess(Unit)
|
||||
}
|
||||
}
|
||||
}.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
private fun clearSigningKeys() {
|
||||
masterPkSigning?.releaseSigning()
|
||||
userPkSigning?.releaseSigning()
|
||||
selfSigningPkSigning?.releaseSigning()
|
||||
|
||||
masterPkSigning = null
|
||||
userPkSigning = null
|
||||
selfSigningPkSigning = null
|
||||
|
||||
cryptoStore.setMyCrossSigningInfo(null)
|
||||
cryptoStore.storePrivateKeysInfo(null, null, null)
|
||||
}
|
||||
|
||||
private fun resetTrustOnKeyChange() {
|
||||
Timber.i("## CrossSigning - Clear all other user trust")
|
||||
cryptoStore.clearOtherUserTrust()
|
||||
}
|
||||
|
||||
override fun onSecretSSKGossip(sskPrivateKey: String) {
|
||||
Timber.i("## CrossSigning - onSecretSSKGossip")
|
||||
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
|
||||
|
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import dagger.Lazy
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.canonicalSignable
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||
import im.vector.matrix.android.internal.crypto.model.KeyUsage
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UploadSignatureQueryBuilder
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||
import org.matrix.olm.OlmPkSigning
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface InitializeCrossSigningTask : Task<InitializeCrossSigningTask.Params, InitializeCrossSigningTask.Result> {
|
||||
data class Params(
|
||||
val authParams: UserPasswordAuth?
|
||||
)
|
||||
|
||||
data class Result(
|
||||
val masterKeyPK: String,
|
||||
val userKeyPK: String,
|
||||
val selfSigningKeyPK: String,
|
||||
val masterKeyInfo: CryptoCrossSigningKey,
|
||||
val userKeyInfo: CryptoCrossSigningKey,
|
||||
val selfSignedKeyInfo: CryptoCrossSigningKey
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultInitializeCrossSigningTask @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
||||
private val uploadSigningKeysTask: UploadSigningKeysTask,
|
||||
private val uploadSignaturesTask: UploadSignaturesTask
|
||||
) : InitializeCrossSigningTask {
|
||||
|
||||
override suspend fun execute(params: InitializeCrossSigningTask.Params): InitializeCrossSigningTask.Result {
|
||||
var masterPkOlm: OlmPkSigning? = null
|
||||
var userSigningPkOlm: OlmPkSigning? = null
|
||||
var selfSigningPkOlm: OlmPkSigning? = null
|
||||
|
||||
try {
|
||||
// =================
|
||||
// MASTER KEY
|
||||
// =================
|
||||
|
||||
masterPkOlm = OlmPkSigning()
|
||||
val masterKeyPrivateKey = OlmPkSigning.generateSeed()
|
||||
val masterPublicKey = masterPkOlm.initWithSeed(masterKeyPrivateKey)
|
||||
|
||||
Timber.v("## CrossSigning - masterPublicKey:$masterPublicKey")
|
||||
|
||||
// =================
|
||||
// USER KEY
|
||||
// =================
|
||||
userSigningPkOlm = OlmPkSigning()
|
||||
val uskPrivateKey = OlmPkSigning.generateSeed()
|
||||
val uskPublicKey = userSigningPkOlm.initWithSeed(uskPrivateKey)
|
||||
|
||||
Timber.v("## CrossSigning - uskPublicKey:$uskPublicKey")
|
||||
|
||||
// Sign userSigningKey with master
|
||||
val signedUSK = CryptoCrossSigningKey.Builder(userId, KeyUsage.USER_SIGNING)
|
||||
.key(uskPublicKey)
|
||||
.build()
|
||||
.canonicalSignable()
|
||||
.let { masterPkOlm.sign(it) }
|
||||
|
||||
// =================
|
||||
// SELF SIGNING KEY
|
||||
// =================
|
||||
selfSigningPkOlm = OlmPkSigning()
|
||||
val sskPrivateKey = OlmPkSigning.generateSeed()
|
||||
val sskPublicKey = selfSigningPkOlm.initWithSeed(sskPrivateKey)
|
||||
|
||||
Timber.v("## CrossSigning - sskPublicKey:$sskPublicKey")
|
||||
|
||||
// Sign userSigningKey with master
|
||||
val signedSSK = JsonCanonicalizer.getCanonicalJson(Map::class.java, CryptoCrossSigningKey.Builder(userId, KeyUsage.SELF_SIGNING)
|
||||
.key(sskPublicKey)
|
||||
.build().signalableJSONDictionary()).let { masterPkOlm.sign(it) }
|
||||
|
||||
// I need to upload the keys
|
||||
val mskCrossSigningKeyInfo = CryptoCrossSigningKey.Builder(userId, KeyUsage.MASTER)
|
||||
.key(masterPublicKey)
|
||||
.build()
|
||||
val uploadSigningKeysParams = UploadSigningKeysTask.Params(
|
||||
masterKey = mskCrossSigningKeyInfo,
|
||||
userKey = CryptoCrossSigningKey.Builder(userId, KeyUsage.USER_SIGNING)
|
||||
.key(uskPublicKey)
|
||||
.signature(userId, masterPublicKey, signedUSK)
|
||||
.build(),
|
||||
selfSignedKey = CryptoCrossSigningKey.Builder(userId, KeyUsage.SELF_SIGNING)
|
||||
.key(sskPublicKey)
|
||||
.signature(userId, masterPublicKey, signedSSK)
|
||||
.build(),
|
||||
userPasswordAuth = params.authParams
|
||||
)
|
||||
|
||||
uploadSigningKeysTask.execute(uploadSigningKeysParams)
|
||||
|
||||
// Sign the current device with SSK
|
||||
val uploadSignatureQueryBuilder = UploadSignatureQueryBuilder()
|
||||
|
||||
val myDevice = myDeviceInfoHolder.get().myDevice
|
||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary())
|
||||
val signedDevice = selfSigningPkOlm.sign(canonicalJson)
|
||||
val updateSignatures = (myDevice.signatures?.toMutableMap() ?: HashMap())
|
||||
.also {
|
||||
it[userId] = (it[userId]
|
||||
?: HashMap()) + mapOf("ed25519:$sskPublicKey" to signedDevice)
|
||||
}
|
||||
myDevice.copy(signatures = updateSignatures).let {
|
||||
uploadSignatureQueryBuilder.withDeviceInfo(it)
|
||||
}
|
||||
|
||||
// sign MSK with device key (migration) and upload signatures
|
||||
val message = JsonCanonicalizer.getCanonicalJson(Map::class.java, mskCrossSigningKeyInfo.signalableJSONDictionary())
|
||||
olmDevice.signMessage(message)?.let { sign ->
|
||||
val mskUpdatedSignatures = (mskCrossSigningKeyInfo.signatures?.toMutableMap()
|
||||
?: HashMap()).also {
|
||||
it[userId] = (it[userId]
|
||||
?: HashMap()) + mapOf("ed25519:${myDevice.deviceId}" to sign)
|
||||
}
|
||||
mskCrossSigningKeyInfo.copy(
|
||||
signatures = mskUpdatedSignatures
|
||||
).let {
|
||||
uploadSignatureQueryBuilder.withSigningKeyInfo(it)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO should we ignore failure of that?
|
||||
uploadSignaturesTask.execute(UploadSignaturesTask.Params(uploadSignatureQueryBuilder.build()))
|
||||
|
||||
return InitializeCrossSigningTask.Result(
|
||||
masterKeyPK = masterKeyPrivateKey.toBase64NoPadding(),
|
||||
userKeyPK = uskPrivateKey.toBase64NoPadding(),
|
||||
selfSigningKeyPK = sskPrivateKey.toBase64NoPadding(),
|
||||
masterKeyInfo = uploadSigningKeysParams.masterKey,
|
||||
userKeyInfo = uploadSigningKeysParams.userKey,
|
||||
selfSignedKeyInfo = uploadSigningKeysParams.selfSignedKey
|
||||
)
|
||||
} finally {
|
||||
masterPkOlm?.releaseSigning()
|
||||
userSigningPkOlm?.releaseSigning()
|
||||
selfSigningPkOlm?.releaseSigning()
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.tools
|
||||
|
||||
import org.matrix.olm.OlmPkDecryption
|
||||
import org.matrix.olm.OlmPkEncryption
|
||||
import org.matrix.olm.OlmPkSigning
|
||||
|
||||
fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
|
||||
val olmPkEncryption = OlmPkEncryption()
|
||||
@ -36,3 +37,12 @@ fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T {
|
||||
olmPkDecryption.releaseDecryption()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T {
|
||||
val olmPkSigning = OlmPkSigning()
|
||||
try {
|
||||
return block(olmPkSigning)
|
||||
} finally {
|
||||
olmPkSigning.releaseSigning()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user