summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/dictionaries/user.xml2
-rw-r--r--.idea/jarRepositories.xml5
-rw-r--r--build.gradle7
-rw-r--r--src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt69
-rw-r--r--src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt17
-rw-r--r--src/commonTest/kotlin/net/taler/wallet/kotlin/Base32CrockfordTest.kt10
-rw-r--r--src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/EllipticCurveTest.kt96
-rw-r--r--src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt12
-rw-r--r--src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt104
-rw-r--r--src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt82
10 files changed, 364 insertions, 40 deletions
diff --git a/.idea/dictionaries/user.xml b/.idea/dictionaries/user.xml
index 55989af..75c46ba 100644
--- a/.idea/dictionaries/user.xml
+++ b/.idea/dictionaries/user.xml
@@ -1,6 +1,8 @@
<component name="ProjectDictionaryState">
<dictionary name="user">
<words>
+ <w>ecdhe</w>
+ <w>eddsa</w>
<w>nacl</w>
</words>
</dictionary>
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index fdc392f..b3e9cbd 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -16,5 +16,10 @@
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
+ <remote-repository>
+ <option name="id" value="BintrayJCenter" />
+ <option name="name" value="BintrayJCenter" />
+ <option name="url" value="https://jcenter.bintray.com/" />
+ </remote-repository>
</component>
</project> \ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7e06be1..b87f250 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,7 @@ plugins {
}
repositories {
mavenCentral()
+ jcenter()
}
group 'net.taler'
version '0.0.1'
@@ -44,7 +45,10 @@ kotlin {
androidMain {
dependencies {
implementation kotlin('stdlib-jdk8')
- implementation 'org.bouncycastle:bcprov-jdk15on:1.65.01'
+ // TODO Android
+// implementation "com.goterl.lazycode:lazysodium-android:4.1.1@aar"
+ implementation "com.goterl.lazycode:lazysodium-java:4.2.6"
+ implementation 'net.java.dev.jna:jna:5.5.0@aar'
}
}
androidTest {
@@ -57,6 +61,7 @@ kotlin {
dependencies {
implementation kotlin('stdlib-js')
implementation npm('tweetnacl', '1.0.3')
+ implementation npm('ed2curve', '0.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 509fabb..0bc933a 100644
--- a/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/androidMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -1,22 +1,69 @@
package net.taler.wallet.kotlin.crypto
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import java.security.MessageDigest
-import java.security.Security
+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.KeyExchange
+import com.goterl.lazycode.lazysodium.interfaces.Sign
+import com.goterl.lazycode.lazysodium.utils.Key
-actual object CryptoFactory {
- actual fun getCrypto(): Crypto = CryptoJvmImpl
+internal actual object CryptoFactory {
+ internal actual fun getCrypto(): Crypto = CryptoJvmImpl
}
-object CryptoJvmImpl : Crypto {
+internal object CryptoJvmImpl : Crypto {
- init {
- Security.addProvider(BouncyCastleProvider())
- }
+ private val sodium = LazySodiumJava(SodiumJava())
override fun sha512(input: ByteArray): ByteArray {
- val digest = MessageDigest.getInstance("SHA-512", "BC")
- return digest.digest(input)
+ val output = ByteArray(Hash.SHA512_BYTES)
+ sodium.cryptoHashSha512(output, input, input.size.toLong())
+ return output
+ }
+
+ override fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray {
+ return sodium.cryptoSignSeedKeypair(eddsaPrivateKey).publicKey.asBytes
+ }
+
+ override fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray {
+ return sodium.cryptoScalarMultBase(Key.fromBytes(ecdhePrivateKey)).asBytes
+ }
+
+ override fun createEddsaKeyPair(): EddsaKeyPair {
+ val privateKey = sodium.randomBytesBuf(KeyExchange.SEEDBYTES)
+ val publicKey = eddsaGetPublic(privateKey)
+ return EddsaKeyPair(privateKey, publicKey)
+ }
+
+ override fun createEcdheKeyPair(): EcdheKeyPair {
+ val privateKey = sodium.randomBytesBuf(KeyExchange.SEEDBYTES)
+ val publicKey = ecdheGetPublic(privateKey)
+ return EcdheKeyPair(privateKey, publicKey)
+ }
+
+ override fun eddsaSign(msg: ByteArray, eddsaPrivateKey: ByteArray): ByteArray {
+ val privateKey = sodium.cryptoSignSeedKeypair(eddsaPrivateKey).secretKey.asBytes
+ val signatureBytes = ByteArray(Sign.BYTES)
+ sodium.cryptoSignDetached(signatureBytes, msg, msg.size.toLong(), privateKey)
+ return signatureBytes
+ }
+
+ override fun eddsaVerify(msg: ByteArray, sig: ByteArray, eddsaPub: ByteArray): Boolean {
+ return sodium.cryptoSignVerifyDetached(sig, msg, msg.size, eddsaPub)
+ }
+
+ override fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: ByteArray): ByteArray {
+ val ph = sha512(eddsaPrivateKey)
+ val a = ph.copyOfRange(0, 32)
+ val x = sodium.cryptoScalarMult(Key.fromBytes(a), Key.fromBytes(ecdhePublicKey)).asBytes
+ return sha512(x)
+ }
+
+ override fun keyExchangeEcdheEddsa(ecdhePrivateKey: ByteArray, eddsaPublicKey: ByteArray): ByteArray {
+ val curve25519Pub = ByteArray(KeyExchange.PUBLICKEYBYTES)
+ sodium.convertPublicKeyEd25519ToCurve25519(curve25519Pub, eddsaPublicKey)
+ val x = sodium.cryptoScalarMult(Key.fromBytes(ecdhePrivateKey), Key.fromBytes(curve25519Pub)).asBytes
+ return sha512(x)
}
}
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 a5eeb69..3735c31 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Crypto.kt
@@ -1,11 +1,22 @@
package net.taler.wallet.kotlin.crypto
-interface Crypto {
+internal interface Crypto {
fun sha512(input: ByteArray): ByteArray
+ fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray
+ fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray
+ fun createEddsaKeyPair(): EddsaKeyPair
+ fun createEcdheKeyPair(): EcdheKeyPair
+ fun eddsaSign(msg: ByteArray, eddsaPrivateKey: ByteArray): ByteArray
+ fun eddsaVerify(msg: ByteArray, sig: ByteArray, eddsaPub: ByteArray): Boolean
+ fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: ByteArray): ByteArray
+ fun keyExchangeEcdheEddsa(ecdhePrivateKey: ByteArray, eddsaPublicKey: ByteArray): ByteArray
}
-expect object CryptoFactory {
- fun getCrypto(): Crypto
+class EddsaKeyPair(val privateKey: ByteArray, val publicKey: ByteArray)
+class EcdheKeyPair(val privateKey: ByteArray, val publicKey: ByteArray)
+
+internal expect object CryptoFactory {
+ internal fun getCrypto(): Crypto
}
private val hexArray = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/Base32CrockfordTest.kt b/src/commonTest/kotlin/net/taler/wallet/kotlin/Base32CrockfordTest.kt
index daa505a..4a52a47 100644
--- a/src/commonTest/kotlin/net/taler/wallet/kotlin/Base32CrockfordTest.kt
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/Base32CrockfordTest.kt
@@ -5,6 +5,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
+@ExperimentalStdlibApi
class Base32CrockfordTest {
private class TestVector(val value: ByteArray, val encoding: List<String>)
@@ -94,4 +95,13 @@ class Base32CrockfordTest {
assertTrue(bytes contentEquals Base32Crockford.decode("FVCK"))
}
+ @Test
+ fun testEncodingDecoding() {
+ val input = "Hello, World"
+ val encoded = Base32Crockford.encode(input.encodeToByteArray())
+ val decoded = Base32Crockford.decode(encoded)
+ val output = decoded.decodeToString()
+ assertEquals(input, output)
+ }
+
}
diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/EllipticCurveTest.kt b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/EllipticCurveTest.kt
new file mode 100644
index 0000000..c74d9ff
--- /dev/null
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/EllipticCurveTest.kt
@@ -0,0 +1,96 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Base32Crockford
+import kotlin.random.Random
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class EllipticCurveTest {
+
+ private val crypto = CryptoFactory.getCrypto()
+
+ @Test
+ fun testExchangeTvgEddsaKey() {
+ val pri = "9TM70AKDTS57AWY9JK2J4TMBTMW6K62WHHGZWYDG0VM5ABPZKD40"
+ val pub = "8GSJZ649T2PXMKZC01Y4ANNBE7MF14QVK9SQEC4E46ZHKCVG8AS0"
+ val pubBytes = crypto.eddsaGetPublic(Base32Crockford.decode(pri))
+ assertEquals(pub, Base32Crockford.encode(pubBytes))
+ }
+
+ @Test
+ fun testExchangeTvgEcdheKey() {
+ val pri = "X4T4N0M8PVQXQEBW2BA7049KFSM7J437NSDFC6GDNM3N5J9367A0"
+ val pub = "M997P494MS6A95G1P0QYWW2VNPSHSX5Q6JBY5B9YMNYWP0B50X3G"
+ val pubBytes = crypto.ecdheGetPublic(Base32Crockford.decode(pri))
+ assertEquals(pub, Base32Crockford.encode(pubBytes))
+ }
+
+ @Test
+ fun testCreateEddsaKeyPair() {
+ val pair1 = crypto.createEddsaKeyPair()
+ val pair2 = crypto.createEddsaKeyPair()
+ assertFalse(pair1.privateKey contentEquals pair2.privateKey)
+ assertFalse(pair1.publicKey contentEquals pair2.publicKey)
+ }
+
+ @Test
+ fun testCreateEcdheKeyPair() {
+ val pair1 = crypto.createEcdheKeyPair()
+ val pair2 = crypto.createEcdheKeyPair()
+ assertFalse(pair1.privateKey contentEquals pair2.privateKey)
+ assertFalse(pair1.publicKey contentEquals pair2.publicKey)
+ }
+
+ @Test
+ @ExperimentalStdlibApi
+ fun testEddsaSignAndVerify() {
+ val msg = "Hallo world!".encodeToByteArray()
+ val pri = "9TM70AKDTS57AWY9JK2J4TMBTMW6K62WHHGZWYDG0VM5ABPZKD40"
+ val expectedSig =
+ "Z6H76JXPJFP3JBGSF54XBF0BVXDJ0CJBK4YT9GVR1AT916ZD57KP53YZN5G67A4YN95WGMZKQW7744483P5JDF06B6S7TMK195QGP20"
+ val sig = crypto.eddsaSign(msg, Base32Crockford.decode(pri))
+ assertEquals(expectedSig, Base32Crockford.encode(sig))
+
+ val pub = crypto.eddsaGetPublic(Base32Crockford.decode(pri))
+ assertTrue(crypto.eddsaVerify(msg, sig, pub))
+
+ val wrongSig = Random.nextBytes(64)
+ assertFalse(crypto.eddsaVerify(msg, wrongSig, pub))
+
+ val wrongPub = Random.nextBytes(32)
+ assertFalse(crypto.eddsaVerify(msg, sig, wrongPub))
+
+ val wrongMsg = Random.nextBytes(16)
+ assertFalse(crypto.eddsaVerify(wrongMsg, sig, pub))
+ }
+
+ @Test
+ fun testExchangeTvgEddsaEcdh() {
+ val ecdhePrivateKey = "4AFZWMSGTVCHZPQ0R81NWXDCK4N58G7SDBBE5KXE080Y50370JJG"
+ val ecdhePublicKey = "FXFN5GPAFTKVPWJDPVXQ87167S8T82T5ZV8CDYC0NH2AE14X0M30"
+ val ecdhePublicKeyBytes = crypto.ecdheGetPublic(Base32Crockford.decode(ecdhePrivateKey))
+ assertEquals(ecdhePublicKey, Base32Crockford.encode(ecdhePublicKeyBytes))
+
+ val eddsaPrivateKey = "1KG54M8T3X8BSFSZXCR3SQBSR7Y9P53NX61M864S7TEVMJ2XVPF0"
+ val eddsaPublicKey = "7BXWKG6N224C57RTDV8XEAHR108HG78NMA995BE8QAT5GC1S7E80"
+ val eddsaPublicKeyBytes = crypto.eddsaGetPublic(Base32Crockford.decode(eddsaPrivateKey))
+ assertEquals(eddsaPublicKey, Base32Crockford.encode(eddsaPublicKeyBytes))
+
+ val keyMaterial =
+ "PKZ42Z56SVK2796HG1QYBRJ6ZQM2T9QGA3JA4AAZ8G7CWK9FPX175Q9JE5P0ZAX3HWWPHAQV4DPCK10R9X3SAXHRV0WF06BHEC2ZTKR"
+ val keyMaterial1Bytes = crypto.keyExchangeEddsaEcdhe(
+ Base32Crockford.decode(eddsaPrivateKey),
+ Base32Crockford.decode(ecdhePublicKey)
+ )
+ assertEquals(keyMaterial, Base32Crockford.encode(keyMaterial1Bytes))
+
+ val keyMaterial2Bytes = crypto.keyExchangeEcdheEddsa(
+ Base32Crockford.decode(ecdhePrivateKey),
+ Base32Crockford.decode(eddsaPublicKey)
+ )
+ assertEquals(keyMaterial, Base32Crockford.encode(keyMaterial2Bytes))
+ }
+
+}
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 f41080f..c401b73 100644
--- a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/Sha512Test.kt
@@ -1,5 +1,6 @@
package net.taler.wallet.kotlin.crypto
+import net.taler.wallet.kotlin.Base32Crockford
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -36,7 +37,8 @@ class Sha512Test {
fun testAbc896bits() {
assertEquals(
"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909",
- crypto.sha512("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".encodeToByteArray()).toHexString()
+ crypto.sha512("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu".encodeToByteArray())
+ .toHexString()
)
}
@@ -49,4 +51,12 @@ class Sha512Test {
)
}
+ @Test
+ fun testExchangeTvgHashCode() {
+ val input = "91JPRV3F5GG4EKJN41A62V35E8"
+ val output =
+ "CW96WR74JS8T53EC8GKSGD49QKH4ZNFTZXDAWMMV5GJ1E4BM6B8GPN5NVHDJ8ZVXNCW7Q4WBYCV61HCA3PZC2YJD850DT29RHHN7ESR"
+ assertEquals(output, Base32Crockford.encode(crypto.sha512(Base32Crockford.decode(input))))
+ }
+
}
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 e1786d9..e36b833 100644
--- a/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/jsMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -3,35 +3,113 @@ package net.taler.wallet.kotlin.crypto
import org.khronos.webgl.Uint8Array
import org.khronos.webgl.get
-actual object CryptoFactory {
- actual fun getCrypto(): Crypto = CryptoJsImpl
+internal actual object CryptoFactory {
+ internal actual fun getCrypto(): Crypto = CryptoJsImpl
}
-object CryptoJsImpl : Crypto {
+internal object CryptoJsImpl : Crypto {
override fun sha512(input: ByteArray): ByteArray {
return nacl.hash(input.toUint8Array()).toByteArray()
}
-}
+ override fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray {
+ val pair = nacl.sign.keyPair.fromSeed(eddsaPrivateKey.toUint8Array())
+ return pair.publicKey.toByteArray()
+ }
-private fun Uint8Array.toByteArray(): ByteArray {
- val result = ByteArray(this.length)
- for (i in 0 until this.length) {
- result[i] = this[i]
+ override fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray {
+ return nacl.scalarMult.base(ecdhePrivateKey.toUint8Array()).toByteArray()
+ }
+
+ override fun createEddsaKeyPair(): EddsaKeyPair {
+ val privateKey = nacl.randomBytes(32).toByteArray()
+ val publicKey = eddsaGetPublic(privateKey)
+ return EddsaKeyPair(privateKey, publicKey)
+ }
+
+ override fun createEcdheKeyPair(): EcdheKeyPair {
+ val privateKey = nacl.randomBytes(32).toByteArray()
+ val publicKey = ecdheGetPublic(privateKey)
+ return EcdheKeyPair(privateKey, publicKey)
+ }
+
+ override fun eddsaSign(msg: ByteArray, eddsaPrivateKey: ByteArray): ByteArray {
+ val privateKey = nacl.sign.keyPair.fromSeed(eddsaPrivateKey.toUint8Array()).secretKey
+ return nacl.sign.detached(msg.toUint8Array(), privateKey).toByteArray()
+ }
+
+ override fun eddsaVerify(msg: ByteArray, sig: ByteArray, eddsaPub: ByteArray): Boolean {
+ return nacl.sign.detached.verify(msg.toUint8Array(), sig.toUint8Array(), eddsaPub.toUint8Array())
+ }
+
+ override fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: ByteArray): ByteArray {
+ val ph = sha512(eddsaPrivateKey)
+ val a = ph.copyOfRange(0, 32)
+ val x = nacl.scalarMult(a.toUint8Array(), ecdhePublicKey.toUint8Array()).toByteArray()
+ return sha512(x)
+ }
+
+ override fun keyExchangeEcdheEddsa(ecdhePrivateKey: ByteArray, eddsaPublicKey: ByteArray): ByteArray {
+ val curve25519Pub =
+ ed2curve.convertPublicKey(eddsaPublicKey.toUint8Array()) ?: throw Error("invalid public key")
+ val x = nacl.scalarMult(ecdhePrivateKey.toUint8Array(), curve25519Pub).toByteArray()
+ return sha512(x)
+ }
+
+ private fun Uint8Array.toByteArray(): ByteArray {
+ val result = ByteArray(this.length)
+ for (i in 0 until this.length) result[i] = this[i]
+ return result
+ }
+
+ private fun ByteArray.toUint8Array(): Uint8Array {
+ return Uint8Array(this.toTypedArray())
}
- return result
-}
-private fun ByteArray.toUint8Array():Uint8Array {
- return Uint8Array(this.toTypedArray())
}
@Suppress("ClassName")
@JsModule("tweetnacl")
@JsNonModule
-external class nacl {
+private external class nacl {
+
companion object {
fun hash(input: Uint8Array): Uint8Array
+ fun scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array
+ fun randomBytes(n: Int): Uint8Array
+ }
+
+ class scalarMult {
+ companion object {
+ fun base(n: Uint8Array): Uint8Array
+ }
+ }
+
+ class sign {
+ 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
+ }
+ }
+ }
+}
+
+private class KeyPair(val publicKey: Uint8Array, @Suppress("unused") val secretKey: Uint8Array)
+
+@Suppress("ClassName")
+@JsModule("ed2curve")
+@JsNonModule
+private external class ed2curve {
+ companion object {
+ fun convertPublicKey(pk: 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 d1ec46a..f5eca15 100644
--- a/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
+++ b/src/linuxMain/kotlin/net/taler/wallet/kotlin/crypto/CryptoFactory.kt
@@ -1,26 +1,86 @@
package net.taler.wallet.kotlin.crypto
+import kotlinx.cinterop.CValuesRef
+import kotlinx.cinterop.UByteVar
+import kotlinx.cinterop.refTo
import org.libsodium.*
-import kotlinx.cinterop.*
-actual object CryptoFactory {
+internal actual object CryptoFactory {
@ExperimentalUnsignedTypes
- actual fun getCrypto(): Crypto = CryptoNativeImpl
+ internal actual fun getCrypto(): Crypto = CryptoNativeImpl
}
@ExperimentalUnsignedTypes
-object CryptoNativeImpl : Crypto {
+internal object CryptoNativeImpl : Crypto {
override fun sha512(input: ByteArray): ByteArray {
val output = ByteArray(crypto_hash_sha512_bytes().toInt())
- val cInput = if (input.isEmpty()) null else input.toUByteArray().refTo(0)
- val cInputSize = input.size.toULong()
- output.usePinned { pinned ->
- @Suppress("UNCHECKED_CAST")
- val cOutput = pinned.addressOf(0) as CValuesRef<UByteVar>
- crypto_hash_sha512(cOutput, cInput, cInputSize)
- }
+ val cInput = if (input.isEmpty()) null else input.toCValuesRef()
+ crypto_hash_sha512(output.toCValuesRef(), cInput, input.size.toULong())
return output
}
+ override fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray {
+ val publicKey = ByteArray(crypto_sign_PUBLICKEYBYTES.toInt())
+ val privateKey = ByteArray(crypto_sign_SECRETKEYBYTES.toInt())
+ crypto_sign_seed_keypair(publicKey.toCValuesRef(), privateKey.toCValuesRef(), eddsaPrivateKey.toCValuesRef())
+ return publicKey
+ }
+
+ override fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray {
+ val publicKey = ByteArray(crypto_scalarmult_BYTES.toInt())
+ crypto_scalarmult_base(publicKey.toCValuesRef(), ecdhePrivateKey.toCValuesRef())
+ return publicKey
+ }
+
+ override fun createEddsaKeyPair(): EddsaKeyPair {
+ val privateKey = ByteArray(crypto_sign_SECRETKEYBYTES.toInt())
+ randombytes(privateKey.toCValuesRef(), crypto_sign_SECRETKEYBYTES.toULong())
+ val publicKey = eddsaGetPublic(privateKey)
+ return EddsaKeyPair(privateKey, publicKey)
+ }
+
+ override fun createEcdheKeyPair(): EcdheKeyPair {
+ val privateKey = ByteArray(crypto_scalarmult_BYTES.toInt())
+ randombytes(privateKey.toCValuesRef(), crypto_scalarmult_BYTES.toULong())
+ val publicKey = ecdheGetPublic(privateKey)
+ return EcdheKeyPair(privateKey, publicKey)
+ }
+
+ override fun eddsaSign(msg: ByteArray, eddsaPrivateKey: ByteArray): ByteArray {
+ val publicKey = ByteArray(crypto_sign_PUBLICKEYBYTES.toInt())
+ val privateKey = ByteArray(crypto_sign_SECRETKEYBYTES.toInt())
+ 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())
+ 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
+ }
+
+ override fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: ByteArray): ByteArray {
+ val privateKey = sha512(eddsaPrivateKey).copyOfRange(0, 32)
+ val sharedKey = ByteArray(crypto_scalarmult_BYTES.toInt())
+ crypto_scalarmult(sharedKey.toCValuesRef(), privateKey.toCValuesRef(), ecdhePublicKey.toCValuesRef())
+ return sha512(sharedKey)
+ }
+
+ override fun keyExchangeEcdheEddsa(ecdhePrivateKey: ByteArray, eddsaPublicKey: ByteArray): ByteArray {
+ val curve25519Pub = ByteArray(crypto_scalarmult_curve25519_BYTES.toInt())
+ val cCurve25519Pub = curve25519Pub.toCValuesRef()
+ crypto_sign_ed25519_pk_to_curve25519(cCurve25519Pub, eddsaPublicKey.toCValuesRef())
+
+ val sharedKey = ByteArray(crypto_scalarmult_BYTES.toInt())
+ crypto_scalarmult(sharedKey.toCValuesRef(), ecdhePrivateKey.toCValuesRef(), cCurve25519Pub)
+ return sha512(sharedKey)
+ }
+
+ private fun ByteArray.toCValuesRef(): CValuesRef<UByteVar> {
+ @Suppress("UNCHECKED_CAST")
+ return this.refTo(0) as CValuesRef<UByteVar>
+ }
+
}