hash StreamProcessor is now public
better docs on BinaryId some sugar
This commit is contained in:
parent
8eed7a3de7
commit
194fe22afa
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.5.8"
|
version = "0.5.9-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
@ -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
|
||||||
|
16
src/commonTest/kotlin/BinaryIdTest.kt
Normal file
16
src/commonTest/kotlin/BinaryIdTest.kt
Normal 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)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user