diff options
author | Torsten Grote <t@grobox.de> | 2020-06-30 16:04:04 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-06-30 16:04:04 -0300 |
commit | 08f5f3618b5a9d9f38f7da0dbff39164c8b5f77b (patch) | |
tree | fec1e932c247da66f7087d5019a66fd4eb51ff76 /src | |
parent | 1a6121d7246b8dbf39eba2e91c92507c2b85e3d2 (diff) | |
download | wallet-kotlin-08f5f3618b5a9d9f38f7da0dbff39164c8b5f77b.tar.gz wallet-kotlin-08f5f3618b5a9d9f38f7da0dbff39164c8b5f77b.tar.bz2 wallet-kotlin-08f5f3618b5a9d9f38f7da0dbff39164c8b5f77b.zip |
Add incremental hashing (needed for creating refresh sessions)
Diffstat (limited to 'src')
5 files changed, 150 insertions, 6 deletions
diff --git a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt index 7435c3f..b54d93e 100644 --- a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt +++ b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt @@ -3,6 +3,7 @@ package net.taler.wallet.kotlin.crypto import com.goterl.lazycode.lazysodium.LazySodiumJava import com.goterl.lazycode.lazysodium.SodiumJava import com.goterl.lazycode.lazysodium.interfaces.Hash +import com.goterl.lazycode.lazysodium.interfaces.Hash.State512 import com.goterl.lazycode.lazysodium.interfaces.KeyExchange import com.goterl.lazycode.lazysodium.interfaces.Sign import com.goterl.lazycode.lazysodium.utils.Key @@ -27,6 +28,10 @@ internal object CryptoJvmImpl : CryptoImpl() { return output } + override fun getHashSha512State(): HashSha512State { + return JvmHashSha512State() + } + override fun getRandomBytes(num: Int): ByteArray { return sodium.randomBytesBuf(num) } @@ -88,4 +93,24 @@ internal object CryptoJvmImpl : CryptoImpl() { return RsaBlinding.rsaVerify(hm, rsaSig, rsaPubEnc) } + private class JvmHashSha512State : HashSha512State { + private val state = State512() + + init { + check(sodium.cryptoHashSha512Init(state)) { "Error doing cryptoHashSha512Init" } + } + + override fun update(data: ByteArray): HashSha512State { + sodium.cryptoHashSha512Update(state, data, data.size.toLong()) + return this + } + + override fun final(): ByteArray { + val output = ByteArray(Hash.SHA512_BYTES) + sodium.cryptoHashSha512Final(state, output) + return output + } + + } + } diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt index 7019310..7c5fa29 100644 --- a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt +++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt @@ -3,6 +3,7 @@ package net.taler.wallet.kotlin.crypto internal interface Crypto { fun sha256(input: ByteArray): ByteArray fun sha512(input: ByteArray): ByteArray + fun getHashSha512State(): HashSha512State fun getRandomBytes(num: Int): ByteArray fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray @@ -19,6 +20,10 @@ internal interface Crypto { fun setupRefreshPlanchet(secretSeed: ByteArray, coinNumber: Int): FreshCoin } +interface HashSha512State { + fun update(data: ByteArray): HashSha512State + fun final(): ByteArray +} class EddsaKeyPair(val privateKey: ByteArray, val publicKey: ByteArray) class EcdheKeyPair(val privateKey: ByteArray, val publicKey: ByteArray) data class FreshCoin(val coinPublicKey: ByteArray, val coinPrivateKey: ByteArray, val bks: ByteArray) { diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt index c401b73..309e053 100644 --- a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt +++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt @@ -1,10 +1,10 @@ package net.taler.wallet.kotlin.crypto import net.taler.wallet.kotlin.Base32Crockford +import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals -@kotlin.ExperimentalStdlibApi class Sha512Test { private val crypto = CryptoFactory.getCrypto() @@ -59,4 +59,42 @@ class Sha512Test { assertEquals(output, Base32Crockford.encode(crypto.sha512(Base32Crockford.decode(input)))) } + @Test + fun testIncrementalHashing() { + val n = 1024 + val d = Random.nextBytes(n) + + val h1 = crypto.sha512(d) + val h2 = crypto.getHashSha512State().update(d).final() + assertEquals(Base32Crockford.encode(h1), Base32Crockford.encode(h2)) + + val s = crypto.getHashSha512State() + for (i in 0 until n) { + val b = ByteArray(1) + b[0] = d[i] + s.update(b) + } + val h3 = s.final() + assertEquals(Base32Crockford.encode(h1), Base32Crockford.encode(h3)) + } + + @Test + fun testIncrementalHashing2() { + val n = 10 + val d = Random.nextBytes(n) + + val h1 = crypto.sha512(d) + val h2 = crypto.getHashSha512State().update(d).final() + assertEquals(Base32Crockford.encode(h1), Base32Crockford.encode(h2)) + + val s = crypto.getHashSha512State() + for (i in 0 until n) { + val b = ByteArray(1) + b[0] = d[i] + s.update(b) + } + val h3 = s.final() + assertEquals(Base32Crockford.encode(h1), Base32Crockford.encode(h3)) + } + } diff --git a/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt b/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt index 7cf210a..dab9ecc 100644 --- a/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt +++ b/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt @@ -10,13 +10,17 @@ internal actual object CryptoFactory { internal object CryptoJsImpl : CryptoImpl() { override fun sha256(input: ByteArray): ByteArray { - return sha256(input.toUint8Array()).toByteArray() + return hash.sha256().update(input.toUint8Array()).digest().toByteArray() } override fun sha512(input: ByteArray): ByteArray { return nacl.hash(input.toUint8Array()).toByteArray() } + override fun getHashSha512State(): HashSha512State { + return JsHashSha512State() + } + override fun getRandomBytes(num: Int): ByteArray { return nacl.randomBytes(num).toByteArray() } @@ -77,6 +81,19 @@ internal object CryptoJsImpl : CryptoImpl() { TODO("Not yet implemented") } + private class JsHashSha512State : HashSha512State { + private val state = hash.sha512() + + override fun update(data: ByteArray): HashSha512State { + state.update(data.toUint8Array()) + return this + } + + override fun final(): ByteArray { + return state.digest().toByteArray() + } + } + private fun Uint8Array.toByteArray(): ByteArray { val result = ByteArray(this.length) for (i in 0 until this.length) result[i] = this[i] @@ -110,11 +127,13 @@ private external class nacl { companion object { fun detached(msg: Uint8Array, secretKey: Uint8Array): Uint8Array } + class detached { companion object { fun verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): Boolean } } + class keyPair { companion object { fun fromSeed(seed: Uint8Array): KeyPair @@ -134,6 +153,17 @@ private external class ed2curve { } } -@JsModule("fast-sha256") +@Suppress("ClassName") +@JsModule("hash.js") @JsNonModule -private external fun sha256(message: Uint8Array): Uint8Array +private external class hash { + class sha256 { + fun update(message: Uint8Array): sha256 + fun digest(): Uint8Array + } + + class sha512 { + fun update(message: Uint8Array): sha512 + fun digest(): Uint8Array + } +} diff --git a/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt b/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt index a44bc46..b0bb5b2 100644 --- a/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt +++ b/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt @@ -2,11 +2,19 @@ package net.taler.wallet.kotlin.crypto import kotlinx.cinterop.CValuesRef import kotlinx.cinterop.UByteVar +import kotlinx.cinterop.alloc +import kotlinx.cinterop.free +import kotlinx.cinterop.nativeHeap +import kotlinx.cinterop.ptr import kotlinx.cinterop.refTo import org.libsodium.crypto_hash_sha256 import org.libsodium.crypto_hash_sha256_bytes import org.libsodium.crypto_hash_sha512 import org.libsodium.crypto_hash_sha512_bytes +import org.libsodium.crypto_hash_sha512_final +import org.libsodium.crypto_hash_sha512_init +import org.libsodium.crypto_hash_sha512_state +import org.libsodium.crypto_hash_sha512_update import org.libsodium.crypto_scalarmult import org.libsodium.crypto_scalarmult_BYTES import org.libsodium.crypto_scalarmult_base @@ -41,6 +49,10 @@ internal object CryptoNativeImpl : CryptoImpl() { return output } + override fun getHashSha512State(): HashSha512State { + return NativeHashSha512State() + } + override fun getRandomBytes(num: Int): ByteArray { val bytes = ByteArray(num) randombytes(bytes.toCValuesRef(), num.toULong()) @@ -80,12 +92,23 @@ internal object CryptoNativeImpl : CryptoImpl() { crypto_sign_seed_keypair(publicKey.toCValuesRef(), privateKey.toCValuesRef(), eddsaPrivateKey.toCValuesRef()) val signatureBytes = ByteArray(crypto_sign_BYTES.toInt()) - crypto_sign_detached(signatureBytes.toCValuesRef(), null, msg.toCValuesRef(), msg.size.toULong(), privateKey.toCValuesRef()) + crypto_sign_detached( + signatureBytes.toCValuesRef(), + null, + msg.toCValuesRef(), + msg.size.toULong(), + privateKey.toCValuesRef() + ) return signatureBytes } override fun eddsaVerify(msg: ByteArray, sig: ByteArray, eddsaPub: ByteArray): Boolean { - return crypto_sign_verify_detached(sig.toCValuesRef(), msg.toCValuesRef(), msg.size.toULong(), eddsaPub.toCValuesRef()) == 0 + return crypto_sign_verify_detached( + sig.toCValuesRef(), + msg.toCValuesRef(), + msg.size.toULong(), + eddsaPub.toCValuesRef() + ) == 0 } override fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: ByteArray): ByteArray { @@ -117,6 +140,29 @@ internal object CryptoNativeImpl : CryptoImpl() { TODO("Not yet implemented") } + private class NativeHashSha512State : HashSha512State { + private val state = nativeHeap.alloc<crypto_hash_sha512_state>() + private val statePointer = state.ptr + + init { + check(crypto_hash_sha512_init(statePointer) == 0) { "Error doing crypto_hash_sha512_init" } + } + + override fun update(data: ByteArray): HashSha512State { + val cInput = if (data.isEmpty()) null else data.toCValuesRef() + crypto_hash_sha512_update(statePointer, cInput, data.size.toULong()) + return this + } + + override fun final(): ByteArray { + val output = ByteArray(crypto_hash_sha512_bytes().toInt()) + crypto_hash_sha512_final(statePointer, output.toCValuesRef()) + nativeHeap.free(statePointer) + return output + } + + } + private fun ByteArray.toCValuesRef(): CValuesRef<UByteVar> { @Suppress("UNCHECKED_CAST") return this.refTo(0) as CValuesRef<UByteVar> |