gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-kotlin] branch master updated: Implement RSA blinding (onl


From: gnunet
Subject: [taler-wallet-kotlin] branch master updated: Implement RSA blinding (only JVM)
Date: Fri, 05 Jun 2020 20:54:43 +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 f25a15c  Implement RSA blinding (only JVM)
f25a15c is described below

commit f25a15c55f4b8cb1bc97aaff68d7468459377934
Author: Torsten Grote <t@grobox.de>
AuthorDate: Fri Jun 5 15:54:20 2020 -0300

    Implement RSA blinding (only JVM)
---
 build.gradle                                       |   5 +
 settings.gradle                                    |   1 -
 .../taler/wallet/kotlin/crypto/CryptoFactory.kt    |   4 +
 .../net/taler/wallet/kotlin/crypto/RsaBlinding.kt  | 103 +++++++++++++++++++++
 .../taler/wallet/kotlin/crypto/RsaBlindingTest.kt  |  29 ++++++
 .../net/taler/wallet/kotlin/crypto/Crypto.kt       |   1 +
 .../taler/wallet/kotlin/crypto/CryptoFactory.kt    |   4 +
 .../taler/wallet/kotlin/crypto/CryptoFactory.kt    |  24 ++++-
 8 files changed, 167 insertions(+), 4 deletions(-)

diff --git a/build.gradle b/build.gradle
index ab969f5..727b42c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -31,6 +31,11 @@ kotlin {
         }
     }
     sourceSets {
+        all {
+            languageSettings {
+                useExperimentalAnnotation('kotlin.ExperimentalStdlibApi')
+            }
+        }
         commonMain {
             dependencies {
                 implementation kotlin('stdlib-common')
diff --git a/settings.gradle b/settings.gradle
index b10916d..8a34848 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,2 @@
 rootProject.name = 'wallet-kotlin'
 
-enableFeaturePreview('GRADLE_METADATA')
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 d41b615..721024b 100644
--- a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -72,4 +72,8 @@ internal object CryptoJvmImpl : CryptoImpl() {
         return sha512(x)
     }
 
+    override fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: 
ByteArray): ByteArray {
+        return RsaBlinding.rsaBlind(hm, bks, rsaPubEnc)
+    }
+
 }
diff --git 
a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt 
b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt
new file mode 100644
index 0000000..6877df8
--- /dev/null
+++ b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/RsaBlinding.kt
@@ -0,0 +1,103 @@
+package net.taler.wallet.kotlin.crypto
+
+import java.math.BigInteger
+import kotlin.math.ceil
+import kotlin.math.floor
+
+@OptIn(ExperimentalStdlibApi::class)
+internal object RsaBlinding {
+
+    fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: ByteArray): 
ByteArray {
+        val rsaPub = rsaPubDecode(rsaPubEnc)
+        val data = rsaFullDomainHash(hm, rsaPub)
+        val r = rsaBlindingKeyDerive(rsaPub, bks)
+        val rE = r.modPow(rsaPub.e, rsaPub.n)
+        val bm = rE.multiply(data).mod(rsaPub.n)
+        return bm.toByteArrayWithoutSign()
+    }
+
+    private fun rsaBlindingKeyDerive(rsaPub: RsaPublicKey, bks: ByteArray): 
BigInteger {
+        val salt = "Blinding KDF extrator HMAC key".encodeToByteArray()
+        val info = "Blinding KDF".encodeToByteArray()
+        return kdfMod(rsaPub.n, bks, salt, info)
+    }
+
+    private fun rsaPubDecode(publicKey: ByteArray): RsaPublicKey {
+        val modulusLength = (publicKey[0].toInt() shl 8) or 
publicKey[1].toInt()
+        val exponentLength = (publicKey[2].toInt() shl 8) or 
publicKey[3].toInt()
+        if (4 + exponentLength + modulusLength != publicKey.size) {
+            throw Error("invalid RSA public key (format wrong)")
+        }
+        val modulus = publicKey.copyOfRange(4, 4 + modulusLength)
+        val exponent = publicKey.copyOfRange(
+            4 + modulusLength,
+            4 + modulusLength + exponentLength
+        )
+        return RsaPublicKey(BigInteger(1, modulus), BigInteger(1, exponent))
+    }
+
+    private fun rsaFullDomainHash(hm: ByteArray, rsaPublicKey: RsaPublicKey): 
BigInteger {
+        val info = "RSA-FDA FTpsW!".encodeToByteArray()
+        val salt = rsaPubEncode(rsaPublicKey)
+        val r = kdfMod(rsaPublicKey.n, hm, salt, info)
+        rsaGcdValidate(r, rsaPublicKey.n)
+        return r
+    }
+
+    private fun rsaPubEncode(rsaPublicKey: RsaPublicKey): ByteArray {
+        val mb = rsaPublicKey.n.toByteArrayWithoutSign()
+        val eb = rsaPublicKey.e.toByteArrayWithoutSign()
+        val out = ByteArray(4 + mb.size + eb.size)
+        out[0] = ((mb.size ushr 8) and 0xff).toByte()
+        out[1] = (mb.size and 0xff).toByte()
+        out[2] = ((eb.size ushr 8) and 0xff).toByte()
+        out[3] = (eb.size and 0xff).toByte()
+        mb.copyInto(out, destinationOffset = 4)
+        eb.copyInto(out, destinationOffset = 4 + mb.size)
+        return out
+    }
+
+    private fun kdfMod(n: BigInteger, ikm: ByteArray, salt: ByteArray, info: 
ByteArray): BigInteger {
+        val nBits = n.bitLength()
+        val bufLen = floor((nBits.toDouble() - 1) / 8 + 1).toInt()
+        val mask = (1 shl (8 - (bufLen * 8 - nBits))) - 1
+        var counter = 0
+        while (true) {
+            val ctx = ByteArray(info.size + 2)
+            info.copyInto(ctx)
+            ctx[ctx.size - 2] = ((counter ushr 8) and 0xff).toByte()
+            ctx[ctx.size - 1] = (counter and 0xff).toByte()
+            val buf = CryptoJvmImpl.kdf(bufLen, ikm, salt, ctx)
+            val arr = buf.copyOf()
+            arr[0] = (arr[0].toInt() and mask).toByte()
+            val r = BigInteger(1, arr)
+            if (r < n) return r
+            counter++
+        }
+    }
+
+    /**
+     * Test for malicious RSA key.
+     *
+     * Assuming n is an RSA modulous and r is generated using a call to
+     * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
+     * malicious RSA key designed to deanomize the user.
+     *
+     * @param r KDF result
+     * @param n RSA modulus of the public key
+     */
+    private fun rsaGcdValidate(r: BigInteger, n: BigInteger) {
+        if (r.gcd(n) != BigInteger.ONE) throw Error("malicious RSA public key")
+    }
+
+    // TODO check that this strips *only* the sign correctly
+    private fun BigInteger.toByteArrayWithoutSign(): ByteArray = 
this.toByteArray().let {
+        val byteLength = ceil(this.bitLength().toDouble() / 8).toInt()
+        val signBitPosition = ceil((this.bitLength() + 1).toDouble() / 
8).toInt()
+        val start = signBitPosition - byteLength
+        it.copyOfRange(start, it.size)  // stripping least significant byte 
(sign)
+    }
+
+}
+
+internal class RsaPublicKey(val n: BigInteger, val e: BigInteger)
diff --git 
a/src/androidTest/kotlin/net/taler/wallet/kotlin/crypto/RsaBlindingTest.kt 
b/src/androidTest/kotlin/net/taler/wallet/kotlin/crypto/RsaBlindingTest.kt
new file mode 100644
index 0000000..2c96797
--- /dev/null
+++ b/src/androidTest/kotlin/net/taler/wallet/kotlin/crypto/RsaBlindingTest.kt
@@ -0,0 +1,29 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Base32Crockford
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+// TODO move to commonTest once RsaBlinding is implemented everywhere
+class RsaBlindingTest {
+
+    private val crypto = CryptoFactory.getCrypto()
+
+    @Test
+    fun testBlinding() {
+        val messageHash =
+            
"TT1R28D79EJEJ9PC35AQS35CCG85DSXSZ508MV2HS2FN4ME6AHESZX5WP485R8A75KG53FN6F1YNW95008663TKAPWB81420VG17BY8"
+        val rsaPublicKey =
+            
"040000Y62RSDDKZXTE7GDVA302ZZR0DY224RSDT6WDWR1XGT8E3YG80XV6TMT3ZCNP8XC84W0N6MSZ0EF8S3YB1JJ2AXY9JQZW3MCA0CG38ER4YE2RY4Q2666DEZSNKT29V6CKZVCDHXSAKY8W6RPEKEQ5YSBYQK23MRK3CQTNNJXQFDKEMRHEC5Y6RDHAC5RJCV8JJ8BF18VPKZ2Q7BB14YN1HJ22H8EZGW0RDGG9YPEWA9183BHEQ651PP81J514TJ9K8DH23AJ50SZFNS429HQ390VRP5E4MQ7RK7ZJXXTSZAQSRTC0QF28P23PD37C17QFQB0BBC54MB8MDH7RW104STG6VN0J22P39JP4EXPVGK5D9AX5W869MDQ6SRD42ZYK5H20227Q8CCWSQ6C3132WP0F0H04002"
+        val bks = "7QD31RPJH0W306RJWBRG646Z2FTA1F89BKSXPDAG7YM0N5Z0B610"
+        val expectedBm =
+            
"GA8PC6YH9VF5MW6P2DKTV0W0ZTQ24DZ9EAN5QH3SQXRH7SCZHFMM21ZY05F0BS7MFW8TSEP4SEB280BYP5ACHNQWGE10PCXDDMK7ECXJDPHJ224JBCV4KYNWG6NBR3SC9HK8FXVFX55GFBJFNQHNZGEB8DB0KN9MSVYFDXN45KPMSNY03FVX0JZ0R3YG9XQ8XVGB5SYZCF0QSHWH61MT0Q10CZD2V114BT64D3GD86EJ5S9WBMYG51SDN5CSKEJ734YAJ4HCEWW0RDN8GXA9ZMA18SKVW8T3TTBCPJRF2Y77JGQ08GF35SYGA2HWFV1HGVS8RCTER6GB9SZHRG4T7919H9C1KFAP50G2KSV6X42D6KNJANNSGKQH649TJ00YJQXPHPNFBSS198RY2C243D4B4W"
+        val bm = crypto.rsaBlind(
+            Base32Crockford.decode(messageHash),
+            Base32Crockford.decode(bks),
+            Base32Crockford.decode(rsaPublicKey)
+        )
+        assertEquals(expectedBm, Base32Crockford.encode(bm))
+    }
+
+}
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 c5c0a0f..d2a77cc 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt
@@ -12,6 +12,7 @@ internal interface Crypto {
     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
+    fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: ByteArray): 
ByteArray
 }
 
 class EddsaKeyPair(val privateKey: ByteArray, val publicKey: ByteArray)
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 2904168..db502df 100644
--- a/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -61,6 +61,10 @@ internal object CryptoJsImpl : CryptoImpl() {
         return sha512(x)
     }
 
+    override fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: 
ByteArray): ByteArray {
+        TODO("Not yet implemented")
+    }
+
     private fun Uint8Array.toByteArray(): ByteArray {
         val result = ByteArray(this.length)
         for (i in 0 until this.length) result[i] = this[i]
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 11c63e7..152e55f 100644
--- a/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -3,14 +3,28 @@ package net.taler.wallet.kotlin.crypto
 import kotlinx.cinterop.CValuesRef
 import kotlinx.cinterop.UByteVar
 import kotlinx.cinterop.refTo
-import org.libsodium.*
+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_scalarmult
+import org.libsodium.crypto_scalarmult_BYTES
+import org.libsodium.crypto_scalarmult_base
+import org.libsodium.crypto_scalarmult_curve25519_BYTES
+import org.libsodium.crypto_sign_BYTES
+import org.libsodium.crypto_sign_PUBLICKEYBYTES
+import org.libsodium.crypto_sign_SECRETKEYBYTES
+import org.libsodium.crypto_sign_detached
+import org.libsodium.crypto_sign_ed25519_pk_to_curve25519
+import org.libsodium.crypto_sign_seed_keypair
+import org.libsodium.crypto_sign_verify_detached
+import org.libsodium.randombytes
 
 internal actual object CryptoFactory {
-    @ExperimentalUnsignedTypes
     internal actual fun getCrypto(): Crypto = CryptoNativeImpl
 }
 
-@ExperimentalUnsignedTypes
+@OptIn(ExperimentalUnsignedTypes::class)
 internal object CryptoNativeImpl : CryptoImpl() {
 
     override fun sha256(input: ByteArray): ByteArray {
@@ -85,6 +99,10 @@ internal object CryptoNativeImpl : CryptoImpl() {
         return sha512(sharedKey)
     }
 
+    override fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: 
ByteArray): ByteArray {
+        TODO("Not yet implemented")
+    }
+
     private fun ByteArray.toCValuesRef(): CValuesRef<UByteVar> {
         @Suppress("UNCHECKED_CAST")
         return this.refTo(0) as CValuesRef<UByteVar>

-- 
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]