summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-06-30 16:04:04 -0300
committerTorsten Grote <t@grobox.de>2020-06-30 16:04:04 -0300
commit08f5f3618b5a9d9f38f7da0dbff39164c8b5f77b (patch)
treefec1e932c247da66f7087d5019a66fd4eb51ff76 /src
parent1a6121d7246b8dbf39eba2e91c92507c2b85e3d2 (diff)
downloadwallet-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')
-rw-r--r--src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt25
-rw-r--r--src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt5
-rw-r--r--src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt40
-rw-r--r--src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt36
-rw-r--r--src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt50
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>