forked from sergeych/crypto2
113 lines
3.7 KiB
Kotlin
113 lines
3.7 KiB
Kotlin
package net.sergeych.crypto2
|
|
|
|
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
|
|
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
|
|
open class BinaryId protected constructor (
|
|
val id: UByteArray,
|
|
) : Comparable<BinaryId> {
|
|
|
|
class InvalidException(text: String) : IllegalArgumentException(text)
|
|
class IncomparableException(text: String) : IllegalArgumentException(text)
|
|
|
|
@Transient
|
|
val magic: 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 ) }
|
|
|
|
/**
|
|
* The id body: all the bytes except check and magic. These could carry useful information.
|
|
*/
|
|
val body: UByteArray by lazy { id.sliceArray( 0 until id.size-2 ) }
|
|
|
|
val asVerifyingKey: VerifyingKey by lazy {
|
|
if( magic != KeysmagicNumber.defaultVerifying.ordinal)
|
|
throw InvalidException("It is not a veryfing key: magic=$magic, required ${KeysmagicNumber.defaultVerifying.ordinal}")
|
|
check(body.size == 32)
|
|
VerifyingPublicKey(body)
|
|
}
|
|
|
|
val asPublicKey: PublicKey by lazy {
|
|
if( magic != KeysmagicNumber.defaultAssymmetric.ordinal)
|
|
throw InvalidException("It is not a veryfing key: magic=$magic, required ${KeysmagicNumber.defaultAssymmetric.ordinal}")
|
|
check(body.size == 32)
|
|
PublicKey(body)
|
|
}
|
|
|
|
override fun toString(): String = id.encodeToBase64Url()
|
|
|
|
override fun compareTo(other: BinaryId): Int {
|
|
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)) {
|
|
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 {
|
|
|
|
/**
|
|
* Restore a string representation of existing BinaryId.
|
|
*/
|
|
@Suppress("unused")
|
|
fun restoreFromString(str: String): BinaryId =
|
|
BinaryId(str.decodeBase64Url().toUByteArray())
|
|
|
|
|
|
fun createFromBytes(magic: Int, bytes: ByteArray): BinaryId = createFromUBytes(magic, bytes.toUByteArray())
|
|
|
|
fun createFromUBytes(magic: Int, bytes: UByteArray): BinaryId {
|
|
val crc = CRC8()
|
|
val mn = magic.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(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 magic.
|
|
*/
|
|
fun createFromString(magicNumber: Int, text: String): BinaryId =
|
|
createFromUBytes(magicNumber, text.encodeToUByteArray())
|
|
}
|
|
} |