/* * 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 } }