forked from sergeych/crypto2
		
	- 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 {
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ import net.sergeych.crypto2.Container.Companion.createWith
 | 
			
		||||
 * decrypt it. This is sometimes very important to be able to add recipients to the message
 | 
			
		||||
 * keeping existing recipients you know no keys of, or update the message when only one of the
 | 
			
		||||
 * keys is known.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * - [createWith] for more on create a new container
 | 
			
		||||
 * - [decryptWith] to decrypt
 | 
			
		||||
 * - [addRecipients] and various [plus] operators to add recipients
 | 
			
		||||
@ -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