hash StreamProcessor is now public

better docs on BinaryId
some sugar
This commit is contained in:
Sergey Chernov 2024-09-28 00:34:32 +03:00
parent 8eed7a3de7
commit 194fe22afa
5 changed files with 85 additions and 6 deletions

View File

@ -8,7 +8,7 @@ plugins {
}
group = "net.sergeych"
version = "0.5.8"
version = "0.5.9-SNAPSHOT"
repositories {
mavenCentral()

View File

@ -5,17 +5,60 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.sergeych.bintools.CRC
import net.sergeych.bintools.CRC8
import net.sergeych.crypto2.BinaryId.Companion.createFromBytes
import net.sergeych.crypto2.BinaryId.Companion.createFromString
import net.sergeych.crypto2.BinaryId.Companion.createFromUBytes
import net.sergeych.crypto2.BinaryId.Companion.createRandom
import net.sergeych.crypto2.BinaryId.IncomparableException
import net.sergeych.crypto2.BinaryId.InvalidException
import net.sergeych.mp_tools.decodeBase64Url
import kotlin.random.Random
/**
* Binary identifier with control code and magic number. To create instaance
* use one of [createFromBytes], [createFromString], [createFromUBytes],
* or [createRandom], also deserialize serialized one.
*
* Integrity is checked on instantiating automatically.
*
* It is comparable to other BinaryId as long as both have the same [magic]. Attempt to
* compare these that differ throws [IncomparableException]
*
* ### Internal structure
*
* Say we have a `BinaryId` of size `N` bytes. The inner structure will be:
*
* | offset | meaning |
* |-----------|---------|
* | 0 ..< N-2 | id bytes |
* | N-2 | magic, 0..255 |
* | N-1 | CRC8, polynomial 0xA7, as in Bluetooth |
*
* @throws InvalidException if crc check failed
*/
@Serializable
open class BinaryId protected constructor (
/**
* The packed binary id. Note that serialized version is one byte longer containing
* the size prefix
*/
val id: UByteArray,
) : Comparable<BinaryId> {
/**
* Bad format (crc does not match)
*/
class InvalidException(text: String) : IllegalArgumentException(text)
/**
* Attempt to compare binary ids with different magic. In this case only [equals]
* works, but [compareTo] throws this exception.
*/
class IncomparableException(text: String) : IllegalArgumentException(text)
/**
* magic number (as decoded), `0..255`
*/
@Transient
val magic: Int = run {
if (id.size < 4) throw InvalidException("BinaryId is too short")
@ -30,7 +73,9 @@ open class BinaryId protected constructor (
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.
* The ID body: all the bytes except check and magic. ID bytes could carry useful information.
*
* - `id.size` is [body] size + 2 (see [BinaryId] inner structure)
*/
val body: UByteArray by lazy { id.sliceArray( 0 until id.size-2 ) }
@ -41,6 +86,11 @@ open class BinaryId protected constructor (
VerifyingPublicKey(body)
}
/**
* Try to recnstruct a [PublicKey] from [id] bytes. For such keys, [PublicKey.id] and [SecretKey.id]
* are made from public key bytes so it could be restored from such an ID
*
*/
val asPublicKey: PublicKey by lazy {
if( magic != KeysmagicNumber.defaultAssymmetric.ordinal)
throw InvalidException("It is not a veryfing key: magic=$magic, required ${KeysmagicNumber.defaultAssymmetric.ordinal}")
@ -50,6 +100,11 @@ open class BinaryId protected constructor (
override fun toString(): String = id.encodeToBase64Url()
/**
* Compare to another ID which __must have the same [magic]__ number; note that [equals]
* works well despite magic inequity.
* @throws IncomparableException if magic id do not match
*/
override fun compareTo(other: BinaryId): Int {
if (other.magic != magic) throw IncomparableException("Mask mismatch (my=$magic their=${other.magic})")
val id1 = other.id

View File

@ -70,7 +70,14 @@ class ByteChunk(val data: UByteArray): Comparable<ByteChunk> {
*/
operator fun plus(other: ByteChunk): ByteChunk = ByteChunk(data + other.data)
fun toByteArray(): ByteArray = data.asByteArray()
fun toUByteArray(): UByteArray = data
companion object {
fun fromHex(hex: String): ByteChunk = ByteChunk(hex.decodeHex().asUByteArray())
}
}
}
@Suppress("unused")
fun ByteArray.asChunk() = ByteChunk(toUByteArray())
fun UByteArray.asChunk(): ByteChunk = ByteChunk(this)

View File

@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow
import org.kotlincrypto.hash.sha3.SHA3_256
import org.kotlincrypto.hash.sha3.SHA3_384
private interface StreamProcessor {
interface StreamProcessor {
fun update(data: UByteArray)
fun final(): UByteArray
}
@ -23,7 +23,7 @@ private interface StreamProcessor {
@Suppress("unused")
enum class Hash(
private val direct: ((UByteArray) -> UByteArray)? = null,
private val streamProcessor: () -> StreamProcessor,
val streamProcessor: () -> StreamProcessor,
) {
Blake2b(
@ -111,7 +111,6 @@ enum class Hash(
for (block in source) sp.update(block)
return sp.final()
}
}
private val defaultSuffix1 = "All lay loads on a willing horse".encodeToUByteArray()
@ -122,6 +121,8 @@ private val defaultSuffix2 = "A stitch in time saves nine".encodeToUByteArray()
*/
fun blake2b(src: UByteArray): UByteArray = Hash.Blake2b.digest(src)
fun blake2b(src: ByteChunk): ByteChunk = blake2b(src.data).asChunk()
/**
* Double linked Blake2b using the default or specified suffix. This should be more hard to
* brute force.collision attack than just [blake2b]. Note that different suffixes provide different

View File

@ -0,0 +1,16 @@
import net.sergeych.crypto2.BinaryId
import kotlin.test.Test
import kotlin.test.assertEquals
class BinaryIdTest {
@Test
fun testSizes() {
val a = BinaryId.createRandom(5, 4)
// println(a.id.toDump())
// println(pack(a).toDump())
assertEquals(2, a.body.size)
assertEquals(5, a.magic)
assertEquals(4, a.id.size)
}
}