PBKD with tests. Some improvements in keys listing
This commit is contained in:
		
							parent
							
								
									46a8b5bc06
								
							
						
					
					
						commit
						9efa763b45
					
				@ -125,7 +125,9 @@ object Asymmetric {
 | 
			
		||||
    @SerialName("encp")
 | 
			
		||||
    class PublicKey(override val keyBytes: UByteArray) : UniversalKey(), EncryptingKey {
 | 
			
		||||
 | 
			
		||||
        override val magick: KeysMagickNumber = KeysMagickNumber.defaultAssymmetric
 | 
			
		||||
        override val magic: KeysmagicNumber = KeysmagicNumber.defaultAssymmetric
 | 
			
		||||
        @Transient
 | 
			
		||||
        override val label: String = "pub"
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Create an anonymous message that could be decrypted only with the [SecretKey] that corresponds this.
 | 
			
		||||
@ -193,6 +195,8 @@ object Asymmetric {
 | 
			
		||||
        val _cachedPublicKey: PublicKey? = null,
 | 
			
		||||
    ) : DecryptingKey, UniversalKey() {
 | 
			
		||||
 | 
			
		||||
        @Transient
 | 
			
		||||
        override val label: String = "sec"
 | 
			
		||||
        /**
 | 
			
		||||
         * Decrypt with authentication checks the message which must have [Message.senderPublicKey] set.
 | 
			
		||||
         * Use [decryptWithSenderKey] otherwise. Note that the authenticated encryption is always use, even if
 | 
			
		||||
@ -238,7 +242,7 @@ object Asymmetric {
 | 
			
		||||
            return message.decrypt(this)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override val magick: KeysMagickNumber = KeysMagickNumber.defaultAssymmetric
 | 
			
		||||
        override val magic: KeysmagicNumber = KeysmagicNumber.defaultAssymmetric
 | 
			
		||||
        override val id: KeyId by lazy { publicKey.id }
 | 
			
		||||
 | 
			
		||||
        override val nonceBytesLength: Int
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ class BinaryId private constructor (
 | 
			
		||||
    class IncomparableException(text: String) : IllegalArgumentException(text)
 | 
			
		||||
 | 
			
		||||
    @Transient
 | 
			
		||||
    val magick: Int = run {
 | 
			
		||||
    val magic: Int = run {
 | 
			
		||||
        if (id.size < 4) throw InvalidException("BinaryId is too short")
 | 
			
		||||
        val crc = id.last()
 | 
			
		||||
        val rest = id.dropLast(1).toUByteArray()
 | 
			
		||||
@ -32,7 +32,7 @@ class BinaryId private constructor (
 | 
			
		||||
    override fun toString(): String = id.encodeToBase64Url()
 | 
			
		||||
 | 
			
		||||
    override fun compareTo(other: BinaryId): Int {
 | 
			
		||||
        if (other.magick != magick) throw IncomparableException("Mask mismatch (my=$magick their=${other.magick})")
 | 
			
		||||
        if (other.magic != magic) throw IncomparableException("Mask mismatch (my=$magic their=${other.magic})")
 | 
			
		||||
        val id1 = other.id
 | 
			
		||||
        if (id1.size != id.size) throw IncomparableException("different sizes")
 | 
			
		||||
        for ((a, b) in innerData.zip(other.innerData)) {
 | 
			
		||||
@ -66,11 +66,11 @@ class BinaryId private constructor (
 | 
			
		||||
            BinaryId(str.decodeBase64Url().toUByteArray())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        fun createFromBytes(magick: Int, bytes: ByteArray): BinaryId = createFromUBytes(magick, bytes.toUByteArray())
 | 
			
		||||
        fun createFromBytes(magic: Int, bytes: ByteArray): BinaryId = createFromUBytes(magic, bytes.toUByteArray())
 | 
			
		||||
 | 
			
		||||
        fun createFromUBytes(magick: Int, bytes: UByteArray): BinaryId {
 | 
			
		||||
        fun createFromUBytes(magic: Int, bytes: UByteArray): BinaryId {
 | 
			
		||||
            val crc = CRC8()
 | 
			
		||||
            val mn = magick.toUByte()
 | 
			
		||||
            val mn = magic.toUByte()
 | 
			
		||||
            crc.update(bytes)
 | 
			
		||||
            crc.update(mn)
 | 
			
		||||
            return BinaryId(UByteArray(bytes.size + 2).also {
 | 
			
		||||
@ -82,13 +82,13 @@ class BinaryId private constructor (
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun createRandom(magickNumber: Int, size: Int=16) =
 | 
			
		||||
            createFromBytes(magickNumber, Random.Default.nextBytes(size-2))
 | 
			
		||||
        fun createRandom(magicNumber: Int, size: Int=16) =
 | 
			
		||||
            createFromBytes(magicNumber, Random.Default.nextBytes(size-2))
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Encode a string as UTF and create a binaryId from its bytes and provided magick.
 | 
			
		||||
         * Encode a string as UTF and create a binaryId from its bytes and provided magic.
 | 
			
		||||
         */
 | 
			
		||||
        fun createFromString(magickNumber: Int, text: String): BinaryId =
 | 
			
		||||
            createFromUBytes(magickNumber, text.encodeToUByteArray())
 | 
			
		||||
        fun createFromString(magicNumber: Int, text: String): BinaryId =
 | 
			
		||||
            createFromUBytes(magicNumber, text.encodeToUByteArray())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
 | 
			
		||||
/**
 | 
			
		||||
 * Key Identity. Can be used to find matching keys for decryption or verifying, etc. The identity
 | 
			
		||||
 * may contain [KDF] parameters if the corresponding key could be derived from a password.
 | 
			
		||||
 * Note that [kdf] part is not respected in [equals].
 | 
			
		||||
 * Note that [kdp] part is not respected in [equals].
 | 
			
		||||
 *
 | 
			
		||||
 * Important. `KeyId` of matching keys are the same, so you can use it to identify
 | 
			
		||||
 * and find matching keys in the [UniversalRing], etc. For example:
 | 
			
		||||
@ -16,16 +16,23 @@ import kotlinx.serialization.Serializable
 | 
			
		||||
 * - [SigningSecretKey] and corresponding [VerifyingKey] have the same `KeyId`. Use it to pick a proper key for
 | 
			
		||||
 *   signing from a ring with [UniversalRing.findKey]
 | 
			
		||||
 *
 | 
			
		||||
 * See [PBKD.Params.deriveKey] for deriving keys from id.
 | 
			
		||||
 *
 | 
			
		||||
 * @param id actual id used in equality test amd hash code generation. `Id` of the matching keys is the same.
 | 
			
		||||
 * @param kdp optional key derivation parameters. Does not affect equality. Allow deriving the key from proper
 | 
			
		||||
 *            password, see above.
 | 
			
		||||
 */
 | 
			
		||||
@Serializable
 | 
			
		||||
data class KeyId(val id: BinaryId, val kdf: PBKD.Params?=null ) {
 | 
			
		||||
data class KeyId(val id: BinaryId, val kdp: PBKD.Params?=null ) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Binary array representation of the [id], not including the [kdf]. Used in [SafeKeyExchange]
 | 
			
		||||
     * Binary array representation of the [id], not including the [kdp]. Used in [SafeKeyExchange]
 | 
			
		||||
     * and other key exchanges to generate session tokens, etc.
 | 
			
		||||
     */
 | 
			
		||||
    val binaryTag: UByteArray by lazy { id.id }
 | 
			
		||||
 | 
			
		||||
    val keymagic: KeysmagicNumber by lazy { KeysmagicNumber.entries[id.magic]}
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (this === other) return true
 | 
			
		||||
        if (other !is KeyId) return false
 | 
			
		||||
@ -38,6 +45,6 @@ data class KeyId(val id: BinaryId, val kdf: PBKD.Params?=null ) {
 | 
			
		||||
 | 
			
		||||
    override fun toString() = id.toString()
 | 
			
		||||
 | 
			
		||||
    constructor(magickNumber: KeysMagickNumber, keyData: UByteArray, kdp: PBKD.Params?=null)
 | 
			
		||||
    : this(BinaryId.createFromUBytes(magickNumber.number, blake2b3l(keyData)), kdp)
 | 
			
		||||
    constructor(magicNumber: KeysmagicNumber, keyData: UByteArray, kdp: PBKD.Params?=null)
 | 
			
		||||
    : this(BinaryId.createFromUBytes(magicNumber.ordinal, blake2b3l(keyData)), kdp)
 | 
			
		||||
}
 | 
			
		||||
@ -1,18 +1,11 @@
 | 
			
		||||
package net.sergeych.crypto2
 | 
			
		||||
 | 
			
		||||
enum class KeysMagickNumber(val number: Int) {
 | 
			
		||||
    defaultAssymmetric(0),
 | 
			
		||||
    defaultSymmetric(1),
 | 
			
		||||
    defaultSession(2),
 | 
			
		||||
    defaultSigning(3),
 | 
			
		||||
    defaultVerifying(4),
 | 
			
		||||
enum class KeysmagicNumber(val label: String) {
 | 
			
		||||
    Unknown( "???"),
 | 
			
		||||
    defaultAssymmetric( "asm"),
 | 
			
		||||
    defaultSymmetric( "sym"),
 | 
			
		||||
    defaultSession( "ssn"),
 | 
			
		||||
    defaultVerifying( "ver"),
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        val forNumber = entries.map { it.number to it }.toMap()
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun findFor(binaryId: BinaryId): KeysMagickNumber? = forNumber[binaryId.magick]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -47,8 +47,8 @@ object PBKD {
 | 
			
		||||
        fun deriveKey(password: String, keyId: KeyId? = null): SymmetricKey {
 | 
			
		||||
            // check key id sanity
 | 
			
		||||
            keyId?.let {
 | 
			
		||||
                if (it.id.magick != KeysMagickNumber.defaultSymmetric.number) {
 | 
			
		||||
                    throw NotSupportedException("deriving key of type ${it.id.magick} is not implemented")
 | 
			
		||||
                if (it.id.magic != KeysmagicNumber.defaultSymmetric.ordinal) {
 | 
			
		||||
                    throw NotSupportedException("deriving key of type ${it.id.magic} is not implemented")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // this will not be ok when we support other key types:
 | 
			
		||||
@ -56,11 +56,7 @@ object PBKD {
 | 
			
		||||
                throw NotSupportedException("can't create key from $length byte(s), required ${SymmetricKey.keyLength}")
 | 
			
		||||
 | 
			
		||||
            // derive
 | 
			
		||||
            val key = SymmetricKey(
 | 
			
		||||
                deriveUBytes(password)
 | 
			
		||||
                    .sliceArray(startOffset..<startOffset + SymmetricKey.keyLength),
 | 
			
		||||
                this
 | 
			
		||||
            )
 | 
			
		||||
            val key = SymmetricKey(deriveUBytes(password),this)
 | 
			
		||||
 | 
			
		||||
            // check if we can
 | 
			
		||||
            if (keyId != null && keyId != key.id)
 | 
			
		||||
@ -121,6 +117,15 @@ object PBKD {
 | 
			
		||||
            entry.data!!
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generated cache bytes are cached and present in memory probably longer than needed.
 | 
			
		||||
     * After this call, the password derivation cache is clear and the new deriving will
 | 
			
		||||
     * be performed.
 | 
			
		||||
     */
 | 
			
		||||
    fun clearCache() {
 | 
			
		||||
        op.invoke { cache.clear() }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,8 +22,6 @@ class SigningPublicKey(override val keyBytes: UByteArray) : UniversalKey(), Veri
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = "Pub:${super.toString()}"
 | 
			
		||||
 | 
			
		||||
    @Transient
 | 
			
		||||
    override val magick: KeysMagickNumber = KeysMagickNumber.defaultVerifying
 | 
			
		||||
    override val magic: KeysmagicNumber = KeysmagicNumber.defaultVerifying
 | 
			
		||||
}
 | 
			
		||||
@ -28,12 +28,10 @@ class SigningSecretKey(
 | 
			
		||||
    override fun seal(message: UByteArray, expiresAt: Instant?): Seal =
 | 
			
		||||
        Seal.create(this, message, now(), expiresAt)
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = "Sct:${super.toString()}"
 | 
			
		||||
 | 
			
		||||
    override val id: KeyId = verifyingKey.id
 | 
			
		||||
 | 
			
		||||
    @Transient
 | 
			
		||||
    override val magick = KeysMagickNumber.defaultSigning
 | 
			
		||||
    override val label: String
 | 
			
		||||
        get() = "sig"
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -23,10 +23,10 @@ import kotlinx.serialization.Transient
 | 
			
		||||
class SymmetricKey(
 | 
			
		||||
    override val keyBytes: UByteArray,
 | 
			
		||||
    @Transient
 | 
			
		||||
    val pbkdfParams: PBKD.Params?=null
 | 
			
		||||
    private val pbkdfParams: PBKD.Params?=null
 | 
			
		||||
) : EncryptingKey, DecryptingKey, UniversalKey() {
 | 
			
		||||
 | 
			
		||||
    override val magick: KeysMagickNumber = KeysMagickNumber.defaultSymmetric
 | 
			
		||||
    override val magic: KeysmagicNumber = KeysmagicNumber.defaultSymmetric
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @suppress
 | 
			
		||||
@ -46,7 +46,7 @@ class SymmetricKey(
 | 
			
		||||
    override val nonceBytesLength: Int = nonceLength
 | 
			
		||||
 | 
			
		||||
    override val id by lazy {
 | 
			
		||||
        KeyId(KeysMagickNumber.defaultSymmetric,blake2b3l(keyBytes), pbkdfParams)
 | 
			
		||||
        KeyId(KeysmagicNumber.defaultSymmetric,blake2b3l(keyBytes), pbkdfParams)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun decryptWithNonce(cipherData: UByteArray, nonce: UByteArray): UByteArray =
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,22 @@
 | 
			
		||||
package net.sergeych.crypto2
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
import kotlinx.serialization.Transient
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
sealed class UniversalKey: KeyInstance {
 | 
			
		||||
 | 
			
		||||
    abstract val keyBytes: UByteArray
 | 
			
		||||
    abstract val magick: KeysMagickNumber
 | 
			
		||||
 | 
			
		||||
    override val id by lazy { KeyId(magick, keyBytes) }
 | 
			
		||||
 | 
			
		||||
    @Transient
 | 
			
		||||
    open val magic: KeysmagicNumber = KeysmagicNumber.Unknown
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override val id by lazy { KeyId(magic, keyBytes) }
 | 
			
		||||
 | 
			
		||||
    // Important: id can be overridden, so we use it, not magic:
 | 
			
		||||
    open val label by lazy { id.keymagic.label }
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        return other is UniversalKey && other.keyBytes contentEquals keyBytes
 | 
			
		||||
@ -18,7 +26,10 @@ sealed class UniversalKey: KeyInstance {
 | 
			
		||||
        return keyBytes.contentHashCode()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = keyBytes.encodeToBase64Url()
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        val pwdSign = if (id.kdp != null) '*' else '#'
 | 
			
		||||
        return "🗝${label}$pwdSign$id"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun newSecretKey() = Asymmetric.newSecretKey()
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,36 @@
 | 
			
		||||
import kotlinx.coroutines.test.runTest
 | 
			
		||||
import net.sergeych.crypto2.PBKD
 | 
			
		||||
import net.sergeych.crypto2.initCrypto
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
 | 
			
		||||
class PBKDTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testDerive() {
 | 
			
		||||
    fun testDerive() = runTest {
 | 
			
		||||
        initCrypto()
 | 
			
		||||
        val (k1, k2, k3) = PBKD.deriveMultipleKeys("foobar", 3)
 | 
			
		||||
        PBKD.clearCache()
 | 
			
		||||
        println(k1)
 | 
			
		||||
        println(k2)
 | 
			
		||||
        println(k3)
 | 
			
		||||
//        println("------------------- Secret ket encryption!")
 | 
			
		||||
//        val s = UniversalKey.newSecretKey()
 | 
			
		||||
//        println(s)
 | 
			
		||||
//        println(s.publicKey)
 | 
			
		||||
//        println("------------------- public key signing")
 | 
			
		||||
//        val i = UniversalKey.newSigningKey()
 | 
			
		||||
//        println(i)
 | 
			
		||||
//        println(i.verifyingKey)
 | 
			
		||||
//        println("------------------- symmetric")
 | 
			
		||||
//        println(UniversalKey.newSymmetricKey())
 | 
			
		||||
        for( k in listOf(k1,k2,k3)) {
 | 
			
		||||
            val i = k.id
 | 
			
		||||
            val kx = i.kdp!!.deriveKey("foobar")
 | 
			
		||||
            assertEquals(k, kx)
 | 
			
		||||
            assertEquals(i, kx.id)
 | 
			
		||||
            assertEquals(i.kdp, kx.id.kdp)
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user