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