fix #5 Just created container is decrypted and can be used to add keys, etc.
This commit is contained in:
parent
c0317dda47
commit
a1561bc280
@ -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>
|
||||||
|
@ -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 {
|
||||||
|
@ -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))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user