diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index df543e3..fb6d147 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 603f21d..b2a0f1c 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ All primitives meant to send over the network or store are `kotlinx.serializatio
# Important notes on upgrade
Since version __0.5.*__ key identity calculation for asymmetric keys is updated
-to make it more safe for theoretic future attack on blake2b hashing. Key.id values
+to make it safer for theoretic future attack on blake2b hashing. Key.id values
are incompatible with older. Sorry for inconvenience.
# Usage
@@ -19,7 +19,7 @@ repositories {
maven("https://gitea.sergeych.net/api/packages/SergeychWorks/maven")
}
dependencies {
- import("net.sergeych:crypto2:0.5.4")
+ import("net.sergeych:crypto2:0.5.5")
}
```
@@ -59,13 +59,13 @@ Please see the current documentation [here](https://code.sergeych.net/docs/crypt
## Diffie-Hellman safe key exchange
-Using X25519 curves, generates two symmetric keys with XSalsa20+Poly1305 for independent p2p ciphers and session token, same on both sides.
+Using X25519 curves, it generates two symmetric keys with XSalsa20+Poly1305 for independent p2p ciphers and session token, same on both sides.
## Multi-signed container
A container with random binary or `kolinx.serialized` data, signed with one or more Ed25519 secret keys. Signatures could be added sequentially, each signature contains timestamp and the optional expiration. __Blake2b__ fast and strong hashing is used.
-Signatures include public keys (as these are short for Ed25519) which can be used to identity signing party easily.
+Signatures include public keys (as these are short for Ed25519) which can be used to easily identify the signing party.
## Multi-key encrypted contained.
@@ -75,9 +75,9 @@ The `koltlinx.serialized` message intended to be readable by owner of one of an
- Secret/Public keys Ed25519.
- password-derived keys (PBKDF generation parameters are stored in the container so the key could be derived from the password)
-Once the container is decrypted with any of the intended keys, it could be re-encrypted with new data and/or new destination keys, keeping all existing keys event not having them. E.g., it allows "reply all" function even when not all recipients are known and even if the symmetric key cryptography is used.
+Once the container is decrypted with any of the intended keys, it could be re-encrypted with new data and/or new destination keys, keeping all existing keys events not having them. E.g., it allows "reply all" function even when not all recipients are known and even if the symmetric key cryptography is used.
-The proper keys are retrieved from the keyrings (below) automatically.
+The proper keys are retrieved from the keyring (below) automatically.
## Keyring
diff --git a/build.gradle.kts b/build.gradle.kts
index c28e84d..84f1a12 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,7 +6,7 @@ plugins {
}
group = "net.sergeych"
-version = "0.5.4"
+version = "0.5.5"
repositories {
mavenCentral()
@@ -48,7 +48,7 @@ kotlin {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0")
implementation("com.ionspin.kotlin:multiplatform-crypto-libsodium-bindings:0.9.2")
- implementation(platform("org.kotlincrypto.hash:bom:0.5.1"))
+ implementation(project.dependencies.platform("org.kotlincrypto.hash:bom:0.5.1"))
implementation("org.kotlincrypto.hash:sha3")
api("com.ionspin.kotlin:bignum:0.3.9")
api("net.sergeych:mp_bintools:0.1.7")
diff --git a/src/commonMain/kotlin/net/sergeych/crypto2/Hash.kt b/src/commonMain/kotlin/net/sergeych/crypto2/Hash.kt
index e27c4b4..3eb2e9e 100644
--- a/src/commonMain/kotlin/net/sergeych/crypto2/Hash.kt
+++ b/src/commonMain/kotlin/net/sergeych/crypto2/Hash.kt
@@ -21,21 +21,24 @@ private interface StreamProcessor {
* - [ofFlow] to calculate hash of the data in the [Flow]
*/
@Suppress("unused")
-enum class Hash(private val direct: ((UByteArray) -> UByteArray)? = null,private val streamProcessor: () -> StreamProcessor) {
+enum class Hash(
+ private val direct: ((UByteArray) -> UByteArray)? = null,
+ private val streamProcessor: () -> StreamProcessor,
+) {
Blake2b(
// direct blacke2 is faster than stream:
{ GenericHash.genericHash(it) }, {
- object : StreamProcessor {
- val state = GenericHash.genericHashInit()
- override fun update(data: UByteArray) {
- GenericHash.genericHashUpdate(state, data)
- }
+ object : StreamProcessor {
+ val state = GenericHash.genericHashInit()
+ override fun update(data: UByteArray) {
+ GenericHash.genericHashUpdate(state, data)
+ }
- override fun final(): UByteArray =
- GenericHash.genericHashFinal(state)
- }
- }),
+ override fun final(): UByteArray =
+ GenericHash.genericHashFinal(state)
+ }
+ }),
Sha3_384(
// direct Keccaak currently is slower
null,
@@ -59,6 +62,24 @@ enum class Hash(private val direct: ((UByteArray) -> UByteArray)? = null,private
override fun final(): UByteArray = state.digest().asUByteArray()
}
+ }),
+
+ /**
+ * Synthetic hash contains of [Blake2b] and [Sha3_384] hashes concatenated in this order. Let calculate
+ * compound hash using [ofFlow] or [ofChannel] effectively.
+ */
+ Sha3AndBlake(null,
+ {
+ object : StreamProcessor {
+ val blake = Blake2b.streamProcessor()
+ val sha384 = Sha3_384.streamProcessor()
+ override fun update(data: UByteArray) {
+ blake.update(data)
+ sha384.update(data)
+ }
+
+ override fun final(): UByteArray = blake.final() + sha384.final()
+ }
});
@Deprecated("will be removed in favor of digest()", ReplaceWith("digest()"))
@@ -69,7 +90,7 @@ enum class Hash(private val direct: ((UByteArray) -> UByteArray)? = null,private
* @param src data to calculate hash digest over
* @return calculated hash value
*/
- fun digest(src: UByteArray): UByteArray = direct?.invoke(src) ?: streamProcessor().also { it.update(src)}.final()
+ fun digest(src: UByteArray): UByteArray = direct?.invoke(src) ?: streamProcessor().also { it.update(src) }.final()
/**
* Collect the flow and return the hash digest of all the data. Let calculate hashes on data
@@ -87,7 +108,7 @@ enum class Hash(private val direct: ((UByteArray) -> UByteArray)? = null,private
*/
suspend fun ofChannel(source: ReceiveChannel): UByteArray {
val sp = streamProcessor()
- for( block in source) sp.update(block)
+ for (block in source) sp.update(block)
return sp.final()
}
@@ -106,7 +127,7 @@ fun blake2b(src: UByteArray): UByteArray = Hash.Blake2b.digest(src)
* brute force.collision attack than just [blake2b]. Note that different suffixes provide different
* results.
*/
-fun blake2b2l(src: UByteArray,suffix: UByteArray = defaultSuffix1): UByteArray =
+fun blake2b2l(src: UByteArray, suffix: UByteArray = defaultSuffix1): UByteArray =
blake2b(blake2b(src) + suffix + src)
/**
diff --git a/src/commonTest/kotlin/HashTest.kt b/src/commonTest/kotlin/HashTest.kt
index 9bc9645..9bc8693 100644
--- a/src/commonTest/kotlin/HashTest.kt
+++ b/src/commonTest/kotlin/HashTest.kt
@@ -35,4 +35,16 @@ class HashTest {
testMethod(Hash.Sha3_384)
}
}
+
+ @Test
+ fun testSynthetic() = runTest {
+ initCrypto()
+ val a = Random.Default.nextUBytes(1024)
+ val x = Hash.Sha3AndBlake.digest(a)
+ val p1 = x.sliceArray(0..31)
+ val p2 = x.drop(32)
+ assertContentEquals(Hash.Blake2b.digest(a), p1)
+ assertContentEquals(Hash.Sha3_384.digest(a), p2)
+ }
}
+