Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
feat: add [de]serialization and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mirceanis committed May 16, 2021
1 parent 1242a03 commit 52ffb4c
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 148 deletions.
Expand Up @@ -6,9 +6,10 @@ import ro.jwt.komuta.Curve25519.POINT_BYTES
/**
* Wraps a commutative encryption system.
*
* The assumption is that your message is already mapped onto a msgEmbedding
* The assumption is that your message is already mapped onto a msgEmbedding.
* You can also initialize this with an already encrypted message, in which case you would also need to add the mapping of ephemeralKeys.
*/
class ElGamal(msgEmbedding: PublicKey, ephemeralKeys: Map<String, ByteArray> = emptyMap()) {
class Komuta(msgEmbedding: PublicKey, ephemeralKeys: Map<String, ByteArray> = emptyMap()) {

val accumulator: ByteArray = ByteArray(POINT_BYTES)
private val pubKeys: MutableMap<String, ByteArray> = mutableMapOf()
Expand All @@ -35,13 +36,39 @@ class ElGamal(msgEmbedding: PublicKey, ephemeralKeys: Map<String, ByteArray> = e
}

fun isMasked() = pubKeys.isNotEmpty()

override fun toString(): String {
val out = arrayListOf(accumulator.encodeBase64())
out.addAll(pubKeys.flatMap {
listOf(it.key, it.value.encodeBase64())
})
return out.joinToString(",")
}

companion object {
fun fromString(serialized: String): Komuta {
val collection = ArrayDeque(serialized.split(","))

val accumulator = collection.removeFirst().decodeBase64()
val ephemeralKeys = mutableMapOf<String, ByteArray>()
while (collection.isNotEmpty()) {
val pub = collection.removeFirst()
val eph = collection.removeFirst().decodeBase64()
ephemeralKeys[pub] = eph
}

return Komuta(PublicKey(accumulator), ephemeralKeys)
}
}

}

inline class PublicKey(val encoded: String) {
val raw: ByteArray
get() = encoded.decodeBase64()
@Suppress("INLINE_CLASS_DEPRECATED")
inline class PublicKey(val raw: ByteArray) {
val encoded: String
get() = raw.encodeBase64()

constructor(bytes: ByteArray) : this(bytes.encodeBase64())
constructor(encoded: String) : this(encoded.decodeBase64())
}

fun String.decodeBase64(): ByteArray = BaseCodec(base64pad).decode(this)
Expand Down
142 changes: 0 additions & 142 deletions src/jvmTest/kotlin/ro/jwt/komuta/ElGamalTest.kt

This file was deleted.

124 changes: 124 additions & 0 deletions src/jvmTest/kotlin/ro/jwt/komuta/KomutaTest.kt
@@ -0,0 +1,124 @@
package ro.jwt.komuta

import org.junit.Assert
import org.junit.Test
import java.util.*

class KomutaTest {

/**
* A few curve points usable as prebaked message embeddings
*/
val cardEmbeddings = arrayOf(
"V5aZXdL2bfzyXqnwcYCsHpa5ojGKxc7Nh9vMQIrnvlY=",
"27kXjSX9x+O6AT/uf/KHBwdlE/5pUHj3JOxXmh9SsiE=",
"M2d/3YvGrWzDAUQ5wckpaYB/od85NEzPN4DohhI0d3Y=",
"eVkpC5mUtBLogawnkBwPDGMpwq5UheJf9LrY04GwHAU="
)

@Test
fun `decode base64`() {
for (i in 0..365) {
val input = randomBytes(32)
val encoded = Base64.getEncoder().encodeToString(input)
val decoded = BaseCodec(BaseCodec.Base.base64pad).decode(encoded)
Assert.assertArrayEquals(input, decoded)
}
}

@Test
fun `encode base64`() {
for (i in 0..365) {
val input = randomBytes(32)
val encoded = BaseCodec(BaseCodec.Base.base64pad).encode(input)
val expected = Base64.getEncoder().encodeToString(input)
Assert.assertEquals(expected, encoded)
}
}

@Test
fun `add and subtract`() {
val card1 = cardEmbeddings[1].decodeBase64()
val card2 = cardEmbeddings[2].decodeBase64()
val result = Curve25519.add(card1, card2)
val inverse = Curve25519.sub(result, card2)
Assert.assertArrayEquals(inverse, card1)
Assert.assertEquals(cardEmbeddings[1], inverse.encodeBase64())
}

@Test
fun `single encrypt card`() {
val card1 = cardEmbeddings[1]
val maskedCard = Komuta(PublicKey(card1))
val aliceKeypair = Curve25519.keyPair()

maskedCard.addEncryption(aliceKeypair.publicKey)
Assert.assertNotEquals(card1, maskedCard.accumulator.encodeBase64())
maskedCard.removeEncryption(aliceKeypair)

Assert.assertArrayEquals(card1.decodeBase64(), maskedCard.accumulator)
}

@Test
fun `double encrypt card, decrypt LIFO`() {
val card1 = cardEmbeddings[1]
val maskedCard = Komuta(PublicKey(card1))
val aliceKeypair = Curve25519.keyPair()
val bobKeypair = Curve25519.keyPair()

maskedCard.addEncryption(aliceKeypair.publicKey)
maskedCard.addEncryption(bobKeypair.publicKey)
maskedCard.removeEncryption(bobKeypair)
maskedCard.removeEncryption(aliceKeypair)

Assert.assertArrayEquals(card1.decodeBase64(), maskedCard.accumulator)
}

@Test
fun `double encrypt card, decrypt FIFO`() {
val card1 = cardEmbeddings[1]
val maskedCard = Komuta(PublicKey(card1))
val aliceKeypair = Curve25519.keyPair()
val bobKeypair = Curve25519.keyPair()

maskedCard.addEncryption(aliceKeypair.publicKey)
maskedCard.addEncryption(bobKeypair.publicKey)
maskedCard.removeEncryption(aliceKeypair)
maskedCard.removeEncryption(bobKeypair)

Assert.assertArrayEquals(card1.decodeBase64(), maskedCard.accumulator)
}

@Test
fun `triple encrypt card, with serialization, decrypt radnom order`() {
val card1 = cardEmbeddings[1]
var maskedCard = Komuta(PublicKey(card1))

val aliceKeypair = Curve25519.keyPair()
val bobKeypair = Curve25519.keyPair()
val charlieKeypair = Curve25519.keyPair()

//add 3 encryption layers and serialize
maskedCard.addEncryption(aliceKeypair.publicKey)
maskedCard.addEncryption(bobKeypair.publicKey)
maskedCard.addEncryption(charlieKeypair.publicKey)
Assert.assertTrue(maskedCard.isMasked())
val toBob = maskedCard.toString()

//bob decrypts
maskedCard = Komuta.fromString(toBob)
maskedCard.removeEncryption(bobKeypair)
val toAlice = maskedCard.toString()
//alice decrypts
maskedCard = Komuta.fromString(toAlice)
maskedCard.removeEncryption(aliceKeypair)
val toCharlie = maskedCard.toString()
//charlie decrypts
maskedCard = Komuta.fromString(toCharlie)
maskedCard.removeEncryption(charlieKeypair)

Assert.assertFalse(maskedCard.isMasked())

Assert.assertArrayEquals(card1.decodeBase64(), maskedCard.accumulator)
}
}

0 comments on commit 52ffb4c

Please sign in to comment.