Universal keys. fixed bugs with keys serialization and equality

This commit is contained in:
Sergey Chernov 2024-06-18 22:45:48 +07:00
parent 9027bb0e88
commit f64412fdc7
18 changed files with 593 additions and 26 deletions

View File

@ -5,7 +5,7 @@ plugins {
}
group = "net.sergeych"
version = "0.2.2-SNAPSHOT"
version = "0.3.1-SNAPSHOT"
repositories {
mavenCentral()

View File

@ -6,6 +6,7 @@ import com.ionspin.kotlin.crypto.box.crypto_box_NONCEBYTES
import com.ionspin.kotlin.crypto.scalarmult.ScalarMultiplication
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.sergeych.bipack.BipackDecoder
import net.sergeych.crypto2.Asymmetric.Message
import net.sergeych.crypto2.Asymmetric.PublicKey
import net.sergeych.crypto2.Asymmetric.SecretKey
@ -94,7 +95,7 @@ object Asymmetric {
/**
* The generated key pair. See [generateKeys]
*/
data class KeyPair(val secretKey: SecretKey,val senderKey: PublicKey)
data class KeyPair(val secretKey: SecretKey, val senderKey: PublicKey)
/**
* Generate a new random pair of public and secret keys.
@ -107,6 +108,11 @@ object Asymmetric {
private fun randomNonce(): UByteArray = randomUBytes(crypto_box_NONCEBYTES)
fun randomSecretKey(): SecretKey = generateKeys().secretKey
@Suppress("unused")
val nonceBytesLength = crypto_box_NONCEBYTES
/**
* The public key used as the recipient for [Message] (see [SecretKey.encrypt], etc.). It also
* could be used to encrypt anonymous messages with [encryptAnonymously]
@ -143,7 +149,7 @@ object Asymmetric {
val keyBytes: UByteArray,
@Transient
val _cachedPublicKey: PublicKey? = null,
) {
) : DecryptingKey {
/**
* Encrypt the message for [recipient]. The [Message] will include the our [publicKey]
@ -151,7 +157,7 @@ object Asymmetric {
*
* The message is authenticated by this secret key.
*/
fun encrypt(plainData: UByteArray,recipient: PublicKey): Message =
fun encrypt(plainData: UByteArray, recipient: PublicKey): Message =
createMessage(this, recipient, plainData)
/**
@ -169,9 +175,10 @@ object Asymmetric {
* Decrypt using [senderPublicKey] as a sender key (overriding the [Message.senderPublicKey] if set).
* See [decrypt] for more.
*/
fun decryptWithSenderKey(message: Message,senderPublicKey: PublicKey): UByteArray =
message.decryptWithSenderKey(senderPublicKey,this)
fun decryptWithSenderKey(message: Message, senderPublicKey: PublicKey): UByteArray =
message.decryptWithSenderKey(senderPublicKey, this)
@Transient
private var cachedPublicKey: PublicKey? = _cachedPublicKey
/**
@ -192,6 +199,26 @@ object Asymmetric {
override fun hashCode(): Int {
return keyBytes.hashCode()
}
/**
* Nonce-based decryption is impossible, it is already included in message
*/
override fun decryptWithNonce(cipherData: UByteArray, nonce: UByteArray): UByteArray = decrypt(cipherData)
/**
* Decrypt without a nonce as edwards curve decryption does not need it
*/
override fun decrypt(cipherData: UByteArray): UByteArray {
val message: Message = BipackDecoder.decode(cipherData.toByteArray())
return message.decrypt(this)
}
override val decryptingTag: KeyTag by lazy {
KeyTag(KeysMagickNumbers.defaultAssymmetric, blake2b(publicKey.keyBytes))
}
override val nonceBytesLength: Int
get() = 0
}
}

View File

@ -0,0 +1,85 @@
package net.sergeych.crypto2
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.sergeych.bintools.CRC
import net.sergeych.bintools.CRC8
import net.sergeych.mp_tools.decodeBase64Url
import kotlin.random.Random
@Serializable
class BinaryId(
val id: UByteArray,
) : Comparable<BinaryId> {
class InvalidException(text: String) : IllegalArgumentException(text)
class IncomparableException(text: String) : IllegalArgumentException(text)
@Transient
val magick: Int = run {
if (id.size < 4) throw InvalidException("BinaryId is too short")
val crc = id.last()
val rest = id.dropLast(1).toUByteArray()
if (CRC.crc8(rest) != crc)
throw InvalidException("Bad BinaryId CRC")
rest.last().toInt()
}
private val innerData: UByteArray by lazy { id.sliceArray( 1..< id.size-1 ) }
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})")
val id1 = other.id
if (id1.size != id.size) throw IncomparableException("different sizes")
for ((a, b) in innerData.zip(other.innerData)) {
if (a < b) return -1
if (a > b) return 1
}
return 0
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is BinaryId) return false
if (!id.contentEquals(other.id)) return false
return true
}
override fun hashCode(): Int {
return id.contentHashCode()
}
companion object {
@Suppress("unused")
fun fromString(str: String): BinaryId =
BinaryId(str.decodeBase64Url().toUByteArray())
fun createFromBytes(magick: Int, bytes: ByteArray): BinaryId = createFromUBytes(magick, bytes.toUByteArray())
fun createFromUBytes(magick: Int, bytes: UByteArray): BinaryId {
val crc = CRC8()
val mn = magick.toUByte()
crc.update(bytes)
crc.update(mn)
return BinaryId(UByteArray(bytes.size + 2).also {
bytes.copyInto(it, 0)
val n = bytes.size
it[n] = mn
it[n + 1] = crc.value
})
}
@Suppress("unused")
fun createRandom(magickNumber: Int, size: Int=16) =
createFromBytes(magickNumber, Random.Default.nextBytes(size-2))
}
}

View File

@ -0,0 +1,214 @@
//package net.sergeych.crypto2
//
//import kotlinx.serialization.Serializable
//
//@Serializable
//sealed class Container {
//
//
// /**
// * General error while unpacking and decrypting the container
// */
// open class Error(reason: String = "failed to process the container") : IllegalArgumentException(reason)
//
// /**
// * Decryption failed
// */
// class DecryptionError : Error("failed to decrypt the container")
//
// /**
// * Packed container seems to be corrupted
// */
// class StructureError : Error("illegal container structure")
//
// /**
// * Key Ids for the key that can decrypt the container
// */
// abstract val keyIds: Set<KeyIdentity>
// abstract fun decrypt(key: DecryptingKey): ByteArray
//
// abstract fun update(keyRing: IdentifiableKeyring, newData: ByteArray): ByteArray?
//
// fun decrypt(keyRing: IdentifiableKeyring): Pair<IdentifiableKey, ByteArray>? {
// // Fast search: look for a key that shurely should
// for (k in keyRing.byIdAndSymmetrics(keyIds)) {
// try {
// if( k is DecryptingKey )
// return k to decrypt(k)
// } catch (x: Throwable) {
// // This means our key is not ok. It a rare but possible collision
// }
// }
//// // some symmetric key can sometimes decrypt even if their ID is wrong, so
//// // we check them all:
//// for (k in keyRing.keys) {
//// if (k is SymmetricKey) {
//// try {
//// return k to decrypt(k)
//// } catch (x: Throwable) {
//// // it's ok. we just tried
//// }
//// }
//// }
// return null
// }
//
// @Serializable
// @SerialName("single")
// data class Single(val keyId: KeyIdentity, val ciphertext: ByteArray) : Container() {
// override val keyIds by lazy { setOf(keyId) }
// override fun decrypt(key: DecryptingKey): ByteArray = try {
// key.etaDecrypt(ciphertext)
// } catch (x: Exception) {
// throw DecryptionError()
// }
//
// override fun update(keyRing: IdentifiableKeyring, newData: ByteArray): ByteArray? {
// return decrypt(keyRing)?.let { (k, _) ->
// if (k !is EncryptingKey) throw IllegalArgumentException("key is not suitable for re-encryption")
// encryptData(newData, k)
// }
// }
// }
//
// @Serializable
// data class EncryptedKey(val id: KeyIdentity, val encryptedKey: ByteArray)
//
// @Serializable
// @SerialName("multi")
// data class Multiple(val keys: List<EncryptedKey>, val ciphertext: ByteArray) : Container() {
// override val keyIds: Set<KeyIdentity> by lazy { keys.map { it.id }.toSet() }
//
// override fun decrypt(key: DecryptingKey): ByteArray {
// for (k in keys) {
// // Simple: we just use ID
// // Complex: wrong ID with symmetric key that can decrypt:
// if (key is SymmetricKey || k.id == key.id) {
// try {
// val dataKey = SymmetricKeys.create(key.etaDecrypt(k.encryptedKey), k.id)
// return dataKey.etaDecrypt(ciphertext)
// } catch (x: Throwable) {
// // might be wrong symmetric key id
// if( key !is SymmetricKey) throw DecryptionError()
// }
// }
// }
// throw IllegalArgumentException("the key does not open this container")
// }
//
// override fun update(keyRing: IdentifiableKeyring, newData: ByteArray): ByteArray? {
// for (k in keys) {
// for (key in keyRing[k.id]) {
// if (key is DecryptingKey) {
// val dataKey = SymmetricKeys.create(key.etaDecrypt(k.encryptedKey), k.id)
// return BossEncoder.encode(Multiple(keys, dataKey.etaEncrypt(newData)) as Container)
// }
// }
// }
// return null
// }
// }
//
//
//// return decrypt(keyRing)?.let { (key,_) ->
//// if(key !is EncryptingKey) throw IllegalArgumentException("key is not suitable for re-encryption")
//// if(key !is DecryptingKey) throw IllegalArgumentException("key is not suitable for re-encryption (not decrypting!)")
//// val dataKey = SymmetricKeys.create( key.etaDecrypt(k.encryptedKey), k.id)
//// encryptData(newData,k)
//// }
//// }
//// }
//
// companion object : Loggable by LogTag("CRCON") {
// /**
// * Create single-key container. Most often you should call [encrypt] instead.
// */
// fun single(payload: ByteArray, key: EncryptingKey): ByteArray {
// return BossEncoder.encode(Single(key.id, key.etaEncrypt(payload)) as Container)
// }
//
// /**
// * Create nultiple keys container event with one key. Most often you should not call it but
// * use [encrypt] instead.
// */
// fun multi(payload: ByteArray, keys: List<EncryptingKey>): ByteArray {
// val dataKey = SymmetricKeys.random()
// return BossEncoder.encode(
// Multiple(
// keys.map { EncryptedKey(it.id, it.etaEncrypt(dataKey.packed)) },
// dataKey.etaEncrypt(payload)
// ) as Container
// )
// }
//
// /**
// * Preferred method to create encrypted container. Selects the type automatically. Decrypt it with
// * any of the provided key (their [DecryptingKey] counterparts) with [decrypt]
// *
// * @param payload data to encrypt. Could be `String`, `ByteArray` or any serializable type. Can't be null.
// * @param keys one or more keys to encrypt with.
// * @return encryped and packed container
// */
// inline fun <reified T> encrypt(payload: T, vararg keys: EncryptingKey): ByteArray =
// encryptData(BossEncoder.encode(payload), *keys)
//
// /**
// * Encrypt payload as is, without boss serialization. See [encrypt] for details.
// * @return encrypted packed container
// */
// fun encryptData(payload: ByteArray, vararg keys: EncryptingKey): ByteArray =
// when (keys.size) {
// 0 -> throw IllegalArgumentException("provide at least one key")
// 1 -> single(payload, keys[0])
// else -> multi(payload, keys.toList())
// }
//
// /**
// * Update packed container using proper key in the ring with a new payload. This function os useful
// * when the container could be multiple (but it works with any) and we know only one of its keys,
// * but want to change the data. It is possible as any of the keys could be used to re-encrypt data
// * that _will be available to all other keys_.
// * @param payload packed container
// * @param keyRing keyring which should contain at least one key that can decrypt the contatiner
// * @param packed new payload to encrypt
// * @return packed encrypted container with new payload or null if keyring can't decrypt it
// */
// inline fun <reified T> update(packed: ByteArray, keyRing: IdentifiableKeyring, payload: T): ByteArray? {
// return packed.decodeBoss<Container>().update(keyRing, BossEncoder.encode(payload))
// }
//
// /**
// * Try to decrypt a container using some keys.
// *
// * @param keys to try to open the container.
// * @return the decrypted paylaod on success or null if no keys could open it.
// */
// inline fun <reified T> decrypt(packed: ByteArray, vararg keys: DecryptingKey): T? =
// decrypt<T>(packed, Keyring(*keys))
//
// /**
// * decrypt the container using leys in a keyring.
// * @return recrypted payload or null if no key from a keyring can open it.
// */
// inline fun <reified T> decrypt(packed: ByteArray, ring: IdentifiableKeyring): T? {
// return decryptAsBytes(packed, ring)?.decodeBoss<T>()
// }
//
// fun decryptAsBytes(packed: ByteArray, ring: IdentifiableKeyring): ByteArray? =
// protect {
// packed.decodeBoss<Container>().decrypt(ring)?.second
// }
//
// fun decryptAsBytes(packed: ByteArray, vararg keys: DecryptingKey): ByteArray? =
// protect {
// packed.decodeBoss<Container>().decrypt(Keyring(*keys))?.second
// }
//
// private inline fun <reified T> protect(f: () -> T): T =
// try {
// f()
// } catch (t: Throwable) {
// throw StructureError()
// }
// }
//}

View File

@ -28,7 +28,7 @@ interface DecryptingKey : NonceBased {
fun decryptString(cipherData: UByteArray): String = decrypt(cipherData).decodeFromUByteArray()
val decryptingTag: UByteArray
val decryptingTag: KeyTag
}

View File

@ -27,7 +27,7 @@ interface EncryptingKey : NonceBased {
fun encrypt(plainText: String,randomFill: IntRange? = null): UByteArray =
encrypt(plainText.encodeToUByteArray(),randomFill)
val encryptingTag: UByteArray
val encryptingTag: KeyTag
fun encryptWithNonce(plainData: UByteArray, nonce: UByteArray, randomFill: IntRange? = null): UByteArray
}

View File

@ -14,3 +14,4 @@ enum class Hash(val perform: (UByteArray)->UByteArray) {
fun blake2b(src: UByteArray): UByteArray = Hash.Blake2b.perform(src)
fun blake2b2l(src: UByteArray): UByteArray = blake2b(blake2b(src) + src)
fun blake2b3l(src: UByteArray): UByteArray = blake2b(blake2b2l(src) + src)

View File

@ -0,0 +1,6 @@
package net.sergeych.crypto2
import kotlinx.serialization.Serializable
@Serializable
class KeyDerivationParams()

View File

@ -0,0 +1,28 @@
package net.sergeych.crypto2
import kotlinx.serialization.Serializable
/**
* Tag that identifies in some way the _decrypting key_. Is used in keyrings and
* containers to fast find a proper key
*/
@Serializable
data class KeyTag(val id: BinaryId,val kdp: KeyDerivationParams?=null ) {
val tag: UByteArray by lazy { id.id }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KeyTag) return false
return id == other.id
}
override fun hashCode(): Int {
return id.hashCode()
}
override fun toString() = id.toString()
constructor(magickNumber: KeysMagickNumbers, data: UByteArray,kdp: KeyDerivationParams?=null)
: this(BinaryId.createFromUBytes(magickNumber.number, data), kdp)
}

View File

@ -0,0 +1,16 @@
package net.sergeych.crypto2
enum class KeysMagickNumbers(val number: Int) {
defaultAssymmetric(0),
defaultSymmetric(1),
defaultSession(2),
;
companion object {
val forNumber = entries.map { it.number to it }.toMap()
@Suppress("unused")
fun findFor(binaryId: BinaryId): KeysMagickNumbers? = forNumber[binaryId.magick]
}
}

View File

@ -4,14 +4,6 @@ import com.ionspin.kotlin.crypto.keyexchange.KeyExchange
import kotlinx.serialization.Serializable
import net.sergeych.crypto2.SafeKeyExchange.SessionKey
/**
* Bidirectional key, not necessarily symmetric. It could be asymmetric too. Examples:
*
* - [SafeKeyExchange] provides _asymmetric_ [SafeKeyExchange.SessionKey]
* - [SymmetricKey] is the symmetric cipher key
*/
interface CipherKey : EncryptingKey, DecryptingKey
/**
* Safe (and typesafe) key exchange based on the x25519 protocol.
*
@ -42,7 +34,7 @@ class SafeKeyExchange {
val sendingKey: EncryptingKey,
val receivingKey: DecryptingKey,
val isClient: Boolean,
) : CipherKey, EncryptingKey by sendingKey, DecryptingKey by receivingKey {
) : EncryptingKey by sendingKey, DecryptingKey by receivingKey {
override val nonceBytesLength: Int = sendingKey.nonceBytesLength
@ -55,11 +47,30 @@ class SafeKeyExchange {
@Suppress("unused")
val sessionTag: UByteArray by lazy {
if (!isClient)
blake2b(decryptingTag + encryptingTag)
blake2b(decryptingTag.tag + encryptingTag.tag)
else
blake2b(encryptingTag + decryptingTag)
blake2b(encryptingTag.tag + decryptingTag.tag)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SessionKey) return false
if (sendingKey != other.sendingKey) return false
if (receivingKey != other.receivingKey) return false
if (isClient != other.isClient) return false
return true
}
override fun hashCode(): Int {
var result = sendingKey.hashCode()
result = 31 * result + receivingKey.hashCode()
result = 31 * result + isClient.hashCode()
return result
}
}
/**

View File

@ -20,7 +20,7 @@ import net.sergeych.bipack.BipackEncoder
@Serializable
class SymmetricKey(
val keyBytes: UByteArray,
) : CipherKey {
) : EncryptingKey, DecryptingKey {
/**
* @suppress
@ -55,8 +55,8 @@ class SymmetricKey(
override val nonceBytesLength: Int = nonceByteLength
override val encryptingTag: UByteArray by lazy { blake2b2l(keyBytes) }
override val decryptingTag: UByteArray get() = encryptingTag
override val encryptingTag by lazy { KeyTag(KeysMagickNumbers.defaultSymmetric,blake2b3l(keyBytes)) }
override val decryptingTag get() = encryptingTag
override fun decryptWithNonce(cipherData: UByteArray, nonce: UByteArray): UByteArray =
protectDecryption {
@ -64,6 +64,18 @@ class SymmetricKey(
.data
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SymmetricKey) return false
return keyBytes contentEquals other.keyBytes
}
override fun hashCode(): Int {
return keyBytes.hashCode()
}
companion object {
/**
* Create a secure random symmetric key.

View File

@ -0,0 +1,55 @@
package net.sergeych.crypto2
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable
sealed class UniversalKey : DecryptingKey {
abstract val tag: KeyTag
@Serializable
@SerialName("sy")
data class Symmetric(val key: SymmetricKey) : UniversalKey(), EncryptingKey by key, DecryptingKey by key {
@Transient
override val tag: KeyTag = key.encryptingTag
@Transient
override val nonceBytesLength: Int = key.nonceBytesLength
override fun toString() = "U.Sym:$tag"
}
@Serializable
@SerialName("sn")
data class Session(val key: SafeKeyExchange.SessionKey) : UniversalKey(), EncryptingKey by key,
DecryptingKey by key {
@Transient
override val tag: KeyTag = key.encryptingTag
@Transient
override val nonceBytesLength: Int = key.nonceBytesLength
}
@Serializable
@SerialName("se")
data class Secret(val key: Asymmetric.SecretKey) : UniversalKey(), DecryptingKey by key {
override val tag: KeyTag by lazy { key.decryptingTag }
override fun toString() = "U.Sec:$tag"
}
companion object {
fun from(key: DecryptingKey) =
when (key) {
is Asymmetric.SecretKey -> Secret(key)
is SymmetricKey -> Symmetric(key)
is SafeKeyExchange.SessionKey -> Session(key)
else -> throw UnsupportedOperationException("can't create universal key from ${key::class.simpleName}")
}
}
}

View File

@ -0,0 +1,44 @@
package net.sergeych.crypto2
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable
class UniversalRing(
private val keys: Collection<UniversalKey>
): Collection<UniversalKey> by keys {
constructor(vararg keys: UniversalKey) : this(keys.toSet())
@Transient
val keySet = if( keys is Set<UniversalKey> ) keys else keys.toSet()
private val byTag by lazy { keySet.associateBy { it.tag } }
operator fun get(keyTag: KeyTag): UniversalKey? = byTag[keyTag]
override operator fun contains(element: UniversalKey): Boolean = byTag.containsKey(element.tag)
operator fun plus(key: UniversalKey): UniversalRing =
if( key in this ) this else UniversalRing(keySet + key )
operator fun minus(key: UniversalKey): UniversalRing =
if( key in this ) UniversalRing(keySet.filter { it.tag != key.tag }) else this
operator fun minus(keyTag: KeyTag): UniversalRing =
if( keyTag in byTag ) UniversalRing(keySet.filter { it.tag != keyTag }) else this
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UniversalRing) return false
return size == other.size && keySet == other.keySet
}
override fun toString(): String {
return "Kr[${keys.joinToString(",")}]"
}
override fun hashCode(): Int {
return keySet.hashCode()
}
}

View File

@ -8,4 +8,8 @@ open class Crypto2Exception(text: String,cause: Throwable?=null): RuntimeExcepti
class DecryptionFailedException(text: String="can't decrypt: wrong key or tampered message",
cause: Throwable?=null): Crypto2Exception(text,cause)
class NonceOutOfBoundsException(text: String): Crypto2Exception(text)
class NonceOutOfBoundsException(text: String): Crypto2Exception(text)
@Suppress("unused")
class NotSupportedException(text: String="operation is not supported for this object")
: Crypto2Exception(text, null)

View File

@ -3,6 +3,7 @@ import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import net.sergeych.bipack.BipackEncoder
import net.sergeych.crypto2.*
import net.sergeych.utools.now
import net.sergeych.utools.pack
@ -153,6 +154,11 @@ class KeysTest {
assertContentEquals(plain, m.decrypt(sk1))
}
val x1 = BipackEncoder.encode(Asymmetric.createMessage(sk0, pk1, plain))
val x2 = BipackEncoder.encode(Asymmetric.createMessage(sk0, pk1, plain))
assertFalse { x1 contentEquals x2 }
}
@Test
fun asymmetricKeySerializationTest() = runTest {

View File

@ -0,0 +1,55 @@
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import kotlinx.coroutines.test.runTest
import net.sergeych.bintools.toDump
import net.sergeych.bipack.BipackDecoder
import net.sergeych.bipack.BipackEncoder
import net.sergeych.crypto2.*
import kotlin.test.*
class RingTest {
@Test
fun testCreationAndSerialization() = runTest {
initCrypto()
val y1 = SymmetricKey("1234567890Hello,dolly.here-we-go".encodeToUByteArray())
val y2 = SymmetricKey("1234567890Hello,dolly.here-we-go".encodeToUByteArray())
assertEquals(y1, y2)
val e1 = Asymmetric.randomSecretKey()
val e2: Asymmetric.SecretKey = BipackDecoder.decode(BipackEncoder.encode(e1))
assertEquals(e1, e2)
val k1 = UniversalKey.from(SymmetricKey("1234567890Hello,dolly.here-we-go".encodeToUByteArray()))
val k11 = UniversalKey.from(SymmetricKey("1234567890Hello,dolly.here-we-go".encodeToUByteArray()))
assertEquals(k1, k11)
val k2 = UniversalKey.from(Asymmetric.randomSecretKey())
val k3 = UniversalKey.from(Asymmetric.randomSecretKey())
//
val r = UniversalRing(k1, k2)
// val r = UniversalRing(k1)
assertContains(r, k2)
assertContains(r, k1)
assertFalse { k3 in r }
println(Asymmetric.randomSecretKey().keyBytes.size)
println(BipackEncoder.encode(Asymmetric.randomSecretKey()).size)
val encoded = BipackEncoder.encode(r)
println(encoded.toDump())
println(encoded.size)
assertTrue { encoded.size < 80 }
val r2: UniversalRing = BipackDecoder.decode(encoded)
assertContains(r2, k2)
assertContains(r2, k1)
assertFalse { k3 in r2 }
println(r)
println(r2)
assertEquals(r, r2)
}
}

View File

@ -1,5 +1,8 @@
import kotlinx.coroutines.test.runTest
import net.sergeych.crypto2.*
import net.sergeych.crypto2.NumericNonce
import net.sergeych.crypto2.createContrail
import net.sergeych.crypto2.initCrypto
import net.sergeych.crypto2.isValidContrail
import kotlin.test.*
class ToolsTest {
@ -20,8 +23,8 @@ class ToolsTest {
val counter = 1031
val x1 = nn.withInt(0)
val x2 = nn.withInt(counter)
println(x1.toDump())
println(x2.toDump())
// println(x1.toDump())
// println(x2.toDump())
val t = (x1[0] xor x2[0]).toInt() + ((x1[1] xor x2[1]).toInt() shl 8)
assertEquals(counter, t)
assertContentEquals(x1.drop(2), x2.drop(2))