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" group = "net.sergeych"
version = "0.5.8" version = "0.5.9-SNAPSHOT"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -5,17 +5,60 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import net.sergeych.bintools.CRC import net.sergeych.bintools.CRC
import net.sergeych.bintools.CRC8 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 net.sergeych.mp_tools.decodeBase64Url
import kotlin.random.Random 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 @Serializable
open class BinaryId protected constructor ( open class BinaryId protected constructor (
/**
* The packed binary id. Note that serialized version is one byte longer containing
* the size prefix
*/
val id: UByteArray, val id: UByteArray,
) : Comparable<BinaryId> { ) : Comparable<BinaryId> {
/**
* Bad format (crc does not match)
*/
class InvalidException(text: String) : IllegalArgumentException(text) 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) class IncomparableException(text: String) : IllegalArgumentException(text)
/**
* magic number (as decoded), `0..255`
*/
@Transient @Transient
val magic: Int = run { val magic: Int = run {
if (id.size < 4) throw InvalidException("BinaryId is too short") 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 ) } 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 ) } val body: UByteArray by lazy { id.sliceArray( 0 until id.size-2 ) }
@ -41,6 +86,11 @@ open class BinaryId protected constructor (
VerifyingPublicKey(body) 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 { val asPublicKey: PublicKey by lazy {
if( magic != KeysmagicNumber.defaultAssymmetric.ordinal) if( magic != KeysmagicNumber.defaultAssymmetric.ordinal)
throw InvalidException("It is not a veryfing key: magic=$magic, required ${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() 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 { override fun compareTo(other: BinaryId): Int {
if (other.magic != magic) throw IncomparableException("Mask mismatch (my=$magic their=${other.magic})") if (other.magic != magic) throw IncomparableException("Mask mismatch (my=$magic their=${other.magic})")
val id1 = other.id 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) operator fun plus(other: ByteChunk): ByteChunk = ByteChunk(data + other.data)
fun toByteArray(): ByteArray = data.asByteArray()
fun toUByteArray(): UByteArray = data
companion object { companion object {
fun fromHex(hex: String): ByteChunk = ByteChunk(hex.decodeHex().asUByteArray()) 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_256
import org.kotlincrypto.hash.sha3.SHA3_384 import org.kotlincrypto.hash.sha3.SHA3_384
private interface StreamProcessor { interface StreamProcessor {
fun update(data: UByteArray) fun update(data: UByteArray)
fun final(): UByteArray fun final(): UByteArray
} }
@ -23,7 +23,7 @@ private interface StreamProcessor {
@Suppress("unused") @Suppress("unused")
enum class Hash( enum class Hash(
private val direct: ((UByteArray) -> UByteArray)? = null, private val direct: ((UByteArray) -> UByteArray)? = null,
private val streamProcessor: () -> StreamProcessor, val streamProcessor: () -> StreamProcessor,
) { ) {
Blake2b( Blake2b(
@ -111,7 +111,6 @@ enum class Hash(
for (block in source) sp.update(block) for (block in source) sp.update(block)
return sp.final() return sp.final()
} }
} }
private val defaultSuffix1 = "All lay loads on a willing horse".encodeToUByteArray() 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: 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 * 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 * 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)
}
}