/*
* This file is part of GNU Taler
* (C) 2020 Taler Systems S.A.
*
* GNU Taler is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3, or (at your option) any later version.
*
* GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* GNU Taler; see the file COPYING. If not, see
*/
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
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 {
internal actual fun getCrypto(): Crypto = CryptoNativeImpl
}
@OptIn(ExperimentalUnsignedTypes::class)
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())
val cInput = if (input.isEmpty()) null else input.toCValuesRef()
crypto_hash_sha512(output.toCValuesRef(), cInput, input.size.toULong())
return output
}
override fun getHashSha512State(): HashSha512State {
return NativeHashSha512State()
}
override fun getRandomBytes(num: Int): ByteArray {
val bytes = ByteArray(num)
randombytes(bytes.toCValuesRef(), num.toULong())
return bytes
}
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)
}
override fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: ByteArray): ByteArray {
TODO("Not yet implemented")
}
override fun rsaUnblind(sig: ByteArray, rsaPubEnc: ByteArray, bks: ByteArray): ByteArray {
TODO("Not yet implemented")
}
override fun rsaVerify(hm: ByteArray, rsaSig: ByteArray, rsaPubEnc: ByteArray): Boolean {
TODO("Not yet implemented")
}
private class NativeHashSha512State : HashSha512State {
private val state = nativeHeap.alloc()
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 {
@Suppress("UNCHECKED_CAST")
return this.refTo(0) as CValuesRef
}
}