Compare commits
2 Commits
875c0f7a50
...
85b13ed8ca
Author | SHA1 | Date | |
---|---|---|---|
85b13ed8ca | |||
fbbe4d3a34 |
@ -15,11 +15,19 @@ import kotlin.random.nextUBytes
|
|||||||
*
|
*
|
||||||
* Keys are stored encrypted and used hashed so it is not possible to
|
* Keys are stored encrypted and used hashed so it is not possible to
|
||||||
* retrieve them without knowing the encryption key.
|
* retrieve them without knowing the encryption key.
|
||||||
|
*
|
||||||
|
* @param plainStore where to store encrypted data
|
||||||
|
* @param encryptionKey key to decrypt existing/encrypt new data. Can cause [DecryptionFailedException]
|
||||||
|
* if the key is wrong and the storage is already initialized with a new key and same [prefix]
|
||||||
|
* @param prefix prefix for keys to distinguish from other data in [plainStore]
|
||||||
|
* @param removeExisting if true, removes all existing data in [plainStore] if the [encryptionKey] can't
|
||||||
|
* decrypt existing encrypted data
|
||||||
*/
|
*/
|
||||||
class EncryptedKVStorage(
|
class EncryptedKVStorage(
|
||||||
private val plainStore: KVStorage,
|
private val plainStore: KVStorage,
|
||||||
private var encryptionKey: SymmetricKey,
|
private var encryptionKey: SymmetricKey,
|
||||||
private val prefix: String = "EKVS_"
|
private val prefix: String = "EKVS_",
|
||||||
|
removeExisting: Boolean
|
||||||
) : KVStorage {
|
) : KVStorage {
|
||||||
private val op = ProtectedOp()
|
private val op = ProtectedOp()
|
||||||
|
|
||||||
@ -29,10 +37,21 @@ class EncryptedKVStorage(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
var encryptedSeed by plainStore.optStored<UByteArray>("$prefix#seed")
|
var encryptedSeed by plainStore.optStored<UByteArray>("$prefix#seed")
|
||||||
seed = encryptedSeed?.let { encryptionKey.decrypt(it) }
|
seed = try {
|
||||||
?: Random.nextUBytes(32).also {
|
encryptedSeed?.let { encryptionKey.decrypt(it) }
|
||||||
encryptedSeed = encryptionKey.encrypt(it)
|
?: Random.nextUBytes(32).also {
|
||||||
}
|
encryptedSeed = encryptionKey.encrypt(it)
|
||||||
|
}
|
||||||
|
} catch (x: DecryptionFailedException) {
|
||||||
|
if (removeExisting) {
|
||||||
|
plainStore.keys.filter { it.startsWith(prefix) }.forEach {
|
||||||
|
plainStore.delete(it)
|
||||||
|
}
|
||||||
|
Random.nextUBytes(32).also {
|
||||||
|
encryptedSeed = encryptionKey.encrypt(it)
|
||||||
|
}
|
||||||
|
} else throw x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mkkey(key: String): String =
|
private fun mkkey(key: String): String =
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.bintools.*
|
import net.sergeych.bintools.*
|
||||||
import net.sergeych.bipack.decodeFromBipack
|
import net.sergeych.bipack.decodeFromBipack
|
||||||
|
import net.sergeych.crypto2.DecryptionFailedException
|
||||||
import net.sergeych.crypto2.EncryptedKVStorage
|
import net.sergeych.crypto2.EncryptedKVStorage
|
||||||
import net.sergeych.crypto2.SymmetricKey
|
import net.sergeych.crypto2.SymmetricKey
|
||||||
import net.sergeych.crypto2.initCrypto
|
import net.sergeych.crypto2.initCrypto
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class StorageTest {
|
class StorageTest {
|
||||||
@ -15,7 +17,7 @@ class StorageTest {
|
|||||||
initCrypto()
|
initCrypto()
|
||||||
val plain = MemoryKVStorage()
|
val plain = MemoryKVStorage()
|
||||||
val key = SymmetricKey.new()
|
val key = SymmetricKey.new()
|
||||||
val storage = EncryptedKVStorage(plain, key)
|
val storage = EncryptedKVStorage(plain, key, removeExisting = false)
|
||||||
|
|
||||||
var hello by storage.optStored<String>()
|
var hello by storage.optStored<String>()
|
||||||
assertNull(hello)
|
assertNull(hello)
|
||||||
@ -42,8 +44,9 @@ class StorageTest {
|
|||||||
assertEquals("bar", bar)
|
assertEquals("bar", bar)
|
||||||
assertEquals("bazz", bazz)
|
assertEquals("bazz", bazz)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setup(s: KVStorage, k: SymmetricKey): EncryptedKVStorage {
|
fun setup(s: KVStorage, k: SymmetricKey): EncryptedKVStorage {
|
||||||
val x = EncryptedKVStorage(s, k)
|
val x = EncryptedKVStorage(s, k, removeExisting = false)
|
||||||
var foo by x.stored("1")
|
var foo by x.stored("1")
|
||||||
var bar by x.stored("2")
|
var bar by x.stored("2")
|
||||||
var bazz by x.stored("3")
|
var bazz by x.stored("3")
|
||||||
@ -52,6 +55,7 @@ class StorageTest {
|
|||||||
bazz = "bazz"
|
bazz = "bazz"
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
val k1 = SymmetricKey.new()
|
val k1 = SymmetricKey.new()
|
||||||
val k2 = SymmetricKey.new()
|
val k2 = SymmetricKey.new()
|
||||||
val plain = MemoryKVStorage()
|
val plain = MemoryKVStorage()
|
||||||
@ -62,8 +66,21 @@ class StorageTest {
|
|||||||
// val s2 = EncryptedKVStorage(plain, k2)
|
// val s2 = EncryptedKVStorage(plain, k2)
|
||||||
// test(s2)
|
// test(s2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeleteExisting() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val plain = MemoryKVStorage()
|
||||||
|
val c1 = EncryptedKVStorage(plain, SymmetricKey.new(), removeExisting = false) // 1
|
||||||
|
c1.write("hello", "world")
|
||||||
|
assertFailsWith<DecryptionFailedException> {
|
||||||
|
EncryptedKVStorage(plain, SymmetricKey.new(), removeExisting = false) // 2
|
||||||
|
}
|
||||||
|
EncryptedKVStorage(plain, SymmetricKey.new(), removeExisting = true) // 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
fun KVStorage.dump() {
|
fun KVStorage.dump() {
|
||||||
for (k in keys)
|
for (k in keys)
|
||||||
println("$k: ${this[k]?.toDump()}")
|
println("$k: ${this[k]?.toDump()}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user