com.virgilsecurity.keyknox.storage.SyncKeyStorage.kt Maven / Gradle / Ivy
/*
* Copyright (c) 2015-2020, Virgil Security, Inc.
*
* Lead Maintainer: Virgil Security Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* (3) Neither the name of virgil nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.virgilsecurity.keyknox.storage
import com.virgilsecurity.keyknox.cloud.CloudKeyStorage
import com.virgilsecurity.keyknox.cloud.CloudKeyStorageProtocol
import com.virgilsecurity.keyknox.exception.*
import com.virgilsecurity.sdk.crypto.VirgilCrypto
import com.virgilsecurity.sdk.crypto.VirgilPrivateKey
import com.virgilsecurity.sdk.crypto.VirgilPublicKey
import com.virgilsecurity.sdk.jwt.contract.AccessTokenProvider
import com.virgilsecurity.sdk.storage.DefaultKeyStorage
import com.virgilsecurity.sdk.storage.KeyEntry
import com.virgilsecurity.sdk.storage.KeyStorage
/**
* Class responsible for synchronization between Keychain and Keyknox Cloud.
*/
class SyncKeyStorage {
val identity: String
val cloudKeyStorage: CloudKeyStorageProtocol
private var keyStorage: KeyStorageWrapper
private val keychainUtils = KeychainUtils()
/**
* @param identity User's identity to separate keys in Keychain.
* @param keyStorage KeychainStorageProtocol implementation.
* @param cloudKeyStorage CloudKeyStorageProtocol implementation.
*/
constructor(identity: String, keyStorage: KeyStorage,
cloudKeyStorage: CloudKeyStorageProtocol) {
this.identity = identity
this.keyStorage = KeyStorageWrapper(identity, keyStorage)
this.cloudKeyStorage = cloudKeyStorage
}
/**
* @param identity User's identity to separate keys in Keychain.
* @param cloudKeyStorage CloudKeyStorageProtocol implementation.
*/
constructor(identity: String, cloudKeyStorage: CloudKeyStorageProtocol) {
this.identity = identity
this.cloudKeyStorage = cloudKeyStorage
this.keyStorage = KeyStorageWrapper(identity, DefaultKeyStorage())
}
/**
* @param identity User's identity to separate keys in Keychain.
* @param accessTokenProvider AccessTokenProvider implementation.
* @param crypto Crypto
* @param publicKeys Public keys used for encryption and signature verification.
* @param privateKey Private key used for decryption and signature generation.
*/
constructor(identity: String,
accessTokenProvider: AccessTokenProvider,
crypto: VirgilCrypto,
publicKeys: List,
privateKey: VirgilPrivateKey) {
this.identity = identity
this.cloudKeyStorage = CloudKeyStorage(accessTokenProvider = accessTokenProvider, crypto = crypto,
publicKeys = publicKeys,
privateKey = privateKey)
this.keyStorage = KeyStorageWrapper(identity, DefaultKeyStorage())
}
/**
* Updates entry in Keyknox Cloud and Keychain.
*
* @param name Entry name.
* @param data New data.
* @param meta New meta.
*/
fun update(name: String, data: ByteArray, meta: Map? = null) {
if (!this.keyStorage.exists(name)) {
throw KeychainEntryNotFoundWhileUpdatingException()
}
try {
this.cloudKeyStorage.exists(name)
} catch (e: Exception) {
throw CloudEntryNotFoundWhileUpdatingException()
}
val cloudEntry = this.cloudKeyStorage.update(name, data, meta)
val newMeta = this.keychainUtils.createMetaForKeychain(cloudEntry)
this.keyStorage.update(name, data, newMeta)
}
/**
* Retrieves entry from Keychain.
*
* @param name Name.
*
* @return Key entry.
*/
fun retrieve(name: String): KeyEntry {
return this.keyStorage.retrieve(name)
}
/**
* Deletes entries from both Keychain and Keyknox Cloud.
*
* @param names Names to delete.
*/
fun delete(names: List) {
names.forEach { name ->
if (!this.keyStorage.exists(name)) {
throw CloudEntryNotFoundWhileDeletingException()
}
}
this.cloudKeyStorage.delete(names)
names.forEach { name ->
this.keyStorage.delete(name)
}
}
/**
* Deletes entry from both Keychain and Keyknox Cloud.
*
* @param name Name.
*/
fun delete(name: String) {
this.delete(kotlin.collections.listOf(name))
}
/**
* Stores entry in both Keychain and Keyknox Cloud.
*
* @param name Name.
* @param data Data.
* @param meta Meta.
*
* @return Key entry.
*/
fun store(name: String, data: ByteArray, meta: Map? = null): KeyEntry {
val keyEntry = this.keyStorage.createEntry(name, data)
keyEntry.meta = meta ?: mapOf()
val keyEntries = this.store(kotlin.collections.listOf(keyEntry))
if (keyEntries.size != 1) {
throw EntrySavingException()
}
return keyEntries.first()
}
/**
* Stores entries in both Keychain and Keyknox Cloud.
*
* @param keyEntries Key entries to store.
*
* @return List of stored entries.
*/
fun store(keyEntries: List): List {
keyEntries.forEach { keyEntry ->
if (this.keyStorage.exists(keyEntry.name)) {
throw KeychainEntryAlreadyExistsWhileStoringException(keyEntry.name)
}
if (this.cloudKeyStorage.exists(keyEntry.name)) {
throw CloudEntryAlreadyExistsWhileStoringException(keyEntry.name)
}
}
val cloudEntries = this.cloudKeyStorage.store(keyEntries)
val keychainEntries = mutableListOf()
val keyEntryIt = keyEntries.iterator()
val cloudEntryIt = cloudEntries.listIterator()
while (keyEntryIt.hasNext() && cloudEntryIt.hasNext()) {
val keyEntry = keyEntryIt.next()
val cloudEntry = cloudEntryIt.next()
if (!keyEntry.name.equals(cloudEntry.name)) {
throw InconsistentStateException()
}
val meta = this.keychainUtils.createMetaForKeychain(cloudEntry)
val keychainEntry = this.keyStorage.store(keyEntry.name, keyEntry.value, meta)
keychainEntries.add(keychainEntry)
}
if (keyEntries.size != cloudEntries.size) {
throw InconsistentStateException()
}
return keychainEntries
}
/**
* Performs synchronization between Keychain and Keyknox Cloud.
*/
fun sync() {
this.cloudKeyStorage.retrieveCloudEntries()
val keychainEntries = this.keyStorage.retrieveAll().filter {
this.keychainUtils.filterKeyknoxKeychainEntry(it)
}
val cloudEntries = this.cloudKeyStorage.retrieveAll()
val keychainSet = keychainEntries.map { it.name }
val cloudSet = cloudEntries.map { it.name }
val entriesToDelete = keychainSet.subtract(cloudSet).toList()
val entriesToStore = cloudSet.subtract(keychainSet).toList()
val entriesToCompare = keychainSet.intersect(cloudSet).toList()
this.syncDeleteEntries(entriesToDelete)
this.syncStoreEntries(entriesToStore)
this.syncCompareEntries(entriesToCompare, keychainEntries)
}
/**
* Updates recipients. See KeyknoxManager.updateRecipients.
*
* @param newPublicKeys New public keys.
* @param newPrivateKey New private key.
*/
fun updateRecipients(newPublicKeys: List? = null, newPrivateKey: VirgilPrivateKey? = null) {
this.cloudKeyStorage.updateRecipients(newPublicKeys, newPrivateKey)
}
/**
* Retrieves all entries from Keychain.
*
* @return Keychain entries.
*/
fun retrieveAll(): List {
return this.keyStorage.retrieveAll()
}
/**
* Checks if entry exists in Keychain.
*
* @param name Entry name.
*
* @return True if entry exists, false - otherwise.
*/
fun exists(name: String): Boolean {
return this.keyStorage.exists(name)
}
/**
* Deletes all entries in both Keychain and Keyknox Cloud.
*/
fun deleteAll() {
this.cloudKeyStorage.deleteAll()
val entriesToDelete = this.keyStorage.retrieveAll()
.filter { this.keychainUtils.filterKeyknoxKeychainEntry(it) }
.map { it.name }
this.syncDeleteEntries(entriesToDelete)
}
private fun syncDeleteEntries(entriesToDelete: List) {
entriesToDelete.forEach {
this.keyStorage.delete(it)
}
}
private fun syncStoreEntries(entriesToStore: List) {
entriesToStore.forEach { name ->
val cloudEntry = this.cloudKeyStorage.retrieve(name)
val meta = this.keychainUtils.createMetaForKeychain(cloudEntry)
this.keyStorage.store(cloudEntry.name, cloudEntry.data, meta)
}
}
private fun syncCompareEntries(entriesToCompare: List, keychainEntries: List) {
// Determine newest version and either update keychain entry or upload newer version to cloud
entriesToCompare.forEach { name ->
val keychainEntry = keychainEntries.firstOrNull { name == it.name }
?: throw KeychainEntryNotFoundWhileComparingException()
val cloudEntry = this.cloudKeyStorage.retrieve(name)
val keychainDate = this.keychainUtils.extractModificationDate(keychainEntry)
if (keychainDate.second < cloudEntry.modificationDate) {
val meta = this.keychainUtils.createMetaForKeychain(cloudEntry)
this.keyStorage.update(cloudEntry.name, cloudEntry.data, meta)
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy