libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 4e594a248ea62129129c7aec693e39e27d394acd
parent eb459c842f6d9b8516974b6c0940ab07f7156a5e
Author: Antoine A <>
Date:   Tue,  8 Oct 2024 18:00:50 +0200

common: improve use of secure rng

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt | 2+-
Mbank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt | 5++---
Mbank/src/main/kotlin/tech/libeufin/bank/helpers.kt | 2+-
Mcommon/src/main/kotlin/TalerCommon.kt | 1+
Mcommon/src/main/kotlin/crypto/PwCrypto.kt | 8++------
Mcommon/src/main/kotlin/helpers.kt | 13+++++++++----
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt | 5+----
7 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt @@ -101,7 +101,7 @@ private fun Routing.coreBankTokenApi(db: Database, cfg: BankConfig) { TalerErrorCode.GENERIC_TOKEN_PERMISSION_INSUFFICIENT ) } - val token = Base32Crockford32B.rand() + val token = Base32Crockford32B.secureRand() val tokenDuration: Duration = req.duration?.d_us ?: TOKEN_DEFAULT_DURATION val creationTime = Instant.now() diff --git a/bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt @@ -28,7 +28,7 @@ import tech.libeufin.bank.* import tech.libeufin.bank.db.Database import tech.libeufin.bank.db.TanDAO.Challenge import tech.libeufin.common.X_CHALLENGE_ID -import java.security.SecureRandom +import tech.libeufin.common.SECURE_RNG import java.text.DecimalFormat import java.time.Instant @@ -106,11 +106,10 @@ suspend fun ApplicationCall.checkChallenge( object Tan { private val CODE_FORMAT = DecimalFormat("00000000") - private val SECURE_RNG = SecureRandom() /** Generate a secure random TAN code */ fun genCode(): String { - val rand = SECURE_RNG.nextInt(100000000) + val rand = SECURE_RNG.get().nextInt(100000000) val code = CODE_FORMAT.format(rand) return code } diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt @@ -86,7 +86,7 @@ fun ApplicationRequest.withdrawConfirmUrl(id: UUID) = url { suspend fun createAdminAccount(db: Database, cfg: BankConfig, pw: String? = null): AccountCreationResult { var pwStr = pw if (pwStr == null) { - val pwBuf = ByteArray(32).rand() + val pwBuf = ByteArray(32).secureRand() pwStr = String(pwBuf, Charsets.UTF_8) } diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt @@ -525,6 +525,7 @@ class Base32Crockford32B { companion object { fun rand(): Base32Crockford32B = Base32Crockford32B(ByteArray(32).rand()) + fun secureRand(): Base32Crockford32B = Base32Crockford32B(ByteArray(32).secureRand()) } } diff --git a/common/src/main/kotlin/crypto/PwCrypto.kt b/common/src/main/kotlin/crypto/PwCrypto.kt @@ -22,8 +22,7 @@ package tech.libeufin.common.crypto import kotlinx.serialization.Serializable import tech.libeufin.common.decodeBase64 import tech.libeufin.common.encodeBase64 -import tech.libeufin.common.rand -import java.security.SecureRandom +import tech.libeufin.common.secureRand data class PasswordHashCheck( val match: Boolean, @@ -39,7 +38,7 @@ sealed interface PwCrypto { fun hashpw(pw: String): String { when (this) { is Bcrypt -> { - val salt = ByteArray(16).rand(SECURE_RNG) + val salt = ByteArray(16).secureRand() val pwh = CryptoUtil.bcrypt(pw, salt, cost) return "bcrypt\$$cost\$${salt.encodeBase64()}\$${pwh.encodeBase64()}" } @@ -82,7 +81,4 @@ sealed interface PwCrypto { else -> throw Exception("unsupported hash algo: '$algo'") } } - companion object { - private val SECURE_RNG = SecureRandom() - } } \ No newline at end of file diff --git a/common/src/main/kotlin/helpers.kt b/common/src/main/kotlin/helpers.kt @@ -76,14 +76,19 @@ fun dateTimeToInstant(date: String): Instant = fun BigInteger.encodeHex(): String = this.toByteArray().encodeHex() fun BigInteger.encodeBase64(): String = this.toByteArray().encodeBase64() +/* ----- Random ----- */ + +/** Thread local cryptographically strong random number generator */ +val SECURE_RNG = ThreadLocal.withInitial { SecureRandom() } + /* ----- ByteArray ----- */ -fun ByteArray.rand(): ByteArray { - Random.nextBytes(this) +fun ByteArray.rand(rng: Random = Random): ByteArray { + rng.nextBytes(this) return this } -fun ByteArray.rand(rng: SecureRandom): ByteArray { - rng.nextBytes(this) +fun ByteArray.secureRand(): ByteArray { + SECURE_RNG.get().nextBytes(this) return this } fun ByteArray.encodeHex(): String = HexFormat.of().formatHex(this) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -34,7 +34,6 @@ import tech.libeufin.nexus.* import tech.libeufin.nexus.db.Database import java.io.InputStream import java.io.SequenceInputStream -import java.security.SecureRandom import java.security.interfaces.RSAPrivateCrtKey import java.time.Instant import java.util.* @@ -371,11 +370,9 @@ fun decryptAndDecompressPayload( }.inflate() } -private val SECURE_RNG = SecureRandom() - /** Generate a secure random nonce of [size] bytes */ fun getNonce(size: Int): ByteArray { - return ByteArray(size / 8).rand(SECURE_RNG) + return ByteArray(size / 8).secureRand() } private val EBICS_ID_ALPHABET = ('A'..'Z') + ('0'..'9')