fix #4 update data in container
This commit is contained in:
parent
a1561bc280
commit
165cd07353
@ -55,6 +55,47 @@ sealed class Container {
|
|||||||
*/
|
*/
|
||||||
val isDecrypted: Boolean get() = decryptedData != null
|
val isDecrypted: Boolean get() = decryptedData != null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add one or more recipients to the __decrypted__ container using a standard builder. Note that
|
||||||
|
* [Builder.fill] is not working in this case.
|
||||||
|
*/
|
||||||
|
fun addRecipients(builder: Builder.() -> Unit): Container =
|
||||||
|
if (this is Single) asOpenMulti.addRecipients(builder)
|
||||||
|
else {
|
||||||
|
Builder(this).apply(builder).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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. The new container is also decrypted so you can add
|
||||||
|
* more keys, etc.
|
||||||
|
*/
|
||||||
|
operator fun plus(recipient: EncryptingKey) = addRecipients { key(recipient) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) }
|
||||||
|
|
||||||
|
abstract fun updateData(newPlainData: UByteArray,randomFill: IntRange?=null): Container
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary encoded version. It is desirable to include [Container] as an object, though,
|
||||||
|
* especially when using custom serialization (Json, Boss, etc), it is serializable.
|
||||||
|
* Still, if you need it in binary form, this is a shortcut. You can use [decode] or call
|
||||||
|
* [BipackDecoder.decode] to deserialize the binary form.
|
||||||
|
*/
|
||||||
|
val encoded: UByteArray by lazy {
|
||||||
|
BipackEncoder.encode(this).toUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @suppress
|
* @suppress
|
||||||
* Single-key variant, to conserve space it does not use the main key logic and just encrypts the data.
|
* Single-key variant, to conserve space it does not use the main key logic and just encrypts the data.
|
||||||
@ -89,10 +130,10 @@ sealed class Container {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val asOpenMulti: Container by lazy {
|
private fun reEncrypt(data: UByteArray? = null,f: Builder.()->Unit): Container {
|
||||||
check(isDecrypted) { "container should be decrypted" }
|
check(isDecrypted) { "container should be decrypted" }
|
||||||
create(decryptedData!!) {
|
return create(data ?: decryptedData ?: throw IllegalArgumentException("no data is provided")) {
|
||||||
asOpenedMulti()
|
f()
|
||||||
// The encryption key is known if we just created the container
|
// The encryption key is known if we just created the container
|
||||||
if (reEncryptionKey != null)
|
if (reEncryptionKey != null)
|
||||||
key(reEncryptionKey)
|
key(reEncryptionKey)
|
||||||
@ -120,6 +161,16 @@ sealed class Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val asOpenMulti: Container by lazy {
|
||||||
|
reEncrypt { alwaysMulti() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateData(newPlainData: UByteArray,randomFill: IntRange?): Container =
|
||||||
|
reEncrypt(newPlainData) {
|
||||||
|
randomFill?.let { fill(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +184,6 @@ sealed class Container {
|
|||||||
val encryptedKeys: List<EncryptedKey>, val encryptedMessage: UByteArray,
|
val encryptedKeys: List<EncryptedKey>, val encryptedMessage: UByteArray,
|
||||||
@Transient internal var mainKey: SymmetricKey? = null,
|
@Transient internal var mainKey: SymmetricKey? = null,
|
||||||
@Transient internal var knownPlainData: UByteArray? = null,
|
@Transient internal var knownPlainData: UByteArray? = null,
|
||||||
|
|
||||||
) : Container() {
|
) : Container() {
|
||||||
@Serializable
|
@Serializable
|
||||||
class EncryptedKey(val tag: KeyTag, val cipherData: UByteArray) {
|
class EncryptedKey(val tag: KeyTag, val cipherData: UByteArray) {
|
||||||
@ -176,44 +226,11 @@ sealed class Container {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
override fun updateData(newPlainData: UByteArray, randomFill: IntRange?): Container =
|
||||||
* Add one or more recipients to the __decrypted__ container using a standard builder. Note that
|
Builder(newPlainData, this).apply {
|
||||||
* [Builder.fill] is not working in this case.
|
randomFill?.let { fill(it) }
|
||||||
*/
|
}.build()
|
||||||
fun addRecipients(builder: Builder.() -> Unit): Container =
|
|
||||||
if (this is Single) asOpenMulti.addRecipients(builder)
|
|
||||||
else {
|
|
||||||
Builder(this).apply(builder).build()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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. The new container is also decrypted so you can add
|
|
||||||
* more keys, etc.
|
|
||||||
*/
|
|
||||||
operator fun plus(recipient: EncryptingKey) = addRecipients { key(recipient) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binary encoded version. It is desirable to include [Container] as an object, though,
|
|
||||||
* especially when using custom serialization (Json, Boss, etc), it is serializable.
|
|
||||||
* Still, if you need it in binary form, this is a shortcut. You can use [decode] or call
|
|
||||||
* [BipackDecoder.decode] to deserialize the binary form.
|
|
||||||
*/
|
|
||||||
val encoded: UByteArray by lazy {
|
|
||||||
BipackEncoder.encode(this).toUByteArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -286,14 +303,14 @@ sealed class Container {
|
|||||||
fillRange = range
|
fillRange = range
|
||||||
}
|
}
|
||||||
|
|
||||||
private var makeOpenedMulti = false
|
private var alwaysMulti = 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 asOpenedMulti() {
|
internal fun alwaysMulti() {
|
||||||
makeOpenedMulti = true
|
alwaysMulti = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,8 +320,8 @@ sealed class Container {
|
|||||||
val countNewKeys = plainKeys.size + keyPairs.size
|
val countNewKeys = plainKeys.size + keyPairs.size
|
||||||
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 && parent == null -> throw IllegalArgumentException("Container needs at least one key")
|
||||||
countNewKeys == 1 && makeOpenedMulti == false && parent == null -> {
|
countNewKeys == 1 && alwaysMulti == false && parent == null -> {
|
||||||
createSingle()
|
createSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,10 +371,7 @@ sealed class Container {
|
|||||||
val (sender, recipient) = p
|
val (sender, recipient) = p
|
||||||
eks += Multi.EncryptedKey(sender, recipient, encodedMainKey)
|
eks += Multi.EncryptedKey(sender, recipient, encodedMainKey)
|
||||||
}
|
}
|
||||||
return if (makeOpenedMulti)
|
return Multi(eks, mainKey.encrypt(plainData, fillRange), mainKey, plainData)
|
||||||
Multi(eks, mainKey.encrypt(plainData, fillRange), mainKey, plainData)
|
|
||||||
else
|
|
||||||
Multi(eks, mainKey.encrypt(plainData, fillRange))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ class ContainerTest {
|
|||||||
assertNotNull(d)
|
assertNotNull(d)
|
||||||
assertContentEquals(data, d)
|
assertContentEquals(data, d)
|
||||||
assertTrue { c1.isDecrypted }
|
assertTrue { c1.isDecrypted }
|
||||||
|
|
||||||
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
|
assertFalse { c2.isDecrypted }
|
||||||
|
assertContentEquals(data2, c2.decryptWith(syk1))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -45,6 +50,11 @@ class ContainerTest {
|
|||||||
assertNotNull(d)
|
assertNotNull(d)
|
||||||
assertContentEquals(data, d)
|
assertContentEquals(data, d)
|
||||||
assertTrue { c1.isDecrypted }
|
assertTrue { c1.isDecrypted }
|
||||||
|
|
||||||
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
|
assertFalse { c2.isDecrypted }
|
||||||
|
assertContentEquals(data2, c2.decryptWith(p2.secretKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -67,6 +77,11 @@ class ContainerTest {
|
|||||||
assertNotNull(d)
|
assertNotNull(d)
|
||||||
assertContentEquals(data, d)
|
assertContentEquals(data, d)
|
||||||
assertTrue { c1.isDecrypted }
|
assertTrue { c1.isDecrypted }
|
||||||
|
|
||||||
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
|
assertFalse { c2.isDecrypted }
|
||||||
|
assertContentEquals(data2, c2.decryptWith(p2.secretKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -86,7 +101,7 @@ class ContainerTest {
|
|||||||
key(p1.secretKey to p3.publicKey)
|
key(p1.secretKey to p3.publicKey)
|
||||||
key(p4.publicKey)
|
key(p4.publicKey)
|
||||||
}
|
}
|
||||||
assertFalse { c.isDecrypted }
|
assertTrue { c.isDecrypted }
|
||||||
var c1 = Container.decode(c.encoded)
|
var c1 = Container.decode(c.encoded)
|
||||||
assertFalse { c1.isDecrypted }
|
assertFalse { c1.isDecrypted }
|
||||||
|
|
||||||
@ -104,6 +119,13 @@ class ContainerTest {
|
|||||||
c1 = Container.decode(c.encoded)
|
c1 = Container.decode(c.encoded)
|
||||||
assertFalse { c1.isDecrypted }
|
assertFalse { c1.isDecrypted }
|
||||||
assertContentEquals(data, c1.decryptWith(syk3, p4.secretKey))
|
assertContentEquals(data, c1.decryptWith(syk3, p4.secretKey))
|
||||||
|
|
||||||
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
|
assertTrue { c.isDecrypted }
|
||||||
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
|
assertFalse { c2.isDecrypted }
|
||||||
|
assertContentEquals(data2, c2.decryptWith(syk2))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -119,6 +141,7 @@ class ContainerTest {
|
|||||||
.encodeToUByteArray()
|
.encodeToUByteArray()
|
||||||
|
|
||||||
var c = Container.createWith(data, syk1)
|
var c = Container.createWith(data, syk1)
|
||||||
|
assertTrue { c.isDecrypted }
|
||||||
|
|
||||||
fun expectOpen(k: DecryptingKey) {
|
fun expectOpen(k: DecryptingKey) {
|
||||||
val c1 = Container.decode(c.encoded)
|
val c1 = Container.decode(c.encoded)
|
||||||
@ -158,6 +181,13 @@ class ContainerTest {
|
|||||||
expectOpen(p3.secretKey)
|
expectOpen(p3.secretKey)
|
||||||
expectNotOpen(syk3)
|
expectNotOpen(syk3)
|
||||||
expectOpen(p4.secretKey)
|
expectOpen(p4.secretKey)
|
||||||
|
|
||||||
|
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||||
|
assertTrue { c.isDecrypted }
|
||||||
|
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||||
|
assertFalse { c2.isDecrypted }
|
||||||
|
assertContentEquals(data2, c2.decryptWith(p4.secretKey))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -195,4 +225,80 @@ class ContainerTest {
|
|||||||
expectNotOpen(syk2)
|
expectNotOpen(syk2)
|
||||||
expectOpen(p3.secretKey)
|
expectOpen(p3.secretKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMixedOps1() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val syk1 = SymmetricKey.random()
|
||||||
|
val syk2 = SymmetricKey.random()
|
||||||
|
val syk3 = SymmetricKey.random()
|
||||||
|
val p1 = Asymmetric.generateKeys()
|
||||||
|
val p2 = Asymmetric.generateKeys()
|
||||||
|
val p3 = Asymmetric.generateKeys()
|
||||||
|
val p4 = Asymmetric.generateKeys()
|
||||||
|
val data = "Translating the name 'Sergey Chernov' from Russian to archaic Sanskrit would be 'Ramo Krishna'"
|
||||||
|
.encodeToUByteArray()
|
||||||
|
|
||||||
|
var c = Container.createWith(data, p1.secretKey to p3.publicKey)
|
||||||
|
|
||||||
|
fun expectOpen(testData: UByteArray,vararg keys: DecryptingKey) {
|
||||||
|
val c1 = Container.decode(c.encoded)
|
||||||
|
for( k in keys)
|
||||||
|
assertContentEquals(testData, c1.decryptWith(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectNotOpen(vararg keys: DecryptingKey) {
|
||||||
|
val c1 = Container.decode(c.encoded)
|
||||||
|
for(k in keys) assertNull(c1.decryptWith(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
expectNotOpen(syk1)
|
||||||
|
expectOpen(data, p3.secretKey)
|
||||||
|
|
||||||
|
// c = Container.decode(c.encoded)
|
||||||
|
|
||||||
|
val data2 = "Cocktails have a delicious, complex taste".encodeToUByteArray()
|
||||||
|
c = (c + syk3 + (p1.secretKey to p4.publicKey)).updateData(data2)
|
||||||
|
expectNotOpen(syk2, syk1, p1.secretKey, p2.secretKey )
|
||||||
|
expectOpen(data2, syk3, p3.secretKey, p4.secretKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMixedOps2() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val syk1 = SymmetricKey.random()
|
||||||
|
val syk2 = SymmetricKey.random()
|
||||||
|
val syk3 = SymmetricKey.random()
|
||||||
|
val p1 = Asymmetric.generateKeys()
|
||||||
|
val p2 = Asymmetric.generateKeys()
|
||||||
|
val p3 = Asymmetric.generateKeys()
|
||||||
|
val p4 = Asymmetric.generateKeys()
|
||||||
|
val data = "Translating the name 'Sergey Chernov' from Russian to archaic Sanskrit would be 'Ramo Krishna'"
|
||||||
|
.encodeToUByteArray()
|
||||||
|
|
||||||
|
var c = Container.createWith(data, p1.secretKey to p3.publicKey)
|
||||||
|
|
||||||
|
fun expectOpen(testData: UByteArray,vararg keys: DecryptingKey) {
|
||||||
|
val c1 = Container.decode(c.encoded)
|
||||||
|
for( k in keys)
|
||||||
|
assertContentEquals(testData, c1.decryptWith(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectNotOpen(vararg keys: DecryptingKey) {
|
||||||
|
val c1 = Container.decode(c.encoded)
|
||||||
|
for(k in keys) assertNull(c1.decryptWith(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
expectNotOpen(syk1)
|
||||||
|
expectOpen(data, p3.secretKey)
|
||||||
|
|
||||||
|
c = Container.decode(c.encoded)
|
||||||
|
c.decryptWith(p3.secretKey)
|
||||||
|
|
||||||
|
val data2 = "Cocktails have a delicious, complex taste".encodeToUByteArray()
|
||||||
|
c = (c + syk3 + (p1.secretKey to p4.publicKey)).updateData(data2)
|
||||||
|
expectNotOpen(syk2, syk1, p1.secretKey, p2.secretKey )
|
||||||
|
expectOpen(data2, syk3, p3.secretKey, p4.secretKey)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user