- BinaryId is now open
- Container returns data on the keys used to decrypt it
This commit is contained in:
parent
d0fa74e089
commit
14a63a05c2
@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "net.sergeych"
|
||||
version = "0.5.5"
|
||||
version = "0.5.6-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -4,6 +4,7 @@ import com.ionspin.kotlin.crypto.box.Box
|
||||
import com.ionspin.kotlin.crypto.box.BoxCorruptedOrTamperedDataException
|
||||
import com.ionspin.kotlin.crypto.box.crypto_box_NONCEBYTES
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.sergeych.bipack.BipackDecoder
|
||||
import net.sergeych.bipack.BipackEncoder
|
||||
import net.sergeych.crypto2.Asymmetric.Message
|
||||
import net.sergeych.crypto2.Asymmetric.generateKeys
|
||||
@ -64,6 +65,16 @@ object Asymmetric {
|
||||
}
|
||||
|
||||
val encoded: UByteArray by lazy { BipackEncoder.encode(this).toUByteArray() }
|
||||
|
||||
companion object {
|
||||
fun decode(packed: UByteArray): Message = try {
|
||||
BipackDecoder.decode(packed)
|
||||
}
|
||||
catch(x: Exception) {
|
||||
throw DecryptionFailedException("can't decode message structure",x)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,6 +108,7 @@ object Asymmetric {
|
||||
fun newSecretKey() = SecretKey.new()
|
||||
|
||||
val nonceBytesLength = crypto_box_NONCEBYTES
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,7 @@ import net.sergeych.mp_tools.decodeBase64Url
|
||||
import kotlin.random.Random
|
||||
|
||||
@Serializable
|
||||
class BinaryId private constructor (
|
||||
open class BinaryId protected constructor (
|
||||
val id: UByteArray,
|
||||
) : Comparable<BinaryId> {
|
||||
|
||||
@ -29,6 +29,25 @@ class BinaryId private constructor (
|
||||
|
||||
private val innerData: UByteArray by lazy { id.sliceArray( 1..< id.size-1 ) }
|
||||
|
||||
/**
|
||||
* The id body: all the bytes except check and magic. These could carry useful information.
|
||||
*/
|
||||
val body: UByteArray by lazy { id.sliceArray( 0 until id.size-2 ) }
|
||||
|
||||
val asVerifyingKey: VerifyingKey by lazy {
|
||||
if( magic != KeysmagicNumber.defaultVerifying.ordinal)
|
||||
throw InvalidException("It is not a veryfing key: magic=$magic, required ${KeysmagicNumber.defaultVerifying.ordinal}")
|
||||
check(body.size == 32)
|
||||
VerifyingPublicKey(body)
|
||||
}
|
||||
|
||||
val asPublicKey: PublicKey by lazy {
|
||||
if( magic != KeysmagicNumber.defaultAssymmetric.ordinal)
|
||||
throw InvalidException("It is not a veryfing key: magic=$magic, required ${KeysmagicNumber.defaultAssymmetric.ordinal}")
|
||||
check(body.size == 32)
|
||||
PublicKey(body)
|
||||
}
|
||||
|
||||
override fun toString(): String = id.encodeToBase64Url()
|
||||
|
||||
override fun compareTo(other: BinaryId): Int {
|
||||
|
@ -123,7 +123,7 @@ sealed class Container {
|
||||
* Update the data in the decrypted container. It keeps the same set of keys and update
|
||||
* the data only.
|
||||
*/
|
||||
abstract fun updateData(newPlainData: UByteArray,randomFill: IntRange?=null): Container
|
||||
abstract fun updateData(newPlainData: UByteArray, randomFill: IntRange? = null): Container
|
||||
|
||||
/**
|
||||
* Binary encoded version. It is desirable to include [Container] as an object, though,
|
||||
@ -135,6 +135,23 @@ sealed class Container {
|
||||
BipackEncoder.encode(this).toUByteArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* When the container is decrypted, the [KeyId] of the key that unlocked it,
|
||||
* otherwise null.
|
||||
*/
|
||||
abstract val decryptedWithKeyId: KeyId?
|
||||
|
||||
/**
|
||||
* If the container _is decrypted by the [PublicKey]_, e.g., using secret key encryption,
|
||||
* contains the [PublicKey] that corresponds the [SecretKey] 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
|
||||
var authorisedByKey: PublicKey? = null
|
||||
protected set
|
||||
|
||||
|
||||
/**
|
||||
* @suppress
|
||||
@ -152,6 +169,9 @@ sealed class Container {
|
||||
@Transient
|
||||
private var decryptedWithKey: DecryptingKey? = null
|
||||
|
||||
override val decryptedWithKeyId: KeyId?
|
||||
get() = decryptedWithKey?.id
|
||||
|
||||
init {
|
||||
decryptedData = creationData
|
||||
}
|
||||
@ -163,6 +183,9 @@ sealed class Container {
|
||||
kotlin.runCatching { k.decrypt(encryptedMessage) }.getOrNull()?.let {
|
||||
decryptedData = it
|
||||
decryptedWithKey = k
|
||||
if( k is SecretKey) {
|
||||
authorisedByKey = Asymmetric.Message.decode(encryptedMessage).senderPublicKey
|
||||
}
|
||||
return it
|
||||
}
|
||||
}
|
||||
@ -170,7 +193,7 @@ sealed class Container {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun reEncrypt(data: UByteArray? = null,f: Builder.()->Unit): Container {
|
||||
private fun reEncrypt(data: UByteArray? = null, f: Builder.() -> Unit): Container {
|
||||
check(isDecrypted) { "container should be decrypted" }
|
||||
return create(data ?: decryptedData ?: throw IllegalArgumentException("no data is provided")) {
|
||||
f()
|
||||
@ -204,7 +227,7 @@ sealed class Container {
|
||||
reEncrypt { alwaysMulti() }
|
||||
}
|
||||
|
||||
override fun updateData(newPlainData: UByteArray,randomFill: IntRange?): Container =
|
||||
override fun updateData(newPlainData: UByteArray, randomFill: IntRange?): Container =
|
||||
reEncrypt(newPlainData) {
|
||||
randomFill?.let { fill(it) }
|
||||
}
|
||||
@ -220,7 +243,7 @@ sealed class Container {
|
||||
val encryptedKeys: List<EncryptedKey>, val encryptedMessage: UByteArray,
|
||||
@Transient internal var mainKey: SymmetricKey? = null,
|
||||
@Transient internal var knownPlainData: UByteArray? = null,
|
||||
) : Container() {
|
||||
) : Container() {
|
||||
@Serializable
|
||||
class EncryptedKey(val tag: KeyId, val cipherData: UByteArray) {
|
||||
constructor(key: EncryptingKey, encodeMainKey: UByteArray) :
|
||||
@ -240,6 +263,10 @@ sealed class Container {
|
||||
knownPlainData?.let { decryptedData = it }
|
||||
}
|
||||
|
||||
@Transient
|
||||
override var decryptedWithKeyId: KeyId? = null
|
||||
private set
|
||||
|
||||
override fun decryptWith(keyRing: UniversalRing): UByteArray? {
|
||||
decryptedData?.let { return it }
|
||||
for (key in keyRing.decryptingKeys) {
|
||||
@ -251,10 +278,13 @@ sealed class Container {
|
||||
key.decrypt(encryptedKey.cipherData).toByteArray()
|
||||
)
|
||||
}.getOrNull()?.let { k ->
|
||||
println(k)
|
||||
if (kotlin.runCatching { decryptedData = k.decrypt(encryptedMessage) }.isFailure)
|
||||
throw InvalidContainerException()
|
||||
decryptedWithKeyId = key.id
|
||||
mainKey = k
|
||||
if( key is SecretKey) {
|
||||
authorisedByKey = Asymmetric.Message.decode(encryptedKey.cipherData).senderPublicKey
|
||||
}
|
||||
}
|
||||
if (decryptedData != null) return decryptedData
|
||||
}
|
||||
@ -438,11 +468,11 @@ sealed class Container {
|
||||
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T>encrypt(plainData: T, vararg keys: EncryptingKey): UByteArray =
|
||||
inline fun <reified T> encrypt(plainData: T, vararg keys: EncryptingKey): UByteArray =
|
||||
createWith(BipackEncoder.encode<T>(plainData).toUByteArray(), *keys).encoded
|
||||
|
||||
inline fun <reified T>decrypt(cipherData: UByteArray, vararg keys: DecryptingKey): T? =
|
||||
decryptAsUBytes(cipherData,*keys)?.let { BipackDecoder.decode<T>(it.asByteArray())}
|
||||
inline fun <reified T> decrypt(cipherData: UByteArray, vararg keys: DecryptingKey): T? =
|
||||
decryptAsUBytes(cipherData, *keys)?.let { BipackDecoder.decode<T>(it.asByteArray()) }
|
||||
|
||||
fun decryptAsUBytes(cipherData: UByteArray, vararg keys: DecryptingKey): UByteArray? =
|
||||
decode(cipherData).decryptWith(*keys)
|
||||
|
@ -7,7 +7,8 @@ import net.sergeych.crypto2.SymmetricKey.WithNonce
|
||||
|
||||
/**
|
||||
* Some key able to perform decrypting. It is not serializable by purpose, as not all such
|
||||
* keys are wise to transfer/save. Concrete implementations are, like [SymmetricKey].
|
||||
* keys are wise to transfer/save. Concrete implementations are, like [SymmetricKey] or
|
||||
* [SecretKey].
|
||||
*/
|
||||
interface DecryptingKey : NonceBased, KeyInstance {
|
||||
/**
|
||||
|
@ -5,7 +5,6 @@ import com.ionspin.kotlin.crypto.scalarmult.ScalarMultiplication
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.sergeych.bipack.BipackDecoder
|
||||
|
||||
/**
|
||||
* The secret key used in public-key encryption; it is used to _decrypt_ data encrypted with its
|
||||
@ -62,8 +61,7 @@ class SecretKey(
|
||||
* Decrypt without a nonce as edwards curve decryption does not need it
|
||||
*/
|
||||
override fun decrypt(cipherData: UByteArray): UByteArray {
|
||||
val message: Asymmetric.Message = BipackDecoder.decode(cipherData.asByteArray())
|
||||
return message.decrypt(this)
|
||||
return Asymmetric.Message.decode(cipherData).decrypt(this)
|
||||
}
|
||||
|
||||
override val magic: KeysmagicNumber = KeysmagicNumber.defaultAssymmetric
|
||||
|
@ -50,11 +50,40 @@ class ContainerTest {
|
||||
assertNotNull(d)
|
||||
assertContentEquals(data, d)
|
||||
assertTrue { c1.isDecrypted }
|
||||
assertEquals(p2.publicKey.id, c1.decryptedWithKeyId)
|
||||
assertEquals(p1.publicKey, c1.authorisedByKey)
|
||||
|
||||
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||
assertFalse { c2.isDecrypted }
|
||||
assertContentEquals(data2, c2.decryptWith(p2.secretKey))
|
||||
|
||||
}
|
||||
@Test
|
||||
fun testMultiplePair() = runTest {
|
||||
initCrypto()
|
||||
val p1 = Asymmetric.generateKeys()
|
||||
val p2 = Asymmetric.generateKeys()
|
||||
val p3 = Asymmetric.generateKeys()
|
||||
val p4 = Asymmetric.generateKeys()
|
||||
val data = "sergeych, ohm many.".encodeToUByteArray()
|
||||
|
||||
val c = Container.createWith(data, p1.secretKey to p2.publicKey, p1.secretKey to p4.publicKey)
|
||||
assertTrue { c.isDecrypted }
|
||||
val c1 = Container.decode(c.encoded)
|
||||
assertFalse { c1.isDecrypted }
|
||||
|
||||
assertNull(c1.decryptWith(p3.secretKey, p1.secretKey))
|
||||
val d = c1.decryptWith(p3.secretKey, p4.secretKey)
|
||||
assertNotNull(d)
|
||||
assertContentEquals(data, d)
|
||||
assertTrue { c1.isDecrypted }
|
||||
assertEquals(p4.publicKey.id, c1.decryptedWithKeyId)
|
||||
assertEquals(p1.publicKey, c1.authorisedByKey)
|
||||
|
||||
val data2 = "To push unpushinable".encodeToUByteArray()
|
||||
val c2 = Container.decode(c.updateData(data2).encoded)
|
||||
assertFalse { c2.isDecrypted }
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -164,7 +164,9 @@ class KeysTest {
|
||||
assertContentEquals(pk1.keyBytes, pk1.id.binaryTag.take(32).toUByteArray())
|
||||
assertContentEquals(pk1.keyBytes, sk1.id.binaryTag.take(32).toUByteArray())
|
||||
|
||||
assertEquals(pk1, pk1.id.id.asPublicKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun asymmetricKeySerializationTest() = runTest {
|
||||
initCrypto()
|
||||
@ -271,6 +273,7 @@ class KeysTest {
|
||||
// not hashed!
|
||||
assertContentEquals(k.verifyingKey.keyBytes, dk2.id.binaryTag.take(32).toUByteArray())
|
||||
assertContentEquals(k.verifyingKey.keyBytes, dk1.id.binaryTag.take(32).toUByteArray())
|
||||
|
||||
// and restored from id should be the same:
|
||||
assertEquals( k.verifyingKey, dk2.id.id.asVerifyingKey)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user