forked from sergeych/crypto2
fixed error in hashcode implementations of various objects so ring works as expected
This commit is contained in:
parent
f08653715b
commit
a1f984c4e6
@ -55,7 +55,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("test"))
|
implementation(kotlin("test"))
|
||||||
implementation("org.slf4j:slf4j-simple:2.0.9")
|
implementation("org.slf4j:slf4j-simple:2.0.9")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val native by creating {
|
val native by creating {
|
||||||
|
@ -135,7 +135,7 @@ object Asymmetric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return keyBytes.hashCode()
|
return keyBytes.contentHashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,7 +234,7 @@ object Asymmetric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return keyBytes.hashCode()
|
return keyBytes.contentHashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
19
src/commonMain/kotlin/net/sergeych/crypto2/PBKDParams.kt
Normal file
19
src/commonMain/kotlin/net/sergeych/crypto2/PBKDParams.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package net.sergeych.crypto2
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.sergeych.bipack.Unsigned
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class PBKDParams(
|
||||||
|
val kdf: KDF,
|
||||||
|
@Unsigned
|
||||||
|
val offset: UInt,
|
||||||
|
@Unsigned
|
||||||
|
val length: UInt
|
||||||
|
) {
|
||||||
|
// val key by lazy {
|
||||||
|
// SymmetricKey(derivedBytes)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// val derivedBytes by lazy {kdf.derivedBytes}
|
||||||
|
}
|
@ -52,7 +52,7 @@ class SymmetricKey(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return keyBytes.hashCode()
|
return keyBytes.contentHashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,10 +28,13 @@ class UniversalRing(
|
|||||||
if( keyTag in byTag ) UniversalRing(keySet.filter { it.tag != keyTag }) else this
|
if( keyTag in byTag ) UniversalRing(keySet.filter { it.tag != keyTag }) else this
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
println("compare1\r\n")
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
println("compare2\r\n")
|
||||||
if (other !is UniversalRing) return false
|
if (other !is UniversalRing) return false
|
||||||
|
|
||||||
return size == other.size && keySet == other.keySet
|
println("compare2 ${size == other.size}: $size : ${other.size} | ${keySet == other.keySet}\r\n")
|
||||||
|
return size == other.size && keySet.containsAll(other.keySet)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
157
src/commonMain/kotlin/net/sergeych/crypto2/kdf.kt
Normal file
157
src/commonMain/kotlin/net/sergeych/crypto2/kdf.kt
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package net.sergeych.crypto2
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.pwhash.*
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.sergeych.bipack.Unsigned
|
||||||
|
import net.sergeych.synctools.ProtectedOp
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed class KDF {
|
||||||
|
|
||||||
|
enum class Complexity {
|
||||||
|
Interactive,
|
||||||
|
Moderate,
|
||||||
|
Sensitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
fun derivedCached(password: String): UByteArray =
|
||||||
|
deriveCached(this, password)
|
||||||
|
|
||||||
|
|
||||||
|
abstract protected fun derive(password: String): UByteArray
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val op = ProtectedOp()
|
||||||
|
private val cache = HashMap<KDF, Entry>()
|
||||||
|
|
||||||
|
private class Entry(val kdf: KDF) {
|
||||||
|
val op = ProtectedOp()
|
||||||
|
var data: UByteArray? = null
|
||||||
|
|
||||||
|
// fun generateCached(): UByteArray = op {
|
||||||
|
// if (data == null)
|
||||||
|
// data = kdf.derive()
|
||||||
|
// data!!
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fun key(kdf: KDF,password: String): KDF {
|
||||||
|
// return BipackEncoder.encode()
|
||||||
|
// }
|
||||||
|
fun deriveCached(kdf: KDF,password: String): UByteArray {
|
||||||
|
TODO()
|
||||||
|
// val entry = op {
|
||||||
|
// cache.getOrPut(kdf) { Entry(kdf) }
|
||||||
|
// }
|
||||||
|
// return entry.generateCached()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("argon")
|
||||||
|
data class Argon(
|
||||||
|
val algorithm: Alg,
|
||||||
|
@Unsigned
|
||||||
|
val instructionsComplexity: ULong,
|
||||||
|
@Unsigned
|
||||||
|
val memComplexity: Int,
|
||||||
|
val salt: UByteArray,
|
||||||
|
@Unsigned
|
||||||
|
val keySize: Int,
|
||||||
|
) : KDF(), Comparable<Argon> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very abstract strength comparison. If a1 > a2 it generally means that its complexity, and, hopefully,
|
||||||
|
* strength is higher.
|
||||||
|
*/
|
||||||
|
override fun compareTo(other: Argon): Int {
|
||||||
|
var d = algorithm.ordinal.compareTo(other.algorithm.ordinal)
|
||||||
|
if (d != 0) return d
|
||||||
|
d = instructionsComplexity.compareTo(other.instructionsComplexity)
|
||||||
|
if (d != 0) return d
|
||||||
|
d = memComplexity.compareTo(other.memComplexity)
|
||||||
|
if (d != 0) return d
|
||||||
|
d = keySize
|
||||||
|
if (d != 0) return d
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Alg(val code: Int) {
|
||||||
|
V2i_13(crypto_pwhash_argon2i_ALG_ARGON2I13),
|
||||||
|
V2id_13(crypto_pwhash_argon2id_ALG_ARGON2ID13),
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val default = V2id_13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun derive(password: String): UByteArray {
|
||||||
|
TODO()
|
||||||
|
//PasswordHash.pwhash(keySize, )
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Argon) return false
|
||||||
|
|
||||||
|
if (algorithm != other.algorithm) return false
|
||||||
|
if (instructionsComplexity != other.instructionsComplexity) return false
|
||||||
|
if (memComplexity != other.memComplexity) return false
|
||||||
|
if (keySize != other.keySize) return false
|
||||||
|
return salt contentEquals other.salt
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = algorithm.hashCode()
|
||||||
|
result = 31 * result + instructionsComplexity.hashCode()
|
||||||
|
result = 31 * result + memComplexity
|
||||||
|
result = 31 * result + salt.contentHashCode()
|
||||||
|
result = 31 * result + keySize
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val saltSize: Int = crypto_pwhash_SALTBYTES
|
||||||
|
val minKeySize: Int = crypto_pwhash_BYTES_MIN.toInt()
|
||||||
|
|
||||||
|
fun randomSalt() = randomUBytes(saltSize)
|
||||||
|
|
||||||
|
fun create(complexity: Complexity, salt: UByteArray, keySize: Int): Argon {
|
||||||
|
require(salt.size == saltSize) { "The salt size should be $saltSize" }
|
||||||
|
require(keySize > minKeySize) { "The key size should be at least $keySize bytes" }
|
||||||
|
return when (complexity) {
|
||||||
|
Complexity.Interactive -> Argon(
|
||||||
|
Alg.default,
|
||||||
|
crypto_pwhash_OPSLIMIT_INTERACTIVE.toULong(),
|
||||||
|
crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
||||||
|
salt, keySize
|
||||||
|
)
|
||||||
|
|
||||||
|
Complexity.Moderate -> Argon(
|
||||||
|
Alg.default,
|
||||||
|
crypto_pwhash_OPSLIMIT_MODERATE,
|
||||||
|
crypto_pwhash_MEMLIMIT_MODERATE,
|
||||||
|
salt, keySize
|
||||||
|
)
|
||||||
|
|
||||||
|
Complexity.Sensitive -> Argon(
|
||||||
|
Alg.default,
|
||||||
|
crypto_pwhash_OPSLIMIT_SENSITIVE,
|
||||||
|
crypto_pwhash_MEMLIMIT_SENSITIVE,
|
||||||
|
salt, keySize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Instance(val kdf: KDF,val password: String)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
41
src/commonTest/kotlin/KDFTest.kt
Normal file
41
src/commonTest/kotlin/KDFTest.kt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import net.sergeych.crypto2.KDF
|
||||||
|
import net.sergeych.crypto2.initCrypto
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
|
||||||
|
class KDFTest {
|
||||||
|
@Test
|
||||||
|
fun testCacheKey() = runTest{
|
||||||
|
initCrypto()
|
||||||
|
val k1 = KDF.Argon.create(KDF.Complexity.Interactive, KDF.Argon.randomSalt(), 128)
|
||||||
|
val k2 = KDF.Argon.create(KDF.Complexity.Interactive, k1.salt, 128)
|
||||||
|
|
||||||
|
assertEquals(k1, k2)
|
||||||
|
assertEquals(k1.hashCode(), k2.hashCode())
|
||||||
|
|
||||||
|
val key1 = KDF.Instance(k1, "foo")
|
||||||
|
val key2 = KDF.Instance(k2, "foo")
|
||||||
|
val key3 = KDF.Instance(k2, "bar")
|
||||||
|
|
||||||
|
assertEquals(key1, key2)
|
||||||
|
assertEquals(key1.hashCode(), key2.hashCode())
|
||||||
|
assertFalse { key3 == key2 }
|
||||||
|
assertFalse { key3.hashCode() == key2.hashCode() }
|
||||||
|
|
||||||
|
val m = mutableMapOf<KDF.Instance, String>()
|
||||||
|
m[key1] = "foo"
|
||||||
|
m[key2] = "bar"
|
||||||
|
m[key3] = "foobar"
|
||||||
|
|
||||||
|
assertEquals("bar", m[key1])
|
||||||
|
assertEquals("bar", m[key2])
|
||||||
|
assertEquals("foobar", m[key3])
|
||||||
|
|
||||||
|
val set1 = setOf(key1, key2, key3)
|
||||||
|
val set2 = setOf(key3, key2, key1)
|
||||||
|
assertEquals(set2, set1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -172,4 +172,76 @@ class KeysTest {
|
|||||||
// println(j.encodeToString(sk1))
|
// println(j.encodeToString(sk1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUniKeys() = runTest {
|
||||||
|
initCrypto()
|
||||||
|
val sy1 = SymmetricKey.random()
|
||||||
|
val sy2 = SymmetricKey(sy1.keyBytes)
|
||||||
|
val sy3 = SymmetricKey.random()
|
||||||
|
|
||||||
|
assertEquals(sy1, sy2)
|
||||||
|
assertEquals(sy2, sy1)
|
||||||
|
assertFalse { sy1 == sy3 }
|
||||||
|
|
||||||
|
assertEquals(sy1, deepCopy(sy1), "symmetric key should be equal to the restored copy")
|
||||||
|
assertEquals(sy1.hashCode(), deepCopy(sy1).hashCode(), "hashcode of the restored symmetric key is wrong")
|
||||||
|
|
||||||
|
val usy1 = UniversalKey.from(sy1)
|
||||||
|
val usy2 = UniversalKey.from(sy2)
|
||||||
|
val usy3 = UniversalKey.from(sy3)
|
||||||
|
|
||||||
|
assertEquals(usy1, usy2)
|
||||||
|
assertEquals(usy2, usy1)
|
||||||
|
assertFalse { usy1 == usy3 }
|
||||||
|
|
||||||
|
val sk1 = Asymmetric.randomSecretKey()
|
||||||
|
val sk2 = Asymmetric.SecretKey(sk1.keyBytes)
|
||||||
|
val sk3 = Asymmetric.randomSecretKey()
|
||||||
|
|
||||||
|
assertEquals(sk1, sk2)
|
||||||
|
assertEquals(sk2, sk1)
|
||||||
|
assertFalse { sk1 == sk3 }
|
||||||
|
|
||||||
|
var usk1 = UniversalKey.from(sk1)
|
||||||
|
var usk2 = UniversalKey.from(sk2)
|
||||||
|
var usk3 = UniversalKey.from(sk3)
|
||||||
|
val usk4 = UniversalKey.from(sy3)
|
||||||
|
|
||||||
|
assertEquals(usk1, usk2)
|
||||||
|
assertEquals(usk2, usk1)
|
||||||
|
assertFalse { usk1 == usk3 }
|
||||||
|
|
||||||
|
var a = setOf(sy1, sy2, sk1, sk2)
|
||||||
|
var b = setOf(sk1, sk2, sy2, sy1)
|
||||||
|
|
||||||
|
assertEquals(a,b)
|
||||||
|
|
||||||
|
a = setOf(usk1, usk2, usk3, usk4)
|
||||||
|
b = setOf(usk1, usk2, usk3, usk4)
|
||||||
|
|
||||||
|
assertEquals(a, b)
|
||||||
|
// usk1 and usk2 are equal so set with only one of should be the same
|
||||||
|
assertEquals(a, setOf(usk1, usk3, usk4))
|
||||||
|
|
||||||
|
usk1 = deepCopy(usk1)
|
||||||
|
usk2 = deepCopy(usk2)
|
||||||
|
usk3 = deepCopy(usk3)
|
||||||
|
|
||||||
|
assertEquals(usk1.hashCode(),usk2.hashCode())
|
||||||
|
|
||||||
|
assertEquals(usk1, usk2)
|
||||||
|
assertEquals(usk2, usk1)
|
||||||
|
assertFalse { usk1 == usk3 }
|
||||||
|
|
||||||
|
a = setOf(usk1, usk2, usk3, usk4)
|
||||||
|
b = setOf(usk4, usk3, usk2, usk1)
|
||||||
|
|
||||||
|
assertEquals(3, a.size)
|
||||||
|
assertEquals(3, b.size)
|
||||||
|
|
||||||
|
assertEquals(a, b)
|
||||||
|
// usk1 and usk2 are equal so set with only one of should be the same
|
||||||
|
assertEquals(a, setOf(usk1, usk3, usk4))
|
||||||
|
}
|
||||||
}
|
}
|
@ -46,9 +46,13 @@ class RingTest {
|
|||||||
assertContains(r2, k1)
|
assertContains(r2, k1)
|
||||||
assertFalse { k3 in r2 }
|
assertFalse { k3 in r2 }
|
||||||
|
|
||||||
|
println("\r\n")
|
||||||
println(r)
|
println(r)
|
||||||
println(r2)
|
println(r2)
|
||||||
|
|
||||||
|
// Kr[U.Sym:XYjneNaPFbg-PZJIYjgIz7F-DsH1dEY8Mg6LCirko2QBFA,U.Sec:FzuzDbrS0xR5nTkdd-mYvrqsfQn9HbQgtFnIw9CEirIAlw]
|
||||||
|
// Kr[U.Sym:XYjneNaPFbg-PZJIYjgIz7F-DsH1dEY8Mg6LCirko2QBFA,U.Sec:FzuzDbrS0xR5nTkdd-mYvrqsfQn9HbQgtFnIw9CEirIAlw]
|
||||||
|
|
||||||
assertEquals(r, r2)
|
assertEquals(r, r2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
src/commonTest/kotlin/test_tools.kt
Normal file
5
src/commonTest/kotlin/test_tools.kt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import net.sergeych.bipack.BipackDecoder
|
||||||
|
import net.sergeych.bipack.BipackEncoder
|
||||||
|
|
||||||
|
inline fun <reified T>deepCopy(x: T):T =
|
||||||
|
BipackDecoder.decode(BipackEncoder.encode(x))
|
Loading…
x
Reference in New Issue
Block a user