summaryrefslogtreecommitdiff
path: root/bank
diff options
context:
space:
mode:
authorAntoine A <>2024-03-06 09:55:03 +0100
committerAntoine A <>2024-03-06 09:55:03 +0100
commit9af4fd9bcb338f20e75254f1aaad9ee7b1c0df47 (patch)
tree871fac85827e18e240e38dc3bb15b57a0ba3fda0 /bank
parent73c011ab2f5679bd1fb966318c53790097241540 (diff)
downloadlibeufin-9af4fd9bcb338f20e75254f1aaad9ee7b1c0df47.tar.gz
libeufin-9af4fd9bcb338f20e75254f1aaad9ee7b1c0df47.tar.bz2
libeufin-9af4fd9bcb338f20e75254f1aaad9ee7b1c0df47.zip
Improve password crypto and TAN documentation
Diffstat (limited to 'bank')
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt4
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt (renamed from bank/src/main/kotlin/tech/libeufin/bank/Tan.kt)20
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt5
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt9
4 files changed, 27 insertions, 11 deletions
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
index 2bda3a43..4b6a6e53 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -318,7 +318,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) {
requireAdmin = !ctx.allowAccountDeletion
) {
delete("/accounts/{USERNAME}") {
- val challenge = call.challenge(db, Operation.account_delete)
+ val challenge = call.checkChallenge(db, Operation.account_delete)
// Not deleting reserved names.
if (RESERVED_ACCOUNTS.contains(username))
@@ -511,7 +511,7 @@ private fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankConfig) {
}
post("/accounts/{USERNAME}/withdrawals/{withdrawal_id}/confirm") {
val id = call.uuidParameter("withdrawal_id")
- val challenge = call.challenge(db, Operation.withdrawal)
+ val challenge = call.checkChallenge(db, Operation.withdrawal)
when (db.withdrawal.confirm(username, id, Instant.now(), challenge != null)) {
WithdrawalConfirmationResult.UnknownOperation -> throw notFound(
"Withdrawal operation $id not found",
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Tan.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt
index eadddc29..f0b346ca 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Tan.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/auth/Tan.kt
@@ -1,6 +1,6 @@
/*
* This file is part of LibEuFin.
- * Copyright (C) 2023 Stanisci and Dold.
+ * Copyright (C) 2023-2024 Stanisci and Dold.
* LibEuFin is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -30,7 +30,13 @@ import java.security.SecureRandom
import java.text.DecimalFormat
import java.time.Instant
-
+/**
+ * Generate a TAN challenge for an [op] request with [body] and
+ * respond to the HTTP request with a TAN challenge.
+ *
+ * If [channel] and [info] are present, they will be used
+ * to send the TAN code, otherwise defaults will be used.
+ */
suspend inline fun <reified B> ApplicationCall.respondChallenge(
db: Database,
op: Operation,
@@ -57,6 +63,10 @@ suspend inline fun <reified B> ApplicationCall.respondChallenge(
)
}
+/**
+ * Retrieve a confirmed challenge and its body for [op] from the database
+ * if the challenge header is defined, otherwise extract the HTTP body.
+ */
suspend inline fun <reified B> ApplicationCall.receiveChallenge(
db: Database,
op: Operation
@@ -70,7 +80,10 @@ suspend inline fun <reified B> ApplicationCall.receiveChallenge(
}
}
-suspend fun ApplicationCall.challenge(
+/**
+ * Retrieve a confirmed challenge body for [op] if the challenge header is defined
+ */
+suspend fun ApplicationCall.checkChallenge(
db: Database,
op: Operation
): Challenge? {
@@ -86,6 +99,7 @@ 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 code = CODE_FORMAT.format(rand)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt
index c70627e7..7e8f4aac 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt
@@ -28,6 +28,7 @@ import io.ktor.util.pipeline.*
import tech.libeufin.bank.*
import tech.libeufin.bank.db.Database
import tech.libeufin.common.*
+import tech.libeufin.common.crypto.*
import java.time.Instant
/** Used to store if the currently authenticated user is admin */
@@ -134,13 +135,13 @@ private suspend fun ApplicationCall.authenticateBankRequest(db: Database, requir
* Returns the authenticated customer login
*/
private suspend fun doBasicAuth(db: Database, encoded: String): String {
- val decoded = String(base64ToBytes(encoded), Charsets.UTF_8)
+ val decoded = String(encoded.decodeBase64(), Charsets.UTF_8)
val (login, plainPassword) = decoded.splitOnce(":") ?: throw badRequest(
"Malformed Basic auth credentials found in the Authorization header",
TalerErrorCode.GENERIC_HTTP_HEADERS_MALFORMED
)
val hash = db.account.passwordHash(login) ?: throw unauthorized("Unknown account")
- if (!CryptoUtil.checkpw(plainPassword, hash)) throw unauthorized("Bad password")
+ if (!PwCrypto.checkpw(plainPassword, hash)) throw unauthorized("Bad password")
return login
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
index a23d34d5..c4c5054c 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
@@ -21,6 +21,7 @@ package tech.libeufin.bank.db
import tech.libeufin.bank.*
import tech.libeufin.common.*
+import tech.libeufin.common.crypto.*
import java.time.Instant
/** Data access logic for accounts */
@@ -78,7 +79,7 @@ class AccountDAO(private val db: Database) {
setString(9, tanChannel?.name)
setString(10, login)
oneOrNull {
- CryptoUtil.checkpw(password, it.getString(1)) && it.getBoolean(2)
+ PwCrypto.checkpw(password, it.getString(1)) && it.getBoolean(2)
}
}
@@ -118,7 +119,7 @@ class AccountDAO(private val db: Database) {
"""
).run {
setString(1, login)
- setString(2, CryptoUtil.hashpw(password))
+ setString(2, PwCrypto.hashpw(password))
setString(3, name)
setString(4, email)
setString(5, phone)
@@ -392,13 +393,13 @@ class AccountDAO(private val db: Database) {
}
if (tanRequired) {
AccountPatchAuthResult.TanRequired
- } else if (oldPw != null && !CryptoUtil.checkpw(oldPw, currentPwh)) {
+ } else if (oldPw != null && !PwCrypto.checkpw(oldPw, currentPwh)) {
AccountPatchAuthResult.OldPasswordMismatch
} else {
val stmt = conn.prepareStatement("""
UPDATE customers SET password_hash=? where login=?
""")
- stmt.setString(1, CryptoUtil.hashpw(newPw))
+ stmt.setString(1, PwCrypto.hashpw(newPw))
stmt.setString(2, login)
stmt.executeUpdateCheck()
AccountPatchAuthResult.Success