fix #5 Just created container is decrypted and can be used to add keys, etc.

This commit is contained in:
Sergey Chernov 2024-06-20 09:27:10 +07:00
parent c0317dda47
commit a1561bc280
3 changed files with 86 additions and 39 deletions

View File

@ -256,4 +256,11 @@ object Asymmetric {
get() = 0
}
}
}
/**
* Shortcut type: a pair of sender secret key and recipient private key could be used so
* simplify such interfaces
*/
typealias AsymmetricEncryptionPair = Pair<SecretKey?,PublicKey>

View File

@ -61,11 +61,20 @@ sealed class Container {
*/
@Serializable
@SerialName("1")
internal class Single(val keyTag: KeyTag, val encryptedMessage: UByteArray) : Container() {
internal class Single(
val keyTag: KeyTag, val encryptedMessage: UByteArray,
@Transient private val creationData: UByteArray? = null,
@Transient private val reEncryptionKey: EncryptingKey? = null,
@Transient private var encryptionPair: AsymmetricEncryptionPair? = null,
) : Container() {
@Transient
private var decryptedWithKey: DecryptingKey? = null
init {
decryptedData = creationData
}
override fun decryptWith(keyRing: UniversalRing): UByteArray? {
decryptedData?.let { return it }
for (k in keyRing) {
@ -83,25 +92,34 @@ sealed class Container {
internal val asOpenMulti: Container by lazy {
check(isDecrypted) { "container should be decrypted" }
create(decryptedData!!) {
alwaysMulti()
when (val k = decryptedWithKey!!) {
is Asymmetric.SecretKey -> {
key(k.publicKey)
}
asOpenedMulti()
// The encryption key is known if we just created the container
if (reEncryptionKey != null)
key(reEncryptionKey)
else if (encryptionPair != null) {
key(encryptionPair!!)
} else {
// otherwise, we don't know the encryption key and will try to derive it
// from the decryption key:
when (val k = decryptedWithKey!!) {
is Asymmetric.SecretKey -> {
key(k.publicKey)
}
is EncryptingKey -> {
key(k)
}
is EncryptingKey -> {
key(k)
}
is UniversalKey.Secret -> {
key(k.key.publicKey)
}
is UniversalKey.Secret -> {
key(k.key.publicKey)
}
else -> {
throw IllegalStateException("unknown key type to convert container: ${k::class.simpleName}")
else -> {
throw IllegalStateException("unknown key type to convert container: ${k::class.simpleName}")
}
}
}
}.apply { decryptWith(decryptedWithKey!!) ?: throw Crypto2Exception("internal error in container update (1)") }
}
}
}
@ -111,7 +129,12 @@ sealed class Container {
*/
@Serializable
@SerialName("*")
internal class Multi(val encryptedKeys: List<EncryptedKey>, val encryptedMessage: UByteArray) : Container() {
internal class Multi(
val encryptedKeys: List<EncryptedKey>, val encryptedMessage: UByteArray,
@Transient internal var mainKey: SymmetricKey? = null,
@Transient internal var knownPlainData: UByteArray? = null,
) : Container() {
@Serializable
class EncryptedKey(val tag: KeyTag, val cipherData: UByteArray) {
constructor(key: EncryptingKey, encodeMainKey: UByteArray) :
@ -127,7 +150,9 @@ sealed class Container {
)
}
internal var mainKey: SymmetricKey? = null
init {
knownPlainData?.let { decryptedData = it }
}
override fun decryptWith(keyRing: UniversalRing): UByteArray? {
decryptedData?.let { return it }
@ -164,17 +189,22 @@ sealed class Container {
}
/**
* Add e key to the __decrypted__ container
* Add e key to the __decrypted__ container. The new container is also decrypted so you can add
* more keys, etc.
*/
operator fun plus(recipient: Asymmetric.PublicKey) = addRecipients { key(recipient) }
/**
* Add e key to the __decrypted__ container
* Add e key to the __decrypted__ container. The new container is also decrypted so you can add
* more keys, etc.
*/
operator fun plus(recipient: EncryptingKey) = addRecipients { key(recipient) }
/**
* Add e sender -> recipient asymmetric keys pair key to the __decrypted__ container
* Add e key to the __decrypted__ container. The new container is also decrypted so you can add
* more keys, etc.
*/
operator fun plus(pair: Pair<Asymmetric.SecretKey,Asymmetric.PublicKey>) = addRecipients { key(pair) }
operator fun plus(pair: Pair<Asymmetric.SecretKey, Asymmetric.PublicKey>) = addRecipients { key(pair) }
/**
* Binary encoded version. It is desirable to include [Container] as an object, though,
@ -222,7 +252,7 @@ sealed class Container {
private val plainKeys = mutableListOf<EncryptingKey>()
private val keyPairs = mutableListOf<Pair<Asymmetric.SecretKey?, Asymmetric.PublicKey>>()
private val keyPairs = mutableListOf<AsymmetricEncryptionPair>()
private var fillRange: IntRange? = null
/**
@ -236,7 +266,7 @@ sealed class Container {
* Add one or more [Asymmetric.SecretKey] as sender authority coupled with [Asymmetric.PublicKey] as
* a recipient. This is faster than anonymous usage of [Asymmetric.PublicKey] only
*/
fun key(vararg pairs: Pair<Asymmetric.SecretKey, Asymmetric.PublicKey>) {
fun key(vararg pairs: AsymmetricEncryptionPair) {
keyPairs.addAll(pairs)
}
@ -256,14 +286,14 @@ sealed class Container {
fillRange = range
}
private var makeMulti = false
private var makeOpenedMulti = false
/**
* @suppress
* will produce multikey internal variant even with only one key. User internally
*/
internal fun alwaysMulti() {
makeMulti = true
internal fun asOpenedMulti() {
makeOpenedMulti = true
}
/**
@ -274,7 +304,7 @@ sealed class Container {
if (parent != null) require(parent is Multi) { "parent container mut be a multikey variant" }
return when {
countNewKeys == 0 -> throw IllegalArgumentException("Container needs at least one key")
countNewKeys == 1 && makeMulti == false && parent == null -> {
countNewKeys == 1 && makeOpenedMulti == false && parent == null -> {
createSingle()
}
@ -297,16 +327,20 @@ sealed class Container {
}
private fun createSingle() = plainKeys.firstOrNull()?.let {
Single(it.tag, it.encrypt(plainData, fillRange))
Single(it.tag, it.encrypt(plainData, fillRange), plainData, reEncryptionKey = it)
} ?: run {
val (sk, pk) = keyPairs.first()
val pair = keyPairs.first()
val (sk, pk) = pair
Single(
pk.tag, pk.encryptMessage(
plainData,
senderKey = sk ?: Asymmetric.randomSecretKey(),
randomFill = fillRange
).encoded
).encoded,
plainData,
encryptionPair = pair
)
}
private fun createMulti(
@ -320,7 +354,10 @@ sealed class Container {
val (sender, recipient) = p
eks += Multi.EncryptedKey(sender, recipient, encodedMainKey)
}
return Multi(eks, mainKey.encrypt(plainData, fillRange))
return if (makeOpenedMulti)
Multi(eks, mainKey.encrypt(plainData, fillRange), mainKey, plainData)
else
Multi(eks, mainKey.encrypt(plainData, fillRange))
}
}
@ -363,7 +400,7 @@ sealed class Container {
* Create the container using one or more `sender to recipient` asymmetric keys and a builder. See [create]
* for builder usage sample.
*/
fun createWith(plainData: UByteArray, vararg keys: Pair<Asymmetric.SecretKey, Asymmetric.PublicKey>) =
fun createWith(plainData: UByteArray, vararg keys: AsymmetricEncryptionPair) =
create(plainData) { key(*keys) }
fun decode(encoded: UByteArray): Container {

View File

@ -1,5 +1,7 @@
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import net.sergeych.crypto2.*
import kotlin.test.*
@ -12,9 +14,9 @@ class ContainerTest {
val data = "sergeych, ohm many.".encodeToUByteArray()
val c = Container.createWith(data, syk1)
assertFalse { c.isDecrypted }
assertTrue { c.isDecrypted }
val c1 = Container.decode(c.encoded)
assertFalse { c.isDecrypted }
assertFalse { c1.isDecrypted }
assertIs<Container.Single>(c)
assertNull(c1.decryptWith(syk2))
@ -33,9 +35,9 @@ class ContainerTest {
val data = "sergeych, ohm many.".encodeToUByteArray()
val c = Container.createWith(data, p1.secretKey to p2.publicKey)
assertFalse { c.isDecrypted }
assertTrue { c.isDecrypted }
val c1 = Container.decode(c.encoded)
assertFalse { c.isDecrypted }
assertFalse { c1.isDecrypted }
assertIs<Container.Single>(c)
assertNull(c1.decryptWith(p3.secretKey))
@ -54,9 +56,10 @@ class ContainerTest {
val data = "sergeych, ohm many.".encodeToUByteArray()
val c = Container.create(data) { key(p2.publicKey) }
assertFalse { c.isDecrypted }
assertTrue { c.isDecrypted }
val c1 = Container.decode(c.encoded)
assertFalse { c.isDecrypted }
println(Json.encodeToString(c1))
assertFalse { c1.isDecrypted }
assertIs<Container.Single>(c)
assertNull(c1.decryptWith(p3.secretKey))