gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-kotlin] branch master updated: Add GNUnet's HKDF implement


From: gnunet
Subject: [taler-wallet-kotlin] branch master updated: Add GNUnet's HKDF implementation to Crypto interface
Date: Thu, 04 Jun 2020 19:07:14 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository wallet-kotlin.

The following commit(s) were added to refs/heads/master by this push:
     new f37a38d  Add GNUnet's HKDF implementation to Crypto interface
f37a38d is described below

commit f37a38dc0fb6a144b8e9ea898331fb8665e602fc
Author: Torsten Grote <t@grobox.de>
AuthorDate: Thu Jun 4 14:04:34 2020 -0300

    Add GNUnet's HKDF implementation to Crypto interface
    
    including a common implementation for all platforms
---
 .gitlab-ci.yml                                     |   1 +
 .idea/codeStyles/Project.xml                       |   3 +
 .idea/dictionaries/user.xml                        |   1 +
 build.gradle                                       |   1 +
 .../taler/wallet/kotlin/crypto/CryptoFactory.kt    |   8 +-
 .../net/taler/wallet/kotlin/crypto/Crypto.kt       |   2 +
 .../net/taler/wallet/kotlin/crypto/CryptoImpl.kt   |   9 +
 .../kotlin/net/taler/wallet/kotlin/crypto/Kdf.kt   |  74 ++++++++
 .../net/taler/wallet/kotlin/crypto/KdfTest.kt      | 197 +++++++++++++++++++++
 .../net/taler/wallet/kotlin/crypto/Sha256Test.kt   |  54 ++++++
 .../taler/wallet/kotlin/crypto/CryptoFactory.kt    |  10 +-
 .../taler/wallet/kotlin/crypto/CryptoFactory.kt    |   9 +-
 12 files changed, 366 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 45a3dc3..9725e9a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,6 +12,7 @@ cache:
     - .konan/dependencies
     - .gradle/wrapper
     - .gradle/caches
+    - build/js/node_modules
 
 test:
   script: ./gradlew check
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 1bec35e..89a53dc 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,6 +1,9 @@
 <component name="ProjectCodeStyleConfiguration">
   <code_scheme name="Project" version="173">
     <JetCodeStyleSettings>
+      <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
+      <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" 
value="2147483647" />
+      <option name="IMPORT_NESTED_CLASSES" value="true" />
       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
     </JetCodeStyleSettings>
     <codeStyleSettings language="kotlin">
diff --git a/.idea/dictionaries/user.xml b/.idea/dictionaries/user.xml
index 75c46ba..5f88694 100644
--- a/.idea/dictionaries/user.xml
+++ b/.idea/dictionaries/user.xml
@@ -3,6 +3,7 @@
     <words>
       <w>ecdhe</w>
       <w>eddsa</w>
+      <w>hmac</w>
       <w>nacl</w>
     </words>
   </dictionary>
diff --git a/build.gradle b/build.gradle
index b87f250..ab969f5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -62,6 +62,7 @@ kotlin {
                 implementation kotlin('stdlib-js')
                 implementation npm('tweetnacl', '1.0.3')
                 implementation npm('ed2curve', '0.3.0')
+                implementation npm('fast-sha256', '1.3.0')
             }
         }
         jsTest {
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 0bc933a..d41b615 100644
--- a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -11,10 +11,16 @@ internal actual object CryptoFactory {
     internal actual fun getCrypto(): Crypto = CryptoJvmImpl
 }
 
-internal object CryptoJvmImpl : Crypto {
+internal object CryptoJvmImpl : CryptoImpl() {
 
     private val sodium = LazySodiumJava(SodiumJava())
 
+    override fun sha256(input: ByteArray): ByteArray {
+        val output = ByteArray(Hash.SHA256_BYTES)
+        sodium.cryptoHashSha256(output, input, input.size.toLong())
+        return output
+    }
+
     override fun sha512(input: ByteArray): ByteArray {
         val output = ByteArray(Hash.SHA512_BYTES)
         sodium.cryptoHashSha512(output, input, input.size.toLong())
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 3735c31..c5c0a0f 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt
@@ -1,6 +1,7 @@
 package net.taler.wallet.kotlin.crypto
 
 internal interface Crypto {
+    fun sha256(input: ByteArray): ByteArray
     fun sha512(input: ByteArray): ByteArray
     fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray
     fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray
@@ -10,6 +11,7 @@ internal interface Crypto {
     fun eddsaVerify(msg: ByteArray, sig: ByteArray, eddsaPub: ByteArray): 
Boolean
     fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: 
ByteArray): ByteArray
     fun keyExchangeEcdheEddsa(ecdhePrivateKey: ByteArray, eddsaPublicKey: 
ByteArray): ByteArray
+    fun kdf(outputLength: Int, ikm: ByteArray, salt: ByteArray, info: 
ByteArray): ByteArray
 }
 
 class EddsaKeyPair(val privateKey: ByteArray, val publicKey: ByteArray)
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoImpl.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoImpl.kt
new file mode 100644
index 0000000..ae333eb
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoImpl.kt
@@ -0,0 +1,9 @@
+package net.taler.wallet.kotlin.crypto
+
+abstract class CryptoImpl : Crypto {
+
+    override fun kdf(outputLength: Int, ikm: ByteArray, salt: ByteArray, info: 
ByteArray): ByteArray {
+        return Kdf.kdf(outputLength, ikm, salt, info, { sha256(it) }, { 
sha512(it) })
+    }
+
+}
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Kdf.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Kdf.kt
new file mode 100644
index 0000000..d6a7658
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Kdf.kt
@@ -0,0 +1,74 @@
+package net.taler.wallet.kotlin.crypto
+
+import kotlin.experimental.xor
+import kotlin.math.ceil
+
+internal object Kdf {
+
+    const val HMAC_SHA256_BLOCK_SIZE = 64
+    const val HMAC_SHA512_BLOCK_SIZE = 128
+
+    fun kdf(
+        outputLength: Int,
+        ikm: ByteArray,
+        salt: ByteArray,
+        info: ByteArray,
+        sha256: (ByteArray) -> ByteArray,
+        sha512: (ByteArray) -> ByteArray
+    ): ByteArray {
+        //extract
+        val prk = hmacSha512(salt, ikm, sha512)
+
+        // expand
+        val n = ceil(outputLength.toDouble() / 32).toInt()
+        val output = ByteArray(n * 32)
+        for (i in 0 until n) {
+            val buf: ByteArray
+            if (i == 0) {
+                buf = ByteArray(info.size + 1)
+                info.copyInto(buf)
+            } else {
+                buf = ByteArray(info.size + 1 + 32)
+                for (j in 0 until 32) {
+                    buf[j] = output[(i - 1) * 32 + j]
+                }
+                info.copyInto(buf, destinationOffset = 32)
+            }
+            buf[buf.size - 1] = (i + 1).toByte()
+            val chunk = hmacSha256(prk, buf, sha256)
+            chunk.copyInto(output, destinationOffset = i * 32)
+        }
+        return output.copyOfRange(0, outputLength)
+    }
+
+    fun hmacSha256(key: ByteArray, message: ByteArray, sha256: (ByteArray) -> 
ByteArray): ByteArray {
+        return hmac(HMAC_SHA256_BLOCK_SIZE, key, message) { sha256(it) }
+    }
+
+    fun hmacSha512(key: ByteArray, message: ByteArray, sha512: (ByteArray) -> 
ByteArray): ByteArray {
+        return hmac(HMAC_SHA512_BLOCK_SIZE, key, message) { sha512(it) }
+    }
+
+    private fun hmac(blockSize: Int, key: ByteArray, message: ByteArray, hash: 
(ByteArray) -> ByteArray): ByteArray {
+        var newKey = key
+        if (newKey.size > blockSize) newKey = hash(newKey)
+        if (newKey.size < blockSize) newKey = ByteArray(blockSize).apply {
+            newKey.copyInto(this)
+        }
+        val okp = ByteArray(blockSize)
+        val ikp = ByteArray(blockSize)
+        for (i in 0 until blockSize) {
+            ikp[i] = newKey[i] xor 0x36
+            okp[i] = newKey[i] xor 0x5c
+        }
+        val b1 = ByteArray(blockSize + message.size)
+        ikp.copyInto(b1)
+        message.copyInto(b1, destinationOffset = blockSize)
+        val h0 = hash(b1)
+        val b2 = ByteArray(blockSize + h0.size)
+        okp.copyInto(b2)
+        h0.copyInto(b2, destinationOffset = blockSize)
+        return hash(b2)
+    }
+
+}
diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/KdfTest.kt 
b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/KdfTest.kt
new file mode 100644
index 0000000..702d6e3
--- /dev/null
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/KdfTest.kt
@@ -0,0 +1,197 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.crypto.Kdf.HMAC_SHA256_BLOCK_SIZE
+import net.taler.wallet.kotlin.crypto.Kdf.HMAC_SHA512_BLOCK_SIZE
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class KdfTest {
+
+    private val crypto = CryptoFactory.getCrypto()
+
+    @Test
+    fun testHmacSha256() {
+        // key smaller than block size
+        val key1 = "EDJP6WK5EG"
+        val message1 = "91JPRV3F41BPYWKCCGGG"
+        val expectedOut1 = 
"DYKV9QN2HVHMHQRGZ6XNJPPSGQZHA2JAVZB1676ACXYSNKQ0FQ30"
+        val out1 = Kdf.hmacSha256(Base32Crockford.decode(key1), 
Base32Crockford.decode(message1)) { crypto.sha256(it) }
+        assertTrue(Base32Crockford.decode(key1).size < HMAC_SHA256_BLOCK_SIZE)
+        assertEquals(expectedOut1, Base32Crockford.encode(out1))
+
+        // key bigger than block size
+        val key2 =
+            
"AHM6JWS089GQ6S9K68G7CRBJD5GPWX10EXGQ6833E9JP2X35CGG64Y908HQQASVCC5SJ0GVJDXHPPSKFE9J20X3F41GQCVV9CGG66VVECSTQ6TBFDRQ0"
+        val message2 = "89P62RVB4166JXK5ECG4TRBMEHJQ488"
+        val expectedOut2 = 
"QGEK8853DW3GSQ4RHDYXZ0KX9R6E356R5495Z10KDNFNZ98V9FQG"
+        val out2 = Kdf.hmacSha256(Base32Crockford.decode(key2), 
Base32Crockford.decode(message2)) { crypto.sha256(it) }
+        assertTrue(Base32Crockford.decode(key2).size > HMAC_SHA256_BLOCK_SIZE)
+        assertEquals(expectedOut2, Base32Crockford.encode(out2))
+
+        // key exactly block size
+        val key3 =
+            
"AHM6JWS0EDJP6WK5EGG6PSBS41MQ6831ECG6RVVECWG62WS0EHM6A832DHQP6TS0EDMQMS90DXK20J2D851J0MT884S3ADH07MG3CD0"
+        val message3 = "89P62RVB4166JXK5ECG4TRBMEHJQ488"
+        val expectedOut3 = 
"HAEWRWXEWWPRMT4W6E32GS0P6QMW85MMZZA5CX2BMJW36QZQFJ2G"
+        val out3 = Kdf.hmacSha256(Base32Crockford.decode(key3), 
Base32Crockford.decode(message3)) { crypto.sha256(it) }
+        assertEquals(HMAC_SHA256_BLOCK_SIZE, Base32Crockford.decode(key3).size)
+        assertEquals(expectedOut3, Base32Crockford.encode(out3))
+    }
+
+    @Test
+    fun testHmacSha512() {
+        // key smaller than block size
+        val key1 = "EDJP6WK5EG"
+        val message1 = "91JPRV3F41BPYWKCCGGG"
+        val expectedOut1 =
+            
"ZMVHDTWE341YSE6YQ4S0Y6XESW7RVFG4WK2CE7K4ZESQ10125JJ2VTRBCHPAQTTKW9MT4H350ZXN8GMP2SBFG03SB6YK63QMHQRE2FG"
+        val out1 = Kdf.hmacSha512(Base32Crockford.decode(key1), 
Base32Crockford.decode(message1)) { crypto.sha512(it) }
+        assertTrue(Base32Crockford.decode(key1).size < HMAC_SHA512_BLOCK_SIZE)
+        assertEquals(expectedOut1, Base32Crockford.encode(out1))
+
+        // key bigger than block size
+        val key2 =
+            
"AHM6JWS089GQ6S9K68G7CRBJD5GPWX10EXGQ6833E9JP2X35CGG64Y908HQQASVCC5SJ0GVJDXHPPSKFE9J2W829EGG6AY33DHTP8SBK414JR82C5GG4Y83MDWG62XKFD5J20RVFDSK7AWV9DXQ20XV9EHM20S39CXMQ8WS0C5Q6882N41T6Y83GE9JQCSBEEGG62RV3D5J6AVKMC5P20VV2EDHPAVK9EHWJW"
+        val message2 = "89P62RVB4166JXK5ECG4TRBMEHJQ488"
+        val expectedOut2 =
+            
"0N9GF90FES6HXS344WB396CF78SQDRZ7CS9MRAVCVZPJ3PJ97HYSB5A3C7RCXTTYYSS4CV11X3DANKQD71YMTA46R3NS1X8A99YVAE0"
+        val out2 = Kdf.hmacSha512(Base32Crockford.decode(key2), 
Base32Crockford.decode(message2)) { crypto.sha512(it) }
+        assertTrue(Base32Crockford.decode(key2).size > HMAC_SHA512_BLOCK_SIZE)
+        assertEquals(expectedOut2, Base32Crockford.encode(out2))
+
+        // key exactly block size
+        val key3 =
+            
"AHM6JWS0ESJQ4Y90EDJP6WK5EGG6PSBS41MQ6835F1GP6X3CF4G62WS0DHQPWSS0C5SJ0X38CMG64V3FCDNJ0WV9F9JJ0VV641T6GS90916M2GS0AD44281N64S20RBCCXQQ4TBMD1PJ0XV8D5HPG839ECG32CHR41H7JX35ECG62VK441GJ0V3FEGG6YSH0EHJQGX1E5RQ2W"
+        val message3 = "89P62RVB4166JXK5ECG4TRBMEHJQ488"
+        val expectedOut3 =
+            
"A3ZAKHA0EADSBP832KBCPDNVB4GP876PMQEEB2F55ERTQFQ7KMYNTJ3SEWJEQRNH3EV2H0HSA740JE4JQH7GM8FC708KHF4A5FG9QNG"
+        val out3 = Kdf.hmacSha512(Base32Crockford.decode(key3), 
Base32Crockford.decode(message3)) { crypto.sha512(it) }
+        assertEquals(HMAC_SHA512_BLOCK_SIZE, Base32Crockford.decode(key3).size)
+        assertEquals(expectedOut3, Base32Crockford.encode(out3))
+    }
+
+    @Test
+    fun testRfc4231() {
+        val key1 = ByteArray(20) { 0x0b }
+        val data1 = "91MJ0N38CNS6A"
+        assertEquals(
+            "P0T4RRERVCW56Q58NZ7AY2ZH5E41VGG0S61KV9S6X4VPRBHJSZVG",
+            Base32Crockford.encode(Kdf.hmacSha256(key1, 
Base32Crockford.decode(data1)) { crypto.sha256(it) })
+        )
+        assertEquals(
+            
"GYN7SQN5XXGSTKZGPGJ1M7BCP0HQKX72SS7C4Y3TT2SGAHF1FKFDNA1KPZBBH9R20E5JEKNEMFTE9FMXJ57EPRFHE0Q6JV107896GN0",
+            Base32Crockford.encode(Kdf.hmacSha512(key1, 
Base32Crockford.decode(data1)) { crypto.sha512(it) })
+        )
+
+        val key2 = Base32Crockford.decode("99JPCS8")
+        val data2 = "EXM62X10CHQJ0YB141VP2VKM41K6YWH0DSQQ8T39DSKKY"
+        assertEquals(
+            "BFEC2HNZC1TMWTG44GK0H5BNRXD00FR8KMKKK0WXXHCBJS7C711G",
+            Base32Crockford.encode(Kdf.hmacSha256(key2, 
Base32Crockford.decode(data2)) { crypto.sha256(it) })
+        )
+        assertEquals(
+            
"2S5QMYZWZ0CY5RWNZFKKPNQ0ME3VTS125T1HZNGG4W6DFTH50NA9EP5ZEQ05N6AADM1MYSFRY3KFVJQAP6HMTJKB9DHPW1RA72YEEDR",
+            Base32Crockford.encode(Kdf.hmacSha512(key2, 
Base32Crockford.decode(data2)) { crypto.sha512(it) })
+        )
+
+        val key3 = ByteArray(20) { 0xaa.toByte() }
+        val data3 = 
"VQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEXVQEX"
+        assertEquals(
+            "EWZAJ7HPG074D1ADQ3NX14C1MWMNJ2CB7VWC28PSCDAH9KPNCQZ0",
+            Base32Crockford.encode(Kdf.hmacSha256(key3, 
Base32Crockford.decode(data3)) { crypto.sha256(it) })
+        )
+        assertEquals(
+            
"Z9SV024XATH89VXGY1TPS28BX6RVBPYXHVM1MDJNZ0Z37CH7KMWVYFM4G9WTE8P80TT8B93YCZ40FEA6MCVVXT4M4ST2F22SW4S95YR",
+            Base32Crockford.encode(Kdf.hmacSha512(key3, 
Base32Crockford.decode(data3)) { crypto.sha512(it) })
+        )
+
+        val key4 = 
Base32Crockford.decode("041061050R3GG28A1C60T3GF208H44RM2MB1E60S")
+        val data4 = 
"SQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKEDSQ6WVKED"
+        assertEquals(
+            "G9ARME4T8GY0X96CG6C9KWG87A2Z1YN3WNWFG1VT5RZZ8SS9CSDG",
+            Base32Crockford.encode(Kdf.hmacSha256(key4, 
Base32Crockford.decode(data4)) { crypto.sha256(it) })
+        )
+        assertEquals(
+            
"P2X4CNHQ8P66K475N32ZC7AAYZJQDPBZZ55REBF7DY050DGYWFDTJ755R4DA4QNMTSWJEQ65F20679FHJX0H432F5QHAVTZB22H9HQ8",
+            Base32Crockford.encode(Kdf.hmacSha512(key4, 
Base32Crockford.decode(data4)) { crypto.sha512(it) })
+        )
+
+        val key5 = ByteArray(20) { 0x0c.toByte() }
+        val data5 = "AHJQ6X10AXMQ8T10AHS7AVK3C5T6JVVE"
+        assertEquals(
+            "MEV1CX3K207E0VGCF5P2JNAN5C",
+            Base32Crockford.encode(Kdf.hmacSha256(key5, 
Base32Crockford.decode(data5)) { crypto.sha256(it) }
+                .copyOfRange(0, 16))
+        )
+        assertEquals(
+            "85FTTRKHB05567A1F6Y8J7C7MR",
+            Base32Crockford.encode(Kdf.hmacSha512(key5, 
Base32Crockford.decode(data5)) { crypto.sha512(it) }
+                .copyOfRange(0, 16))
+        )
+
+        val key6 = ByteArray(131) { 0xaa.toByte() }
+        val data6 = 
"AHJQ6X10ANSPJVK741662WK7CNS20N38C5Q20GKCDXHPPBAKD5X6A82BCNWJ0B9091GQ6T109DJQJ826D5S76X0"
+        assertEquals(
+            "C3J32P8YW2V7Y3CA4TNCQXDQFY70QHH16WMCA5058R20Y3Q3FXA0",
+            Base32Crockford.encode(Kdf.hmacSha256(key6, 
Base32Crockford.decode(data6)) { crypto.sha256(it) })
+        )
+        assertEquals(
+            
"G2S44RY7R6HYQDRMJF0XTYZ8PJDMDMFM3D5EXG8J3C0KF0ZRYD96PNPG6ZG5Y9CRQM7X48AXD8F555F69XSZCFRAXJ5S2PMRBNW6B60",
+            Base32Crockford.encode(Kdf.hmacSha512(key6, 
Base32Crockford.decode(data6)) { crypto.sha512(it) })
+        )
+
+        val key7 = ByteArray(131) { 0xaa.toByte() }
+        val data7 =
+            
"AHM6JWS0D5SJ0R90EHJQ6X10ENSPJVK741GJ0V31E9KPAWH0EHM62VH0C9P6YRVB5NSPJYK541NPAY90C5Q6883141P62WK7CNS20X38C5Q20RKCDXHPPBBKD5X6A834C5T62BH0AHM6A83BCNWJ0VK5CNJ7683MDWG64S90D1GQ6T35CGG64SB6DXS6A832CNMPWSS0ENSPAS10C9WJ0X38CMG4GKA18CG62V37DXS6JX38DMQ0"
+        assertEquals(
+            "KC4ZZ9RVJGQWP9V3BYYDBC798JZXRRV49W3H74WAFX8N6Q1T6QH0",
+            Base32Crockford.encode(Kdf.hmacSha256(key7, 
Base32Crockford.decode(data7)) { crypto.sha256(it) })
+        )
+        assertEquals(
+            
"WDXPMXTXS1YVN96ZN7WPWQHZZQFBTWFRGSS8K1JXYPHJT86DS52BC0HCNGY4K0NH1NFEPNE3WKF1A4T6EVXPVR24C1JWJX20ZA66MP0",
+            Base32Crockford.encode(Kdf.hmacSha512(key7, 
Base32Crockford.decode(data7)) { crypto.sha512(it) })
+        )
+    }
+
+    @Test
+    fun testKdf() {
+        val salt = "94KPT83PCNS7J83KC5P78Y8"
+        val ikm = "94KPT83MD1JJ0WV5CDS6AX10D5Q70XBM41NPAY90DNGQ8SBJD5GPR"
+        val ctx = 
"94KPT83141HPYVKMCNW78833D1TPWTSC41GPRWVF41NPWVVQDRG62WS04XMPWSKF4WG6JVH0EHM6A82J8S1G"
+        val expectedOut =
+            
"GTMR4QT05Z9WF5HKVG0WK9RPXGHSMHJNW377G9GJXCA8B0FEKPF4D27RJMSJZYWSQNTBJ5EYVV7ZW18B48Z0JVJJ80RHB706Y96Q358"
+        val out =
+            crypto.kdf(64, Base32Crockford.decode(ikm), 
Base32Crockford.decode(salt), Base32Crockford.decode(ctx))
+        assertEquals(expectedOut, Base32Crockford.encode(out))
+
+        val salt2 = "94KPT83141V6AWKS41SP2V3MF4G76VK1CDNG"
+        val ikm2 =
+            
"94KPT83MD1JJ0WV5CDS6AX10D5Q70XBM41NPAY90DNGQ8SBJD5GPR83MD1GQ8838C5SJ0RK5CNQ20RV8C5Q6ESB441K6YWH08X75A82MC5P6AWG"
+        val ctx2 =
+            
"94KPT83141HPYVKMCNW78833D1TPWTSC41GPRWVF41NPWVVQDRG62WS04XMPWSKF4WG6JVH0EHM6A82J8S1J0WVF41VPGY90DSQQ8833C5P6R83DCMG6JVK6DWZG"
+        val expectedOut2 =
+            
"C6TXT6GMVZ8JNWZF27SPD6939FK035Y3FTWXYT0W4EZX9FNJ5KH24MQSK9D0MW3G7T1BBZ4ERVQC1RE24BNDPJQ68ZRQ8S3XN4GNHC8"
+        val out2 =
+            crypto.kdf(64, Base32Crockford.decode(ikm2), 
Base32Crockford.decode(salt2), Base32Crockford.decode(ctx2))
+        assertEquals(expectedOut2, Base32Crockford.encode(out2))
+
+        val salt3 = "C4G76RBCEG"
+        val ikm3 = "ESJQ4Y90EDJP6WK5EGG6PSBS"
+        val ctx3 = "D5Q6CVR"
+        val expectedOut3 = 
"ZYMG67TD51XYS0ZM3QZKH8HF8FW7CTVT91NAWD0PGZM2QGTNYKMXAA4ZJBH5V633TJW9E124CRYEY"
+        val out3 =
+            crypto.kdf(48, Base32Crockford.decode(ikm3), 
Base32Crockford.decode(salt3), Base32Crockford.decode(ctx3))
+        assertEquals(expectedOut3, Base32Crockford.encode(out3))
+
+        val salt4 = "84G76XV5CNT20WV1DHT20EH9"
+        val ikm4 = "9NWJ0SBREHS6AVB5DHWJ0WV5CDS6AX10DDJQJ"
+        val ctx4 = "CDQPWX35F1T20TBECSQKY88"
+        val expectedOut4 = 
"YM46SN0HYJ0RNGY1V5S7WY1SD2RQ1Y17G5Z37JD46E2Z8KY11PH5EV772RQ5NQ9SBMGG"
+        val out4 =
+            crypto.kdf(42, Base32Crockford.decode(ikm4), 
Base32Crockford.decode(salt4), Base32Crockford.decode(ctx4))
+        assertEquals(expectedOut4, Base32Crockford.encode(out4))
+    }
+
+}
diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha256Test.kt 
b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha256Test.kt
new file mode 100644
index 0000000..87b6f75
--- /dev/null
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha256Test.kt
@@ -0,0 +1,54 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Base32Crockford
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+@kotlin.ExperimentalStdlibApi
+class Sha256Test {
+
+    private val crypto = CryptoFactory.getCrypto()
+
+    @Test
+    fun testAbc() {
+        assertEquals(
+            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+            crypto.sha256("abc".encodeToByteArray()).toHexString()
+        )
+    }
+
+    @Test
+    fun testEmptyString() {
+        assertEquals(
+            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+            crypto.sha256("".encodeToByteArray()).toHexString()
+        )
+    }
+
+    @Test
+    fun testAbc448bits() {
+        assertEquals(
+            "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
+            
crypto.sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".encodeToByteArray()).toHexString()
+        )
+    }
+
+    @Test
+    fun testAbc896bits() {
+        assertEquals(
+            "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1",
+            
crypto.sha256("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".encodeToByteArray())
+                .toHexString()
+        )
+    }
+
+    @Test
+    fun testAMillionAs() {
+        val input = ByteArray(1_000_000) { 0x61 }
+        assertEquals(
+            "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0",
+            crypto.sha256(input).toHexString()
+        )
+    }
+
+}
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 e36b833..2904168 100644
--- a/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -7,7 +7,11 @@ internal actual object CryptoFactory {
     internal actual fun getCrypto(): Crypto = CryptoJsImpl
 }
 
-internal object CryptoJsImpl : Crypto {
+internal object CryptoJsImpl : CryptoImpl() {
+
+    override fun sha256(input: ByteArray): ByteArray {
+        return sha256(input.toUint8Array()).toByteArray()
+    }
 
     override fun sha512(input: ByteArray): ByteArray {
         return nacl.hash(input.toUint8Array()).toByteArray()
@@ -113,3 +117,7 @@ private external class ed2curve {
         fun convertPublicKey(pk: Uint8Array): Uint8Array?
     }
 }
+
+@JsModule("fast-sha256")
+@JsNonModule
+private external fun sha256(message: Uint8Array): 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 f5eca15..11c63e7 100644
--- a/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -11,7 +11,14 @@ internal actual object CryptoFactory {
 }
 
 @ExperimentalUnsignedTypes
-internal object CryptoNativeImpl : Crypto {
+internal object CryptoNativeImpl : CryptoImpl() {
+
+    override fun sha256(input: ByteArray): ByteArray {
+        val output = ByteArray(crypto_hash_sha256_bytes().toInt())
+        val cInput = if (input.isEmpty()) null else input.toCValuesRef()
+        crypto_hash_sha256(output.toCValuesRef(), cInput, input.size.toULong())
+        return output
+    }
 
     override fun sha512(input: ByteArray): ByteArray {
         val output = ByteArray(crypto_hash_sha512_bytes().toInt())

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]