sergeych 14a63a05c2 - BinaryId is now open
- Container returns data on the keys used to decrypt it
2024-08-28 09:24:33 +02:00

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())
}
}