mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
Create trusted device service (#1185)
This commit is contained in:
parent
b13c89b688
commit
0a65b37a65
8 changed files with 342 additions and 0 deletions
|
@ -27,6 +27,11 @@ interface AuthDiskSource {
|
|||
*/
|
||||
var rememberedOrgIdentifier: String?
|
||||
|
||||
/**
|
||||
* The currently persisted state indicating that the user has trusted this device.
|
||||
*/
|
||||
var shouldTrustDevice: Boolean
|
||||
|
||||
/**
|
||||
* The currently persisted user state information (or `null` if not set).
|
||||
*/
|
||||
|
@ -106,6 +111,16 @@ interface AuthDiskSource {
|
|||
*/
|
||||
fun storeUserAutoUnlockKey(userId: String, userAutoUnlockKey: String?)
|
||||
|
||||
/**
|
||||
* Gets the device key for the given [userId].
|
||||
*/
|
||||
fun getDeviceKey(userId: String): String?
|
||||
|
||||
/**
|
||||
* Stores the device key for the given [userId].
|
||||
*/
|
||||
fun storeDeviceKey(userId: String, deviceKey: String?)
|
||||
|
||||
/**
|
||||
* Gets the biometrics key for the given [userId].
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.util.UUID
|
|||
private const val ACCOUNT_TOKENS_KEY = "$ENCRYPTED_BASE_KEY:accountTokens"
|
||||
private const val BIOMETRICS_UNLOCK_KEY = "$ENCRYPTED_BASE_KEY:userKeyBiometricUnlock"
|
||||
private const val USER_AUTO_UNLOCK_KEY_KEY = "$ENCRYPTED_BASE_KEY:userKeyAutoUnlock"
|
||||
private const val DEVICE_KEY_KEY = "$ENCRYPTED_BASE_KEY:deviceKey"
|
||||
|
||||
private const val UNIQUE_APP_ID_KEY = "$BASE_KEY:appId"
|
||||
private const val REMEMBERED_EMAIL_ADDRESS_KEY = "$BASE_KEY:rememberedEmail"
|
||||
private const val REMEMBERED_ORG_IDENTIFIER_KEY = "$BASE_KEY:rememberedOrgIdentifier"
|
||||
|
@ -35,6 +37,7 @@ private const val ORGANIZATION_KEYS_KEY = "$BASE_KEY:encOrgKeys"
|
|||
private const val TWO_FACTOR_TOKEN_KEY = "$BASE_KEY:twoFactorToken"
|
||||
private const val MASTER_PASSWORD_HASH_KEY = "$BASE_KEY:keyHash"
|
||||
private const val POLICIES_KEY = "$BASE_KEY:policies"
|
||||
private const val SHOULD_TRUST_DEVICE_KEY = "$BASE_KEY:shouldTrustDevice"
|
||||
|
||||
/**
|
||||
* Primary implementation of [AuthDiskSource].
|
||||
|
@ -101,6 +104,12 @@ class AuthDiskSourceImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override var shouldTrustDevice: Boolean
|
||||
get() = requireNotNull(getBoolean(key = SHOULD_TRUST_DEVICE_KEY, default = false))
|
||||
set(value) {
|
||||
putBoolean(key = SHOULD_TRUST_DEVICE_KEY, value = value)
|
||||
}
|
||||
|
||||
override val userStateFlow: Flow<UserStateJson?>
|
||||
get() = mutableUserStateFlow
|
||||
.onSubscription { emit(userState) }
|
||||
|
@ -115,6 +124,7 @@ class AuthDiskSourceImpl(
|
|||
storePrivateKey(userId = userId, privateKey = null)
|
||||
storeOrganizationKeys(userId = userId, organizationKeys = null)
|
||||
storeOrganizations(userId = userId, organizations = null)
|
||||
storeDeviceKey(userId = userId, deviceKey = null)
|
||||
storeUserBiometricUnlockKey(userId = userId, biometricsKey = null)
|
||||
storeMasterPasswordHash(userId = userId, passwordHash = null)
|
||||
storePolicies(userId = userId, policies = null)
|
||||
|
@ -183,6 +193,14 @@ class AuthDiskSourceImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override fun getDeviceKey(
|
||||
userId: String,
|
||||
): String? = getEncryptedString(key = "${DEVICE_KEY_KEY}_$userId")
|
||||
|
||||
override fun storeDeviceKey(userId: String, deviceKey: String?) {
|
||||
putEncryptedString(key = "${DEVICE_KEY_KEY}_$userId", value = deviceKey)
|
||||
}
|
||||
|
||||
override fun getUserBiometricUnlockKey(userId: String): String? =
|
||||
getEncryptedString(key = "${BIOMETRICS_UNLOCK_KEY}_$userId")
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.x8bit.bitwarden.data.auth.manager
|
||||
|
||||
/**
|
||||
* Manager used to establish trust with this device.
|
||||
*/
|
||||
interface TrustedDeviceManager {
|
||||
/**
|
||||
* Establishes trust with this device if necessary.
|
||||
*/
|
||||
suspend fun trustThisDeviceIfNecessary(userId: String): Result<Boolean>
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.x8bit.bitwarden.data.auth.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.platform.util.flatMap
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
|
||||
/**
|
||||
* Default implementation of the [TrustedDeviceManager] used to establish trust with this device.
|
||||
*/
|
||||
class TrustedDeviceManagerImpl(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val devicesService: DevicesService,
|
||||
) : TrustedDeviceManager {
|
||||
override suspend fun trustThisDeviceIfNecessary(userId: String): Result<Boolean> =
|
||||
if (!authDiskSource.shouldTrustDevice) {
|
||||
false.asSuccess()
|
||||
} else {
|
||||
vaultSdkSource
|
||||
.getTrustDevice(userId = userId)
|
||||
.flatMap { trustedDevice ->
|
||||
devicesService
|
||||
.trustDevice(
|
||||
appId = authDiskSource.uniqueAppId,
|
||||
encryptedDevicePrivateKey = trustedDevice.protectedDevicePrivateKey,
|
||||
encryptedDevicePublicKey = trustedDevice.protectedDevicePublicKey,
|
||||
encryptedUserKey = trustedDevice.protectedUserKey,
|
||||
)
|
||||
.onSuccess {
|
||||
authDiskSource.storeDeviceKey(
|
||||
userId = userId,
|
||||
deviceKey = trustedDevice.deviceKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
.also { authDiskSource.shouldTrustDevice = false }
|
||||
.map { true }
|
||||
}
|
||||
}
|
|
@ -3,12 +3,15 @@ package com.x8bit.bitwarden.data.auth.manager.di
|
|||
import android.content.Context
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.AuthRequestsService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.NewAuthRequestService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
|
||||
import com.x8bit.bitwarden.data.auth.manager.AuthRequestManager
|
||||
import com.x8bit.bitwarden.data.auth.manager.AuthRequestManagerImpl
|
||||
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManager
|
||||
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManagerImpl
|
||||
import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
|
||||
import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManagerImpl
|
||||
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
|
||||
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
|
||||
|
@ -68,6 +71,19 @@ object AuthManagerModule {
|
|||
authDiskSource = authDiskSource,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTrustedDeviceManager(
|
||||
authDiskSource: AuthDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
devicesService: DevicesService,
|
||||
): TrustedDeviceManager =
|
||||
TrustedDeviceManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
devicesService = devicesService,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserLogoutManager(
|
||||
|
|
|
@ -120,6 +120,23 @@ class AuthDiskSourceTest {
|
|||
assertNull(authDiskSource.rememberedOrgIdentifier)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldTrustDevice should pull from and update SharedPreferences`() {
|
||||
val shouldTrustDeviceKey = "bwPreferencesStorage:shouldTrustDevice"
|
||||
|
||||
// Shared preferences and the disk source start with the same value.
|
||||
assertFalse(authDiskSource.shouldTrustDevice)
|
||||
assertFalse(fakeSharedPreferences.getBoolean(shouldTrustDeviceKey, false))
|
||||
|
||||
// Updating the disk source updates shared preferences
|
||||
authDiskSource.shouldTrustDevice = true
|
||||
assertTrue(fakeSharedPreferences.getBoolean(shouldTrustDeviceKey, false))
|
||||
|
||||
// Update SharedPreferences updates the disk source
|
||||
fakeSharedPreferences.edit { putBoolean(shouldTrustDeviceKey, false) }
|
||||
assertFalse(authDiskSource.shouldTrustDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `userState should pull from and update SharedPreferences`() {
|
||||
val userStateKey = "bwPreferencesStorage:state"
|
||||
|
@ -164,6 +181,10 @@ class AuthDiskSourceTest {
|
|||
fun `clearData should clear all necessary data for the given user`() {
|
||||
val userId = "userId"
|
||||
|
||||
authDiskSource.storeDeviceKey(
|
||||
userId = userId,
|
||||
deviceKey = "9876-5432-1234",
|
||||
)
|
||||
authDiskSource.storeUserBiometricUnlockKey(
|
||||
userId = userId,
|
||||
biometricsKey = "1234-9876-0192",
|
||||
|
@ -204,6 +225,7 @@ class AuthDiskSourceTest {
|
|||
|
||||
authDiskSource.clearData(userId = userId)
|
||||
|
||||
assertNull(authDiskSource.getDeviceKey(userId = userId))
|
||||
assertNull(authDiskSource.getUserBiometricUnlockKey(userId = userId))
|
||||
assertNull(authDiskSource.getLastActiveTimeMillis(userId = userId))
|
||||
assertNull(authDiskSource.getInvalidUnlockAttempts(userId = userId))
|
||||
|
@ -484,6 +506,42 @@ class AuthDiskSourceTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getDeviceKey should pull from SharedPreferences`() {
|
||||
val deviceKeyBaseKey = "bwSecureStorage:deviceKey"
|
||||
val mockUserId = "mockUserId"
|
||||
val deviceKeyKey = "${deviceKeyBaseKey}_$mockUserId"
|
||||
val devicesKey = "1234"
|
||||
fakeEncryptedSharedPreferences.edit { putString(deviceKeyKey, devicesKey) }
|
||||
val actual = authDiskSource.getDeviceKey(userId = mockUserId)
|
||||
assertEquals(devicesKey, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeDeviceKey for non-null values should update SharedPreferences`() {
|
||||
val deviceKeyBaseKey = "bwSecureStorage:deviceKey"
|
||||
val mockUserId = "mockUserId"
|
||||
val deviceKeyKey = "${deviceKeyBaseKey}_$mockUserId"
|
||||
val devicesKey = "1234"
|
||||
authDiskSource.storeDeviceKey(userId = mockUserId, deviceKey = devicesKey)
|
||||
val actual = fakeEncryptedSharedPreferences.getString(
|
||||
key = deviceKeyKey,
|
||||
defaultValue = null,
|
||||
)
|
||||
assertEquals(devicesKey, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeDeviceKey for null values should clear SharedPreferences`() {
|
||||
val deviceKeyBaseKey = "bwSecureStorage:deviceKey"
|
||||
val mockUserId = "mockUserId"
|
||||
val deviceKeyKey = "${deviceKeyBaseKey}_$mockUserId"
|
||||
val deviceKey = "1234"
|
||||
fakeEncryptedSharedPreferences.edit { putString(deviceKeyKey, deviceKey) }
|
||||
authDiskSource.storeDeviceKey(userId = mockUserId, deviceKey = null)
|
||||
assertFalse(fakeEncryptedSharedPreferences.contains(deviceKeyKey))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getUserBiometricUnlockKey should pull from SharedPreferences`() {
|
||||
val biometricsKeyBaseKey = "bwSecureStorage:userKeyBiometricUnlock"
|
||||
|
|
|
@ -37,10 +37,13 @@ class FakeAuthDiskSource : AuthDiskSource {
|
|||
mutableMapOf<String, List<SyncResponseJson.Profile.Organization>?>()
|
||||
private val storedOrganizationKeys = mutableMapOf<String, Map<String, String>?>()
|
||||
private val storedAccountTokens = mutableMapOf<String, AccountTokensJson?>()
|
||||
private val storedDeviceKey = mutableMapOf<String, String?>()
|
||||
private val storedBiometricKeys = mutableMapOf<String, String?>()
|
||||
private val storedMasterPasswordHashes = mutableMapOf<String, String?>()
|
||||
private val storedPolicies = mutableMapOf<String, List<SyncResponseJson.Policy>?>()
|
||||
|
||||
override var shouldTrustDevice: Boolean = false
|
||||
|
||||
override var userState: UserStateJson? = null
|
||||
set(value) {
|
||||
field = value
|
||||
|
@ -161,6 +164,12 @@ class FakeAuthDiskSource : AuthDiskSource {
|
|||
getMutableOrganizationsFlow(userId = userId).tryEmit(organizations)
|
||||
}
|
||||
|
||||
override fun getDeviceKey(userId: String): String? = storedDeviceKey[userId]
|
||||
|
||||
override fun storeDeviceKey(userId: String, deviceKey: String?) {
|
||||
storedDeviceKey[userId] = deviceKey
|
||||
}
|
||||
|
||||
override fun getUserBiometricUnlockKey(userId: String): String? =
|
||||
storedBiometricKeys[userId]
|
||||
|
||||
|
@ -273,6 +282,13 @@ class FakeAuthDiskSource : AuthDiskSource {
|
|||
assertEquals(organizationKeys, storedOrganizationKeys[userId])
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the [deviceKey] was stored successfully using the [userId].
|
||||
*/
|
||||
fun assertDeviceKey(userId: String, deviceKey: String?) {
|
||||
assertEquals(deviceKey, storedDeviceKey[userId])
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the [biometricsKey] was stored successfully using the [userId].
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
package com.x8bit.bitwarden.data.auth.manager
|
||||
|
||||
import com.bitwarden.crypto.TrustDeviceResponse
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceKeysResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
class TrustedDeviceManagerTests {
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val vaultSdkSource: VaultSdkSource = mockk()
|
||||
private val devicesService: DevicesService = mockk()
|
||||
|
||||
private val manager: TrustedDeviceManager = TrustedDeviceManagerImpl(
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
devicesService = devicesService,
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `trustThisDeviceIfNecessary when shouldTrustDevice false should return success with false`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
fakeAuthDiskSource.shouldTrustDevice = false
|
||||
|
||||
val result = manager.trustThisDeviceIfNecessary(userId = userId)
|
||||
|
||||
assertEquals(false.asSuccess(), result)
|
||||
coVerify(exactly = 0) {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
devicesService.trustDevice(
|
||||
appId = any(),
|
||||
encryptedUserKey = any(),
|
||||
encryptedDevicePublicKey = any(),
|
||||
encryptedDevicePrivateKey = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `trustThisDeviceIfNecessary when getTrustDevice fails should return failure`() = runTest {
|
||||
val userId = "userId"
|
||||
fakeAuthDiskSource.shouldTrustDevice = true
|
||||
val error = Throwable("Fail")
|
||||
coEvery {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
} returns error.asFailure()
|
||||
|
||||
val result = manager.trustThisDeviceIfNecessary(userId = userId)
|
||||
|
||||
assertEquals(error.asFailure(), result)
|
||||
assertFalse(fakeAuthDiskSource.shouldTrustDevice)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
}
|
||||
coVerify(exactly = 0) {
|
||||
devicesService.trustDevice(
|
||||
appId = any(),
|
||||
encryptedUserKey = any(),
|
||||
encryptedDevicePublicKey = any(),
|
||||
encryptedDevicePrivateKey = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `trustThisDeviceIfNecessary when trustDevice fails should return failure`() = runTest {
|
||||
val userId = "userId"
|
||||
val deviceKey = "deviceKey"
|
||||
val protectedUserKey = "protectedUserKey"
|
||||
val protectedDevicePrivateKey = "protectedDevicePrivateKey"
|
||||
val protectedDevicePublicKey = "protectedDevicePublicKey"
|
||||
val trustedDeviceResponse = TrustDeviceResponse(
|
||||
deviceKey = deviceKey,
|
||||
protectedUserKey = protectedUserKey,
|
||||
protectedDevicePrivateKey = protectedDevicePrivateKey,
|
||||
protectedDevicePublicKey = protectedDevicePublicKey,
|
||||
)
|
||||
val error = Throwable("Fail")
|
||||
fakeAuthDiskSource.shouldTrustDevice = true
|
||||
coEvery {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
} returns trustedDeviceResponse.asSuccess()
|
||||
coEvery {
|
||||
devicesService.trustDevice(
|
||||
appId = "testUniqueAppId",
|
||||
encryptedUserKey = protectedUserKey,
|
||||
encryptedDevicePublicKey = protectedDevicePublicKey,
|
||||
encryptedDevicePrivateKey = protectedDevicePrivateKey,
|
||||
)
|
||||
} returns error.asFailure()
|
||||
|
||||
val result = manager.trustThisDeviceIfNecessary(userId = userId)
|
||||
|
||||
assertEquals(error.asFailure(), result)
|
||||
assertFalse(fakeAuthDiskSource.shouldTrustDevice)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
devicesService.trustDevice(
|
||||
appId = "testUniqueAppId",
|
||||
encryptedUserKey = protectedUserKey,
|
||||
encryptedDevicePublicKey = protectedDevicePublicKey,
|
||||
encryptedDevicePrivateKey = protectedDevicePrivateKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `trustThisDeviceIfNecessary when success should return success with true`() = runTest {
|
||||
val userId = "userId"
|
||||
val deviceKey = "deviceKey"
|
||||
val protectedUserKey = "protectedUserKey"
|
||||
val protectedDevicePrivateKey = "protectedDevicePrivateKey"
|
||||
val protectedDevicePublicKey = "protectedDevicePublicKey"
|
||||
val trustedDeviceResponse = TrustDeviceResponse(
|
||||
deviceKey = deviceKey,
|
||||
protectedUserKey = protectedUserKey,
|
||||
protectedDevicePrivateKey = protectedDevicePrivateKey,
|
||||
protectedDevicePublicKey = protectedDevicePublicKey,
|
||||
)
|
||||
val trustedDeviceKeysResponseJson = TrustedDeviceKeysResponseJson(
|
||||
id = "id",
|
||||
name = "name",
|
||||
identifier = "identifier",
|
||||
type = 0,
|
||||
creationDate = ZonedDateTime.parse("2024-09-13T01:00:00.00Z"),
|
||||
)
|
||||
fakeAuthDiskSource.shouldTrustDevice = true
|
||||
coEvery {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
} returns trustedDeviceResponse.asSuccess()
|
||||
coEvery {
|
||||
devicesService.trustDevice(
|
||||
appId = "testUniqueAppId",
|
||||
encryptedUserKey = protectedUserKey,
|
||||
encryptedDevicePublicKey = protectedDevicePublicKey,
|
||||
encryptedDevicePrivateKey = protectedDevicePrivateKey,
|
||||
)
|
||||
} returns trustedDeviceKeysResponseJson.asSuccess()
|
||||
|
||||
val result = manager.trustThisDeviceIfNecessary(userId = userId)
|
||||
|
||||
assertEquals(true.asSuccess(), result)
|
||||
fakeAuthDiskSource.assertDeviceKey(userId = userId, deviceKey = deviceKey)
|
||||
assertFalse(fakeAuthDiskSource.shouldTrustDevice)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.getTrustDevice(userId = userId)
|
||||
devicesService.trustDevice(
|
||||
appId = "testUniqueAppId",
|
||||
encryptedUserKey = protectedUserKey,
|
||||
encryptedDevicePublicKey = protectedDevicePublicKey,
|
||||
encryptedDevicePrivateKey = protectedDevicePrivateKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue