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")
|
@SerialName("encp")
|
||||||
class PublicKey(override val keyBytes: UByteArray) : UniversalKey(), EncryptingKey {
|
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.
|
* 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,
|
val _cachedPublicKey: PublicKey? = null,
|
||||||
) : DecryptingKey, UniversalKey() {
|
) : DecryptingKey, UniversalKey() {
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
override val label: String = "sec"
|
||||||
/**
|
/**
|
||||||
* Decrypt with authentication checks the message which must have [Message.senderPublicKey] set.
|
* 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
|
* Use [decryptWithSenderKey] otherwise. Note that the authenticated encryption is always use, even if
|
||||||
@ -238,7 +242,7 @@ object Asymmetric {
|
|||||||
return message.decrypt(this)
|
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 id: KeyId by lazy { publicKey.id }
|
||||||
|
|
||||||
override val nonceBytesLength: Int
|
override val nonceBytesLength: Int
|
||||||
|
@ -17,7 +17,7 @@ class BinaryId private constructor (
|
|||||||
class IncomparableException(text: String) : IllegalArgumentException(text)
|
class IncomparableException(text: String) : IllegalArgumentException(text)
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
val magick: Int = run {
|
val magic: Int = run {
|
||||||
if (id.size < 4) throw InvalidException("BinaryId is too short")
|
if (id.size < 4) throw InvalidException("BinaryId is too short")
|
||||||
val crc = id.last()
|
val crc = id.last()
|
||||||
val rest = id.dropLast(1).toUByteArray()
|
val rest = id.dropLast(1).toUByteArray()
|
||||||
@ -32,7 +32,7 @@ class BinaryId private constructor (
|
|||||||
override fun toString(): String = id.encodeToBase64Url()
|
override fun toString(): String = id.encodeToBase64Url()
|
||||||
|
|
||||||
override fun compareTo(other: BinaryId): Int {
|
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
|
val id1 = other.id
|
||||||
if (id1.size != id.size) throw IncomparableException("different sizes")
|
if (id1.size != id.size) throw IncomparableException("different sizes")
|
||||||
for ((a, b) in innerData.zip(other.innerData)) {
|
for ((a, b) in innerData.zip(other.innerData)) {
|
||||||
@ -66,11 +66,11 @@ class BinaryId private constructor (
|
|||||||
BinaryId(str.decodeBase64Url().toUByteArray())
|
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 crc = CRC8()
|
||||||
val mn = magick.toUByte()
|
val mn = magic.toUByte()
|
||||||
crc.update(bytes)
|
crc.update(bytes)
|
||||||
crc.update(mn)
|
crc.update(mn)
|
||||||
return BinaryId(UByteArray(bytes.size + 2).also {
|
return BinaryId(UByteArray(bytes.size + 2).also {
|
||||||
@ -82,13 +82,13 @@ class BinaryId private constructor (
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun createRandom(magickNumber: Int, size: Int=16) =
|
fun createRandom(magicNumber: Int, size: Int=16) =
|
||||||
createFromBytes(magickNumber, Random.Default.nextBytes(size-2))
|
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 =
|
fun createFromString(magicNumber: Int, text: String): BinaryId =
|
||||||
createFromUBytes(magickNumber, text.encodeToUByteArray())
|
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
|
* 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.
|
* 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
|
* 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:
|
* 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
|
* - [SigningSecretKey] and corresponding [VerifyingKey] have the same `KeyId`. Use it to pick a proper key for
|
||||||
* signing from a ring with [UniversalRing.findKey]
|
* 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
|
@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.
|
* and other key exchanges to generate session tokens, etc.
|
||||||
*/
|
*/
|
||||||
val binaryTag: UByteArray by lazy { id.id }
|
val binaryTag: UByteArray by lazy { id.id }
|
||||||
|
|
||||||
|
val keymagic: KeysmagicNumber by lazy { KeysmagicNumber.entries[id.magic]}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is KeyId) return false
|
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()
|
override fun toString() = id.toString()
|
||||||
|
|
||||||
constructor(magickNumber: KeysMagickNumber, keyData: UByteArray, kdp: PBKD.Params?=null)
|
constructor(magicNumber: KeysmagicNumber, keyData: UByteArray, kdp: PBKD.Params?=null)
|
||||||
: this(BinaryId.createFromUBytes(magickNumber.number, blake2b3l(keyData)), kdp)
|
: this(BinaryId.createFromUBytes(magicNumber.ordinal, blake2b3l(keyData)), kdp)
|
||||||
}
|
}
|
@ -1,18 +1,11 @@
|
|||||||
package net.sergeych.crypto2
|
package net.sergeych.crypto2
|
||||||
|
|
||||||
enum class KeysMagickNumber(val number: Int) {
|
enum class KeysmagicNumber(val label: String) {
|
||||||
defaultAssymmetric(0),
|
Unknown( "???"),
|
||||||
defaultSymmetric(1),
|
defaultAssymmetric( "asm"),
|
||||||
defaultSession(2),
|
defaultSymmetric( "sym"),
|
||||||
defaultSigning(3),
|
defaultSession( "ssn"),
|
||||||
defaultVerifying(4),
|
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 {
|
fun deriveKey(password: String, keyId: KeyId? = null): SymmetricKey {
|
||||||
// check key id sanity
|
// check key id sanity
|
||||||
keyId?.let {
|
keyId?.let {
|
||||||
if (it.id.magick != KeysMagickNumber.defaultSymmetric.number) {
|
if (it.id.magic != KeysmagicNumber.defaultSymmetric.ordinal) {
|
||||||
throw NotSupportedException("deriving key of type ${it.id.magick} is not implemented")
|
throw NotSupportedException("deriving key of type ${it.id.magic} is not implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this will not be ok when we support other key types:
|
// 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}")
|
throw NotSupportedException("can't create key from $length byte(s), required ${SymmetricKey.keyLength}")
|
||||||
|
|
||||||
// derive
|
// derive
|
||||||
val key = SymmetricKey(
|
val key = SymmetricKey(deriveUBytes(password),this)
|
||||||
deriveUBytes(password)
|
|
||||||
.sliceArray(startOffset..<startOffset + SymmetricKey.keyLength),
|
|
||||||
this
|
|
||||||
)
|
|
||||||
|
|
||||||
// check if we can
|
// check if we can
|
||||||
if (keyId != null && keyId != key.id)
|
if (keyId != null && keyId != key.id)
|
||||||
@ -121,6 +117,15 @@ object PBKD {
|
|||||||
entry.data!!
|
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
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "Pub:${super.toString()}"
|
|
||||||
|
|
||||||
@Transient
|
@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 =
|
override fun seal(message: UByteArray, expiresAt: Instant?): Seal =
|
||||||
Seal.create(this, message, now(), expiresAt)
|
Seal.create(this, message, now(), expiresAt)
|
||||||
|
|
||||||
override fun toString(): String = "Sct:${super.toString()}"
|
|
||||||
|
|
||||||
override val id: KeyId = verifyingKey.id
|
override val id: KeyId = verifyingKey.id
|
||||||
|
|
||||||
@Transient
|
override val label: String
|
||||||
override val magick = KeysMagickNumber.defaultSigning
|
get() = "sig"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ import kotlinx.serialization.Transient
|
|||||||
class SymmetricKey(
|
class SymmetricKey(
|
||||||
override val keyBytes: UByteArray,
|
override val keyBytes: UByteArray,
|
||||||
@Transient
|
@Transient
|
||||||
val pbkdfParams: PBKD.Params?=null
|
private val pbkdfParams: PBKD.Params?=null
|
||||||
) : EncryptingKey, DecryptingKey, UniversalKey() {
|
) : EncryptingKey, DecryptingKey, UniversalKey() {
|
||||||
|
|
||||||
override val magick: KeysMagickNumber = KeysMagickNumber.defaultSymmetric
|
override val magic: KeysmagicNumber = KeysmagicNumber.defaultSymmetric
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @suppress
|
* @suppress
|
||||||
@ -46,7 +46,7 @@ class SymmetricKey(
|
|||||||
override val nonceBytesLength: Int = nonceLength
|
override val nonceBytesLength: Int = nonceLength
|
||||||
|
|
||||||
override val id by lazy {
|
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 =
|
override fun decryptWithNonce(cipherData: UByteArray, nonce: UByteArray): UByteArray =
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
package net.sergeych.crypto2
|
package net.sergeych.crypto2
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
sealed class UniversalKey: KeyInstance {
|
sealed class UniversalKey: KeyInstance {
|
||||||
|
|
||||||
abstract val keyBytes: UByteArray
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
return other is UniversalKey && other.keyBytes contentEquals keyBytes
|
return other is UniversalKey && other.keyBytes contentEquals keyBytes
|
||||||
@ -18,7 +26,10 @@ sealed class UniversalKey: KeyInstance {
|
|||||||
return keyBytes.contentHashCode()
|
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 {
|
companion object {
|
||||||
fun newSecretKey() = Asymmetric.newSecretKey()
|
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.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class PBKDTest {
|
class PBKDTest {
|
||||||
|
|
||||||
@Test
|
@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