fixed SafeKeyEXchange.SessionKey serialization issue
This commit is contained in:
parent
84a3e82216
commit
66254f8fa6
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
|
|||||||
@ -13,14 +13,14 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.2.21"
|
kotlin("multiplatform") version "2.4.0"
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.2.21"
|
id("org.jetbrains.kotlin.plugin.serialization") version "2.4.0"
|
||||||
id("org.jetbrains.dokka") version "1.9.20"
|
id("org.jetbrains.dokka") version "1.9.20"
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.9.3"
|
version = "0.9.4-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
@ -155,15 +155,9 @@ sealed class Container {
|
|||||||
abstract val decryptedWithKeyId: KeyId?
|
abstract val decryptedWithKeyId: KeyId?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the container _is decrypted by the [EncryptingPublicKey]_, e.g., using secret key encryption,
|
* When the container is decrypted, the key that unlocked it, otherwise null.
|
||||||
* contains the [EncryptingPublicKey] that corresponds the [DecryptingSecretKey] used while encrypting, this
|
|
||||||
* authenticating the sender party cryptographically. This key could be used to encrypt
|
|
||||||
* the response to be visible to the sender only; the sender, providing it kept his secret key,
|
|
||||||
* could decrypt it.
|
|
||||||
*/
|
*/
|
||||||
@Transient
|
abstract val decryptedWithKey: UniversalKey?
|
||||||
var authorisedByKey: EncryptingPublicKey? = null
|
|
||||||
protected set
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of [KeyId] of the keys that unlocked the container, in the same order used for encryption..
|
* List of [KeyId] of the keys that unlocked the container, in the same order used for encryption..
|
||||||
@ -185,7 +179,8 @@ sealed class Container {
|
|||||||
) : Container() {
|
) : Container() {
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private var decryptedWithKey: DecryptingKey? = null
|
override var decryptedWithKey: UniversalKey? = null
|
||||||
|
private set
|
||||||
|
|
||||||
override val decryptedWithKeyId: KeyId?
|
override val decryptedWithKeyId: KeyId?
|
||||||
get() = decryptedWithKey?.id
|
get() = decryptedWithKey?.id
|
||||||
@ -202,10 +197,7 @@ sealed class Container {
|
|||||||
if (k.id == keyId) {
|
if (k.id == keyId) {
|
||||||
kotlin.runCatching { k.decrypt(encryptedMessage) }.getOrNull()?.let {
|
kotlin.runCatching { k.decrypt(encryptedMessage) }.getOrNull()?.let {
|
||||||
decryptedData = it
|
decryptedData = it
|
||||||
decryptedWithKey = k
|
decryptedWithKey = k as UniversalKey
|
||||||
if(k is DecryptingSecretKey) {
|
|
||||||
authorisedByKey = Asymmetric.Message.decode(encryptedMessage).senderPublicKey
|
|
||||||
}
|
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,9 +276,12 @@ sealed class Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
override var decryptedWithKeyId: KeyId? = null
|
override var decryptedWithKey: UniversalKey? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
override val decryptedWithKeyId: KeyId?
|
||||||
|
get() = decryptedWithKey?.id
|
||||||
|
|
||||||
override val keyIds: List<KeyId> = encryptedKeys.map { it.tag }
|
override val keyIds: List<KeyId> = encryptedKeys.map { it.tag }
|
||||||
|
|
||||||
override fun decryptWith(keyRing: UniversalRing): UByteArray? {
|
override fun decryptWith(keyRing: UniversalRing): UByteArray? {
|
||||||
@ -302,11 +297,8 @@ sealed class Container {
|
|||||||
}.getOrNull()?.let { k ->
|
}.getOrNull()?.let { k ->
|
||||||
if (kotlin.runCatching { decryptedData = k.decrypt(encryptedMessage) }.isFailure)
|
if (kotlin.runCatching { decryptedData = k.decrypt(encryptedMessage) }.isFailure)
|
||||||
throw InvalidContainerException()
|
throw InvalidContainerException()
|
||||||
decryptedWithKeyId = key.id
|
decryptedWithKey = key as UniversalKey
|
||||||
mainKey = k
|
mainKey = k
|
||||||
if(key is DecryptingSecretKey) {
|
|
||||||
authorisedByKey = Asymmetric.Message.decode(encryptedKey.cipherData).senderPublicKey
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (decryptedData != null) return decryptedData
|
if (decryptedData != null) return decryptedData
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import com.ionspin.kotlin.crypto.keyexchange.KeyExchangeKeyPair
|
|||||||
import com.ionspin.kotlin.crypto.keyexchange.crypto_kx_PUBLICKEYBYTES
|
import com.ionspin.kotlin.crypto.keyexchange.crypto_kx_PUBLICKEYBYTES
|
||||||
import com.ionspin.kotlin.crypto.keyexchange.crypto_kx_SECRETKEYBYTES
|
import com.ionspin.kotlin.crypto.keyexchange.crypto_kx_SECRETKEYBYTES
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.SerializationException
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
@ -81,7 +82,7 @@ class SafeKeyExchange private constructor(
|
|||||||
* The session key. It uses a pair of keys to encrypt and decrypt messages to maintain high
|
* The session key. It uses a pair of keys to encrypt and decrypt messages to maintain high
|
||||||
* security level and allow using counters as nonce with no extra precautions.
|
* security level and allow using counters as nonce with no extra precautions.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable(with = SafeKeyExchange.SessionKeySerializer::class)
|
||||||
class SessionKey(
|
class SessionKey(
|
||||||
val sendingKey: EncryptingKey,
|
val sendingKey: EncryptingKey,
|
||||||
val receivingKey: DecryptingKey,
|
val receivingKey: DecryptingKey,
|
||||||
@ -128,6 +129,36 @@ class SafeKeyExchange private constructor(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private class PackedSessionKey(
|
||||||
|
val sendingKey: SymmetricKey,
|
||||||
|
val receivingKey: SymmetricKey,
|
||||||
|
val isClient: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
object SessionKeySerializer : KSerializer<SessionKey> {
|
||||||
|
private val packedSerializer = PackedSessionKey.serializer()
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor = packedSerializer.descriptor
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: SessionKey) {
|
||||||
|
val sendingKey = value.sendingKey as? SymmetricKey
|
||||||
|
?: throw SerializationException("SessionKey.sendingKey must be SymmetricKey")
|
||||||
|
val receivingKey = value.receivingKey as? SymmetricKey
|
||||||
|
?: throw SerializationException("SessionKey.receivingKey must be SymmetricKey")
|
||||||
|
|
||||||
|
encoder.encodeSerializableValue(
|
||||||
|
packedSerializer,
|
||||||
|
PackedSessionKey(sendingKey, receivingKey, value.isClient)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): SessionKey {
|
||||||
|
val packed = decoder.decodeSerializableValue(packedSerializer)
|
||||||
|
return SessionKey(packed.sendingKey, packed.receivingKey, packed.isClient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The public key; it should be transmitted to the other party, this is serializable.
|
* The public key; it should be transmitted to the other party, this is serializable.
|
||||||
* Do not use it except to get [SessionKey] with [clientSessionKey] or [serverSessionKey]. Storing and reusing
|
* Do not use it except to get [SessionKey] with [clientSessionKey] or [serverSessionKey]. Storing and reusing
|
||||||
|
|||||||
@ -34,6 +34,8 @@ class ContainerTest {
|
|||||||
assertNotNull(d)
|
assertNotNull(d)
|
||||||
assertContentEquals(data, d)
|
assertContentEquals(data, d)
|
||||||
assertTrue { c1.isDecrypted }
|
assertTrue { c1.isDecrypted }
|
||||||
|
assertEquals(syk1.id, c1.decryptedWithKeyId)
|
||||||
|
assertEquals(syk1, c1.decryptedWithKey)
|
||||||
|
|
||||||
val data2 = "To push unpushinable".encodeToUByteArray()
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
val c2 = Container.decode(c.updateData(data2).encoded)
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
@ -61,7 +63,7 @@ class ContainerTest {
|
|||||||
assertContentEquals(data, d)
|
assertContentEquals(data, d)
|
||||||
assertTrue { c1.isDecrypted }
|
assertTrue { c1.isDecrypted }
|
||||||
assertEquals(p2.publicKey.id, c1.decryptedWithKeyId)
|
assertEquals(p2.publicKey.id, c1.decryptedWithKeyId)
|
||||||
assertEquals(p1.publicKey, c1.authorisedByKey)
|
assertEquals(p2.secretKey, c1.decryptedWithKey)
|
||||||
|
|
||||||
val data2 = "To push unpushinable".encodeToUByteArray()
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
val c2 = Container.decode(c.updateData(data2).encoded)
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
@ -88,7 +90,7 @@ class ContainerTest {
|
|||||||
assertContentEquals(data, d)
|
assertContentEquals(data, d)
|
||||||
assertTrue { c1.isDecrypted }
|
assertTrue { c1.isDecrypted }
|
||||||
assertEquals(p4.publicKey.id, c1.decryptedWithKeyId)
|
assertEquals(p4.publicKey.id, c1.decryptedWithKeyId)
|
||||||
assertEquals(p1.publicKey, c1.authorisedByKey)
|
assertEquals(p4.secretKey, c1.decryptedWithKey)
|
||||||
|
|
||||||
val data2 = "To push unpushinable".encodeToUByteArray()
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
val c2 = Container.decode(c.updateData(data2).encoded)
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
@ -338,4 +340,4 @@ class ContainerTest {
|
|||||||
expectOpen(data2, syk3, p3.secretKey, p4.secretKey)
|
expectOpen(data2, syk3, p3.secretKey, p4.secretKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -241,6 +241,28 @@ class KeysTest {
|
|||||||
assertContentEquals(clientSessionKey.sessionTag, serverSessionKey.sessionTag)
|
assertContentEquals(clientSessionKey.sessionTag, serverSessionKey.sessionTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sessionKeySerializationTest() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val ske = SafeKeyExchange()
|
||||||
|
val cke = SafeKeyExchange()
|
||||||
|
|
||||||
|
val clientSessionKey = cke.clientSessionKey(ske.publicKey)
|
||||||
|
val serverSessionKey = ske.serverSessionKey(cke.publicKey)
|
||||||
|
|
||||||
|
val restoredClientSessionKey = unpack<SafeKeyExchange.SessionKey>(pack(clientSessionKey))
|
||||||
|
val restoredServerSessionKey = unpack<SafeKeyExchange.SessionKey>(pack(serverSessionKey))
|
||||||
|
|
||||||
|
assertEquals(clientSessionKey, restoredClientSessionKey)
|
||||||
|
assertEquals(serverSessionKey, restoredServerSessionKey)
|
||||||
|
assertContentEquals(clientSessionKey.sessionTag, restoredClientSessionKey.sessionTag)
|
||||||
|
assertContentEquals(serverSessionKey.sessionTag, restoredServerSessionKey.sessionTag)
|
||||||
|
|
||||||
|
val src = "Hello from restored session keys!"
|
||||||
|
assertEquals(src, restoredServerSessionKey.decryptString(restoredClientSessionKey.encrypt(src)))
|
||||||
|
assertEquals(src, restoredClientSessionKey.decryptString(restoredServerSessionKey.encrypt(src)))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun asymmetricKeyTest() = runTest {
|
fun asymmetricKeyTest() = runTest {
|
||||||
initCrypto()
|
initCrypto()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user