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 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 @Serializable
@SerialName("1") @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 @Transient
private var decryptedWithKey: DecryptingKey? = null private var decryptedWithKey: DecryptingKey? = null
init {
decryptedData = creationData
}
override fun decryptWith(keyRing: UniversalRing): UByteArray? { override fun decryptWith(keyRing: UniversalRing): UByteArray? {
decryptedData?.let { return it } decryptedData?.let { return it }
for (k in keyRing) { for (k in keyRing) {
@ -83,25 +92,34 @@ sealed class Container {
internal val asOpenMulti: Container by lazy { internal val asOpenMulti: Container by lazy {
check(isDecrypted) { "container should be decrypted" } check(isDecrypted) { "container should be decrypted" }
create(decryptedData!!) { create(decryptedData!!) {
alwaysMulti() asOpenedMulti()
when (val k = decryptedWithKey!!) { // The encryption key is known if we just created the container
is Asymmetric.SecretKey -> { if (reEncryptionKey != null)
key(k.publicKey) 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 -> { is EncryptingKey -> {
key(k) key(k)
} }
is UniversalKey.Secret -> { is UniversalKey.Secret -> {
key(k.key.publicKey) key(k.key.publicKey)
} }
else -> { else -> {
throw IllegalStateException("unknown key type to convert container: ${k::class.simpleName}") 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 @Serializable
@SerialName("*") @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 @Serializable
class EncryptedKey(val tag: KeyTag, val cipherData: UByteArray) { class EncryptedKey(val tag: KeyTag, val cipherData: UByteArray) {
constructor(key: EncryptingKey, encodeMainKey: 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? { override fun decryptWith(keyRing: UniversalRing): UByteArray? {
decryptedData?.let { return it } 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) } 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) } 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, * 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 plainKeys = mutableListOf<EncryptingKey>()
private val keyPairs = mutableListOf<Pair<Asymmetric.SecretKey?, Asymmetric.PublicKey>>() private val keyPairs = mutableListOf<AsymmetricEncryptionPair>()
private var fillRange: IntRange? = null 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 * 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 * 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) keyPairs.addAll(pairs)
} }
@ -256,14 +286,14 @@ sealed class Container {
fillRange = range fillRange = range
} }
private var makeMulti = false private var makeOpenedMulti = false
/** /**
* @suppress * @suppress
* will produce multikey internal variant even with only one key. User internally * will produce multikey internal variant even with only one key. User internally
*/ */
internal fun alwaysMulti() { internal fun asOpenedMulti() {
makeMulti = true makeOpenedMulti = true
} }
/** /**
@ -274,7 +304,7 @@ sealed class Container {
if (parent != null) require(parent is Multi) { "parent container mut be a multikey variant" } if (parent != null) require(parent is Multi) { "parent container mut be a multikey variant" }
return when { return when {
countNewKeys == 0 -> throw IllegalArgumentException("Container needs at least one key") countNewKeys == 0 -> throw IllegalArgumentException("Container needs at least one key")
countNewKeys == 1 && makeMulti == false && parent == null -> { countNewKeys == 1 && makeOpenedMulti == false && parent == null -> {
createSingle() createSingle()
} }
@ -297,16 +327,20 @@ sealed class Container {
} }
private fun createSingle() = plainKeys.firstOrNull()?.let { private fun createSingle() = plainKeys.firstOrNull()?.let {
Single(it.tag, it.encrypt(plainData, fillRange)) Single(it.tag, it.encrypt(plainData, fillRange), plainData, reEncryptionKey = it)
} ?: run { } ?: run {
val (sk, pk) = keyPairs.first() val pair = keyPairs.first()
val (sk, pk) = pair
Single( Single(
pk.tag, pk.encryptMessage( pk.tag, pk.encryptMessage(
plainData, plainData,
senderKey = sk ?: Asymmetric.randomSecretKey(), senderKey = sk ?: Asymmetric.randomSecretKey(),
randomFill = fillRange randomFill = fillRange
).encoded ).encoded,
plainData,
encryptionPair = pair
) )
} }
private fun createMulti( private fun createMulti(
@ -320,7 +354,10 @@ sealed class Container {
val (sender, recipient) = p val (sender, recipient) = p
eks += Multi.EncryptedKey(sender, recipient, encodedMainKey) 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] * Create the container using one or more `sender to recipient` asymmetric keys and a builder. See [create]
* for builder usage sample. * 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) } create(plainData) { key(*keys) }
fun decode(encoded: UByteArray): Container { fun decode(encoded: UByteArray): Container {

View File

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