refining keys model
This commit is contained in:
parent
c4accfcd91
commit
bb383b5457
@ -80,7 +80,7 @@ sealed class Container {
|
|||||||
* @return decrypted data or null if this ring contains no proper key for it
|
* @return decrypted data or null if this ring contains no proper key for it
|
||||||
*/
|
*/
|
||||||
fun decryptWith(vararg keys: DecryptingKey): UByteArray? =
|
fun decryptWith(vararg keys: DecryptingKey): UByteArray? =
|
||||||
decryptWith(UniversalRing(*keys))
|
decryptWith(UniversalRing.from(*keys))
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
var decryptedData: UByteArray? = null
|
var decryptedData: UByteArray? = null
|
||||||
|
@ -3,13 +3,28 @@ package net.sergeych.crypto2
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag that identifies in some way the _decrypting key_. Is used in keyrings and
|
* Key Identity. Can be used to find matching keys for decryption or verifying, etc. The identity
|
||||||
* containers to fast find a proper key
|
* may contain [KDF] parameters if the corresponding key could be derived from a password.
|
||||||
|
* Note that [kdf] 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:
|
||||||
|
*
|
||||||
|
* - [Asymmetric.SecretKey] and [Asymmetric.PublicKey] from the same pair have the same `KeyId`, thus the former
|
||||||
|
* can decrypt what was encrypted with the latter.
|
||||||
|
*
|
||||||
|
* - [SigningSecretKey] and corresponding [VerifyingKey] have the same `KeyId`. Use it to pick a proper key for
|
||||||
|
* signing from a ring with [UniversalRing.findKey]
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class KeyId(val id: BinaryId, val kdp: KeyDerivationParams?=null ) {
|
data class KeyId(val id: BinaryId, val kdf: KeyDerivationParams?=null ) {
|
||||||
|
|
||||||
val tag: UByteArray by lazy { id.id }
|
/**
|
||||||
|
* Binary array representation of the [id], not including the [kdf]. Used in [SafeKeyExchange]
|
||||||
|
* and other key exchanges to generate session tokens, etc.
|
||||||
|
*/
|
||||||
|
val binaryTag: UByteArray by lazy { id.id }
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
@ -47,9 +47,9 @@ class SafeKeyExchange {
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val sessionTag: UByteArray by lazy {
|
val sessionTag: UByteArray by lazy {
|
||||||
if (!isClient)
|
if (!isClient)
|
||||||
blake2b(id.tag + id.tag)
|
blake2b(id.binaryTag + id.binaryTag)
|
||||||
else
|
else
|
||||||
blake2b(id.tag + id.tag)
|
blake2b(id.binaryTag + id.binaryTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val id: KeyId
|
override val id: KeyId
|
||||||
|
@ -4,53 +4,101 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class UniversalRing(
|
class UniversalRing(
|
||||||
val keyWithTags: Map<UniversalKey,Set<String>>
|
val keyWithTags: Map<UniversalKey, Set<String>>,
|
||||||
) {
|
) {
|
||||||
constructor(vararg keys: UniversalKey) : this(keys.associateWith { setOf() })
|
constructor(vararg keys: UniversalKey) : this(keys.associateWith { setOf() })
|
||||||
constructor(vararg keys: DecryptingKey) : this(keys.associate { UniversalKey.from(it) to setOf<String>() })
|
|
||||||
constructor(vararg keyTags: Pair<UniversalKey, String>)
|
constructor(vararg keyTags: Pair<UniversalKey, String>)
|
||||||
: this(keyTags.associate { it.first to setOf(it.second) })
|
: this(keyTags.associate { it.first to setOf(it.second) })
|
||||||
|
|
||||||
val decryptingKeys: Set<DecryptingKey> by lazy { keys<DecryptingKey>() }
|
val decryptingKeys: Set<DecryptingKey> by lazy { keys<DecryptingKey>() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select keys of the specified type
|
||||||
|
*/
|
||||||
|
inline fun <reified T> keys(): Set<T> =
|
||||||
|
allKeys.mapNotNull { it as? T }.toSet()
|
||||||
|
|
||||||
inline fun <reified T>keys(): Set<T> =
|
val allKeys: Set<UniversalKey> by lazy { keyWithTags.keys }
|
||||||
keyWithTags.keys.mapNotNull { it as? T }.toSet()
|
|
||||||
|
inline fun <reified T> findKey(id: KeyId): UniversalKey? =
|
||||||
|
allKeys.find { it is T && it.id == id }
|
||||||
|
|
||||||
inline fun <reified T>findKey(id: KeyId): UniversalKey? =
|
fun keysById(id: KeyId): List<UniversalKey> = allKeys.filter { it.id == id }
|
||||||
keyWithTags.keys.find { it is T && it.id == id }
|
|
||||||
|
|
||||||
fun allByAnyOfTags(vararg tags: String) = sequence {
|
/**
|
||||||
for( e in keyWithTags.entries) {
|
* Return sequence of keys that have at least one of the [tags]
|
||||||
if( tags.any { it in e.value }) yield(e.key)
|
*/
|
||||||
|
fun keysByTags(vararg tags: String) = sequence {
|
||||||
|
for (e in keyWithTags.entries) {
|
||||||
|
if (tags.any { it in e.value }) yield(e.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T>keyByTag(tag: String) = allByAnyOfTags(tag).first { it is T }
|
/**
|
||||||
|
* Get the first key of the specified type having the [tag]
|
||||||
|
*/
|
||||||
|
inline fun <reified T> keyByTag(tag: String) = keysByTags(tag).first { it is T }
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
inline fun <reified T>keyByAnyTag(vararg tags: String) = allByAnyOfTags(*tags).first { it is T }
|
inline fun <reified T> keyByAnyTag(vararg tags: String) = keysByTags(*tags).first { it is T }
|
||||||
|
|
||||||
operator fun get(keyId: KeyId): Collection<UniversalKey> = keyWithTags.keys.filter { it.id == keyId }
|
/**
|
||||||
|
* Get all keys with a given id. Note that _matching keys_ have the same id, see [KeyId] for more.
|
||||||
|
*/
|
||||||
|
operator fun get(keyId: KeyId): Collection<UniversalKey> = allKeys.filter { it.id == keyId }
|
||||||
|
|
||||||
fun getTags(key: UniversalKey): Set<String>? = keyWithTags[key]
|
fun getTags(key: UniversalKey): Set<String>? = keyWithTags[key]
|
||||||
|
|
||||||
operator fun contains(element: UniversalKey): Boolean = element in keyWithTags
|
operator fun contains(element: UniversalKey): Boolean = element in keyWithTags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key if not already in a ring, otherwise return existing ring.
|
||||||
|
*/
|
||||||
operator fun plus(key: UniversalKey): UniversalRing =
|
operator fun plus(key: UniversalKey): UniversalRing =
|
||||||
if( key in this ) this else UniversalRing(keyWithTags + (key to setOf()) )
|
if (key in this) this else UniversalRing(keyWithTags + (key to setOf()))
|
||||||
|
|
||||||
operator fun plus(keyTag: Pair<UniversalKey,String>): UniversalRing =
|
/**
|
||||||
if( keyTag.first in this )
|
* Add a key and tags to the ring. If the key exists, add the tag to it.
|
||||||
addTags(keyTag.first,keyTag.second)
|
* See also [addTags].
|
||||||
else UniversalRing(keyWithTags + keyWithTags)
|
*/
|
||||||
|
operator fun plus(keyTag: Pair<UniversalKey, String>): UniversalRing =
|
||||||
|
if (keyTag.first in this)
|
||||||
|
addTags(keyTag.first, keyTag.second)
|
||||||
|
else add(keyTag.first, keyTag.second)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge two rings. The result will contain all keys and all tags from
|
||||||
|
* both rings.
|
||||||
|
*/
|
||||||
|
operator fun plus(other: UniversalRing): UniversalRing {
|
||||||
|
var result = keyWithTags.toMutableMap()
|
||||||
|
for (e in other.keyWithTags.entries) {
|
||||||
|
result[e.key]?.let {
|
||||||
|
result[e.key] = it + e.value
|
||||||
|
} ?: run {
|
||||||
|
result[e.key] = e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UniversalRing(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add key and tags to the ring. If the key already exists, tags are merged.
|
||||||
|
*/
|
||||||
fun add(key: UniversalKey, vararg tags: String): UniversalRing =
|
fun add(key: UniversalKey, vararg tags: String): UniversalRing =
|
||||||
UniversalRing(keyWithTags + (key to tags.toSet()) )
|
UniversalRing(keyWithTags + (key to tags.toSet()))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add key and tags to the ring. If the key already exists, tags are merged.
|
||||||
|
*/
|
||||||
fun add(key: UniversalKey, tags: Collection<String>): UniversalRing =
|
fun add(key: UniversalKey, tags: Collection<String>): UniversalRing =
|
||||||
UniversalRing(keyWithTags + (key to tags.toSet()) )
|
UniversalRing(keyWithTags + (key to tags.toSet()))
|
||||||
|
|
||||||
fun addTags(key: UniversalKey,tags: Set<String>): UniversalRing {
|
/**
|
||||||
|
* Add tags to the key, and add key if it is not in the ring.
|
||||||
|
*/
|
||||||
|
fun addTags(key: UniversalKey, tags: Set<String>): UniversalRing {
|
||||||
val kt1 = keyWithTags.toMutableMap()
|
val kt1 = keyWithTags.toMutableMap()
|
||||||
kt1[key]?.let {
|
kt1[key]?.let {
|
||||||
kt1[key] = it + tags
|
kt1[key] = it + tags
|
||||||
@ -60,11 +108,17 @@ class UniversalRing(
|
|||||||
return UniversalRing(kt1)
|
return UniversalRing(kt1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addTags(key: UniversalKey,vararg tags: String): UniversalRing =
|
/**
|
||||||
addTags(key,tags.toSet())
|
* Add tags to the key, and add key if it is not in the ring.
|
||||||
|
*/
|
||||||
|
fun addTags(key: UniversalKey, vararg tags: String): UniversalRing =
|
||||||
|
addTags(key, tags.toSet())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the ring without [key] if existed, otherwise returns `this`.
|
||||||
|
*/
|
||||||
operator fun minus(key: UniversalKey): UniversalRing =
|
operator fun minus(key: UniversalKey): UniversalRing =
|
||||||
if( key in this ) UniversalRing(keyWithTags.filter { it.key != key }) else this
|
if (key in this) UniversalRing(keyWithTags.filter { it.key != key }) else this
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@ -72,27 +126,64 @@ class UniversalRing(
|
|||||||
return keyWithTags == other.keyWithTags
|
return keyWithTags == other.keyWithTags
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val ss = keyWithTags.entries
|
|
||||||
.joinToString(","){"${it.value.joinToString{ ":" }}:${it.key}"}
|
|
||||||
return "Kr[$ss]"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return keyWithTags.hashCode()
|
return keyWithTags.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun equalKeys(other: UniversalRing): Boolean = keyWithTags.keys == other.keyWithTags.keys
|
override fun toString(): String {
|
||||||
|
val ss = keyWithTags.entries
|
||||||
fun removeTags(key: UniversalKey, vararg tags: String): UniversalRing = removeTags(key, tags.toSet())
|
.joinToString(",") { "${it.value.joinToString { ":" }}:${it.key}" }
|
||||||
|
return "Kr[$ss]"
|
||||||
fun removeTags(key: UniversalKey, tags: Set<String>): UniversalRing {
|
|
||||||
val kt1 = keyWithTags.toMutableMap()
|
|
||||||
kt1[key]?.let {
|
|
||||||
kt1[key] = it - tags
|
|
||||||
}
|
|
||||||
return UniversalRing(kt1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infix fun equalKeys(other: UniversalRing): Boolean = allKeys == other.allKeys
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a collection where no [tags] are specified for the [key]. If the key is not
|
||||||
|
* in the collection, it returns this.
|
||||||
|
*/
|
||||||
|
fun removeTags(key: UniversalKey, vararg tags: String): UniversalRing = removeTags(key, tags.toSet())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a collection where no [tags] are specified for the [key]. If the key is not
|
||||||
|
* in the collection, it returns this.
|
||||||
|
*/
|
||||||
|
fun removeTags(key: UniversalKey, tags: Set<String>): UniversalRing =
|
||||||
|
keyWithTags[key]?.let { existingTags ->
|
||||||
|
val kt1 = keyWithTags.toMutableMap()
|
||||||
|
kt1[key] = existingTags - tags
|
||||||
|
UniversalRing(kt1)
|
||||||
|
} ?: this
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create string "report" of the ring contents. Note it has no trailing `\n`
|
||||||
|
*/
|
||||||
|
fun ls(): String {
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
for( e in keyWithTags.entries) {
|
||||||
|
result += "${e.key} ${e.value.joinToString(" ")}"
|
||||||
|
}
|
||||||
|
return result.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val EMPTY = UniversalRing(keyWithTags = emptyMap())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join a collection of keyrings together (same as reducing with `+`). Correctly
|
||||||
|
* works if there is no keyring (returns [EMPTY]), or only one keyring (returns
|
||||||
|
* it with no extra work).
|
||||||
|
*
|
||||||
|
* Note that for the known number of rings using [plus] is more clear and expressive
|
||||||
|
*/
|
||||||
|
fun join(keyRings: Collection<UniversalRing>): UniversalRing {
|
||||||
|
if (keyRings.isEmpty()) return EMPTY
|
||||||
|
if (keyRings.size == 1) return keyRings.first()
|
||||||
|
return keyRings.reduce { l, r -> l + r }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun from(vararg keys: DecryptingKey): UniversalRing =
|
||||||
|
UniversalRing(keys.associate { UniversalKey.from(it) to setOf() } )
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,40 @@ class RingTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSize() = runTest {
|
fun testSize() = runTest {
|
||||||
// val sy1 = SymmetricKey.random().toUniversal()
|
initCrypto()
|
||||||
|
val r = UniversalRing(UniversalKey.newSymmetricKey())
|
||||||
|
val rd = BipackEncoder.encode(r)
|
||||||
|
println(rd.size)
|
||||||
|
println(rd.toDump())
|
||||||
|
assertTrue { rd.size <= 46 }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testJoin() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val a = UniversalKey.newSecretKey()
|
||||||
|
val b = UniversalKey.newSigningKey()
|
||||||
|
val c = UniversalKey.newSymmetricKey()
|
||||||
|
val d = UniversalKey.newSigningKey()
|
||||||
|
|
||||||
|
val ra = UniversalRing(a)
|
||||||
|
val rb = UniversalRing(b)
|
||||||
|
val rc = UniversalRing(c)
|
||||||
|
val rd = UniversalRing(d) + (a to "foo_a")
|
||||||
|
|
||||||
|
var r1 = ra + rb + rc + rd
|
||||||
|
|
||||||
|
assertEquals(a, r1.findKey<UniversalKey.Secret>(a.id))
|
||||||
|
assertEquals(a, r1.keyByTag<UniversalKey>("foo_a"))
|
||||||
|
assertEquals(b, r1.findKey<SigningKey>(b.id))
|
||||||
|
assertEquals(c, r1.keysById(c.key.id).first())
|
||||||
|
|
||||||
|
r1 = UniversalRing.join(listOf(ra, rb, rc, rd))
|
||||||
|
|
||||||
|
assertEquals(a, r1.findKey<UniversalKey.Secret>(a.id))
|
||||||
|
assertEquals(a, r1.keyByTag<UniversalKey>("foo_a"))
|
||||||
|
assertEquals(b, r1.findKey<SigningKey>(b.id))
|
||||||
|
assertEquals(c, r1.keysById(c.key.id).first())
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user