From 78383fbeceefd0a8207a6ba92b9abc8a493d0443 Mon Sep 17 00:00:00 2001 From: sergeych Date: Fri, 28 Jun 2024 15:35:48 +0700 Subject: [PATCH] more sugar to be like unicrypto by functions --- build.gradle.kts | 2 +- .../kotlin/net/sergeych/crypto2/Container.kt | 11 +++++++++++ .../kotlin/net/sergeych/crypto2/PBKD.kt | 6 ++++-- .../kotlin/net/sergeych/crypto2/SealedBox.kt | 6 ++++++ .../kotlin/net/sergeych/crypto2/kdf.kt | 18 ++++++++++++++++++ src/commonTest/kotlin/PBKDTest.kt | 13 +++++++++++++ 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c6ab2ec..e22c7b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "net.sergeych" -version = "0.4.1-SNAPSHOT" +version = "0.4.2-SNAPSHOT" repositories { mavenCentral() diff --git a/src/commonMain/kotlin/net/sergeych/crypto2/Container.kt b/src/commonMain/kotlin/net/sergeych/crypto2/Container.kt index e8254d9..9192b36 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto2/Container.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/Container.kt @@ -436,6 +436,17 @@ sealed class Container { fun create(plainData: UByteArray, builder: Builder.() -> Unit) = Builder(plainData).also { it.builder() }.build() + + @Suppress("unused") + inline fun encrypt(plainData: T, vararg keys: EncryptingKey): UByteArray = + createWith(BipackEncoder.encode(plainData).toUByteArray(), *keys).encoded + + inline fun decrypt(cipherData: UByteArray, vararg keys: DecryptingKey): T? = + decryptAsUBytes(cipherData,*keys)?.let { BipackDecoder.decode(it.toByteArray())} + + fun decryptAsUBytes(cipherData: UByteArray, vararg keys: DecryptingKey): UByteArray? = + decode(cipherData).decryptWith(*keys) + /** * Create container using one or more [EncryptingKey] and a builder, see [create] * for builder usage sample. diff --git a/src/commonMain/kotlin/net/sergeych/crypto2/PBKD.kt b/src/commonMain/kotlin/net/sergeych/crypto2/PBKD.kt index 1349061..9385d11 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto2/PBKD.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/PBKD.kt @@ -14,7 +14,7 @@ object PBKD { * with [deriveKey] */ @Serializable - class Params( + data class Params( val kdf: KDF, @Unsigned val startOffset: Int, @@ -76,7 +76,9 @@ object PBKD { require(keysCount > 0) { "keysCount must be positive" } val keyLength = SymmetricKey.keyLength val totalSize = keysCount * keyLength - val kdf = KDF.Argon.create(complexity, salt, totalSize) + check(salt.size >= KDF.Argon.saltSize) + val salt2 = if( salt.size > KDF.Argon.saltSize) salt.sliceArray( 0 ..< KDF.Argon.saltSize) else salt + val kdf = KDF.Argon.create(complexity, salt2, totalSize) return (0 ..< keysCount).map { Params(kdf, it*keyLength, keyLength).deriveKey(password) } } diff --git a/src/commonMain/kotlin/net/sergeych/crypto2/SealedBox.kt b/src/commonMain/kotlin/net/sergeych/crypto2/SealedBox.kt index 58bcbee..44347b0 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto2/SealedBox.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/SealedBox.kt @@ -3,6 +3,7 @@ package net.sergeych.crypto2 import kotlinx.datetime.Instant import kotlinx.serialization.Serializable import kotlinx.serialization.Transient +import net.sergeych.bipack.BipackEncoder /** * Multi-signed data box. Do not use the constructori directly, use [SealedBox.create] @@ -65,6 +66,7 @@ class SealedBox( } } + val encoded: UByteArray by lazy { BipackEncoder.encode(this).toUByteArray() } companion object { /** @@ -80,5 +82,9 @@ class SealedBox( fun create(data: UByteArray, vararg keys: SigningSecretKey): SealedBox { return SealedBox(data, keys.map { it.seal(data) }, false) } + + inline fun encode(value: T, vararg keys: SigningSecretKey): UByteArray = + create(BipackEncoder.encode(value).toUByteArray(), *keys).encoded + } } \ No newline at end of file diff --git a/src/commonMain/kotlin/net/sergeych/crypto2/kdf.kt b/src/commonMain/kotlin/net/sergeych/crypto2/kdf.kt index 1149979..46c6ef1 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto2/kdf.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/kdf.kt @@ -4,6 +4,7 @@ import com.ionspin.kotlin.crypto.pwhash.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.sergeych.bipack.Unsigned +import net.sergeych.crypto2.PBKD.Params @Serializable sealed class KDF { @@ -16,6 +17,16 @@ sealed class KDF { abstract fun derive(password: String): UByteArray + fun deriveMultiple(password: String,count: Int) : List { + val bytes = derive(password) + val ks = SymmetricKey.keyLength + check( ks * count <= bytes.size ) { "KDF is too short for $count keys: ${bytes.size} we need ${ks*count}"} + return (0 ..< count).map { + val base = it*ks + Params(this, base, ks).deriveKey(password) + } + } + @Serializable @SerialName("argon") data class Argon( @@ -114,6 +125,13 @@ sealed class KDF { } } + companion object { + @Suppress("unused") + fun creteDefault(keySize: Int, complexity: Complexity, salt: UByteArray = Argon.randomSalt()): KDF { + return Argon.create(complexity,salt,keySize) + } + } + data class Instance(val kdf: KDF,val password: String) } diff --git a/src/commonTest/kotlin/PBKDTest.kt b/src/commonTest/kotlin/PBKDTest.kt index aa0b2d2..85a2495 100644 --- a/src/commonTest/kotlin/PBKDTest.kt +++ b/src/commonTest/kotlin/PBKDTest.kt @@ -3,6 +3,7 @@ import net.sergeych.crypto2.PBKD import net.sergeych.crypto2.initCrypto import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull class PBKDTest { @@ -32,5 +33,17 @@ class PBKDTest { assertEquals(i.kdp, kx.id.kdp) } + val (y1,y2,y3) = k1.id.kdp!!.kdf.deriveMultiple("foobar", 3) + for( (a,b) in listOf(y1,y2,y3).zip(listOf(k1,k2,k3))) { + assertEquals(a,b) + assertNotNull(a.id.kdp) + assertNotNull(b.id.kdp) + assertEquals(a.id.kdp, b.id.kdp) + assertEquals(a.id, b.id) + } + assertEquals(k1, y1) + assertEquals(k2, y2) + assertEquals(k3, y3) + } } \ No newline at end of file