Caches shared key for performance.

This commit is contained in:
Vitor Pamplona
2023-08-18 10:55:28 -04:00
parent 2f741779fa
commit 41b93e3860
2 changed files with 151 additions and 10 deletions

View File

@@ -0,0 +1,102 @@
package com.vitorpamplona.amethyst.benchmark
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.Bech32
import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.encoders.bechToBytes
import com.vitorpamplona.quartz.encoders.toNpub
import com.vitorpamplona.quartz.events.Event
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class CryptoBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
@Test
fun getSharedKeyNip04() {
val keyPair1 = KeyPair()
val keyPair2 = KeyPair()
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.getSharedSecretNIP04(keyPair1.privKey!!, keyPair2.pubKey))
}
}
@Test
fun getSharedKeyNip44() {
val keyPair1 = KeyPair()
val keyPair2 = KeyPair()
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.getSharedSecretNIP24(keyPair1.privKey!!, keyPair2.pubKey))
}
}
@Test
fun computeSharedKeyNip04() {
val keyPair1 = KeyPair()
val keyPair2 = KeyPair()
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.computeSharedSecretNIP04(keyPair1.privKey!!, keyPair2.pubKey))
}
}
@Test
fun computeSharedKeyNip44() {
val keyPair1 = KeyPair()
val keyPair2 = KeyPair()
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.computeSharedSecretNIP24(keyPair1.privKey!!, keyPair2.pubKey))
}
}
@Test
fun random() {
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.random(1000))
}
}
@Test
fun sha256() {
val keyPair = KeyPair()
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.sha256(keyPair.pubKey))
}
}
@Test
fun sign() {
val keyPair = KeyPair()
val msg = CryptoUtils.sha256(CryptoUtils.random(1000))
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.sign(msg, keyPair.privKey!!))
}
}
@Test
fun verify() {
val keyPair = KeyPair()
val msg = CryptoUtils.sha256(CryptoUtils.random(1000))
val signature = CryptoUtils.sign(msg, keyPair.privKey!!)
benchmarkRule.measureRepeated {
assertNotNull(CryptoUtils.verifySignature(signature, msg, keyPair.pubKey))
}
}
}

View File

@@ -1,10 +1,11 @@
package com.vitorpamplona.quartz.crypto
import android.util.Log
import android.util.LruCache
import com.goterl.lazysodium.SodiumAndroid
import com.goterl.lazysodium.utils.Key
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.events.Event
import fr.acinq.secp256k1.Secp256k1
import java.security.MessageDigest
import java.security.SecureRandom
@@ -15,6 +16,9 @@ import javax.crypto.spec.SecretKeySpec
object CryptoUtils {
private val sharedKeyCache04 = LruCache<Int, ByteArray>(200)
private val sharedKeyCache24 = LruCache<Int, ByteArray>(200)
private val secp256k1 = Secp256k1.get()
private val libSodium = SodiumAndroid()
private val random = SecureRandom()
@@ -27,8 +31,10 @@ object CryptoUtils {
/**
* Provides a 32B "private key" aka random number
*/
fun privkeyCreate(): ByteArray {
val bytes = ByteArray(32)
fun privkeyCreate() = random(32)
fun random(size: Int): ByteArray {
val bytes = ByteArray(size)
random.nextBytes(bytes)
return bytes
}
@@ -52,12 +58,6 @@ object CryptoUtils {
return MessageDigest.getInstance("SHA-256").digest(data)
}
/**
* @return 32B shared secret
*/
fun getSharedSecretNIP04(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33)
fun encryptNIP04(msg: String, privateKey: ByteArray, pubKey: ByteArray): String {
val info = encryptNIP04(msg, getSharedSecretNIP04(privateKey, pubKey))
val encryptionInfo = EncryptedInfoString(
@@ -150,7 +150,39 @@ object CryptoUtils {
/**
* @return 32B shared secret
*/
fun getSharedSecretNIP24(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
fun getSharedSecretNIP04(privateKey: ByteArray, pubKey: ByteArray): ByteArray {
val hash = combinedHashCode(privateKey, pubKey)
val preComputed = sharedKeyCache04[hash]
if (preComputed != null) return preComputed
val computed = computeSharedSecretNIP04(privateKey, pubKey)
sharedKeyCache04.put(hash, computed)
return computed
}
/**
* @return 32B shared secret
*/
fun computeSharedSecretNIP04(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33)
/**
* @return 32B shared secret
*/
fun getSharedSecretNIP24(privateKey: ByteArray, pubKey: ByteArray): ByteArray {
val hash = combinedHashCode(privateKey, pubKey)
val preComputed = sharedKeyCache24[hash]
if (preComputed != null) return preComputed
val computed = computeSharedSecretNIP24(privateKey, pubKey)
sharedKeyCache24.put(hash, computed)
return computed
}
/**
* @return 32B shared secret
*/
fun computeSharedSecretNIP24(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
sha256(secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33))
}
@@ -213,6 +245,13 @@ fun decodeJackson(json: String): EncryptedInfo {
)
}
fun combinedHashCode(a: ByteArray, b: ByteArray): Int {
var result = 1
for (element in a) result = 31 * result + element
for (element in b) result = 31 * result + element
return result
}
/*
OLD Versions used for the Benchmark