diff options
author | Antoine A <> | 2023-10-04 11:20:07 +0000 |
---|---|---|
committer | Antoine A <> | 2023-10-04 11:20:07 +0000 |
commit | 377b22e474af8188b5a1e53e68c43f1920b3bcc9 (patch) | |
tree | 5381a4afbc59eb5c4543ef559ad4e898ea6a651b | |
parent | 05971692600e125018c1450be3558ff11562fbda (diff) | |
download | libeufin-377b22e474af8188b5a1e53e68c43f1920b3bcc9.tar.gz libeufin-377b22e474af8188b5a1e53e68c43f1920b3bcc9.tar.bz2 libeufin-377b22e474af8188b5a1e53e68c43f1920b3bcc9.zip |
Add Crockford32 validation to /taler-wire-gateway/admin/add-incoming
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt | 21 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt | 6 | ||||
-rw-r--r-- | bank/src/test/kotlin/TalerApiTest.kt | 37 | ||||
-rw-r--r-- | bank/src/test/kotlin/helpers.kt | 13 |
4 files changed, 54 insertions, 23 deletions
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt index 035cab5a..f5c4bc5c 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt @@ -30,11 +30,11 @@ import java.time.temporal.ChronoUnit import java.util.* /** - * 32-byte hash code. + * 32-byte Crockford's Base32 encoded data. */ @Serializable() @JvmInline -value class ShortHashCode(val encoded: String) { +value class Base32Crockford32B(val encoded: String) { init { val decoded = try { Base32Crockford.decode(encoded) @@ -52,11 +52,11 @@ value class ShortHashCode(val encoded: String) { } /** - * 64-byte hash code. + * 64-byte Crockford's Base32 encoded data. */ @Serializable() @JvmInline -value class HashCode(val encoded: String) { +value class Base32Crockford64B(val encoded: String) { init { val decoded = try { Base32Crockford.decode(encoded) @@ -73,6 +73,17 @@ value class HashCode(val encoded: String) { } } +/** 32-byte hash code. */ +typealias ShortHashCode = Base32Crockford32B; +/** 64-byte hash code. */ +typealias HashCode = Base32Crockford64B; +/** + * EdDSA and ECDHE public keys always point on Curve25519 + * and represented using the standard 256 bits Ed25519 compact format, + * converted to Crockford Base32. + */ +typealias EddsaPublicKey = Base32Crockford32B; + /** * Allowed lengths for fractional digits in amounts. */ @@ -633,7 +644,7 @@ data class BankWithdrawalOperationPostResponse( @Serializable data class AddIncomingRequest( val amount: TalerAmount, - val reserve_pub: String, + val reserve_pub: EddsaPublicKey, val debit_account: String ) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt index 6da9d70d..cc56c907 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt @@ -158,7 +158,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: BankApplicationContext) "Currency mismatch", TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH ) - if (db.bankTransactionCheckExists(req.reserve_pub) != null) + if (db.bankTransactionCheckExists(req.reserve_pub.encoded) != null) throw conflict( "Reserve pub. already used", TalerErrorCode.TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT @@ -177,7 +177,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: BankApplicationContext) amount = req.amount, creditorAccountId = exchangeAccount.expectRowId(), transactionDate = txTimestamp, - subject = req.reserve_pub + subject = req.reserve_pub.encoded ) val res = db.bankTransactionCreate(op) /** @@ -189,7 +189,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: BankApplicationContext) "Insufficient balance", TalerErrorCode.TALER_EC_BANK_UNALLOWED_DEBIT ) - val rowId = db.bankTransactionCheckExists(req.reserve_pub) + val rowId = db.bankTransactionCheckExists(req.reserve_pub.encoded) ?: throw internalServerError("Could not find the just inserted bank transaction") call.respond( AddIncomingResponse( diff --git a/bank/src/test/kotlin/TalerApiTest.kt b/bank/src/test/kotlin/TalerApiTest.kt index 17f6c276..8f4caa32 100644 --- a/bank/src/test/kotlin/TalerApiTest.kt +++ b/bank/src/test/kotlin/TalerApiTest.kt @@ -135,7 +135,7 @@ class TalerApiTest { put("amount", "EUR:33") } ) - }.assertStatus(HttpStatusCode.BadRequest) + }.assertBadRequest() // Bad BASE32 wtid client.post("/accounts/foo/taler-wire-gateway/transfer") { @@ -145,7 +145,7 @@ class TalerApiTest { put("wtid", "I love chocolate") } ) - }.assertStatus(HttpStatusCode.BadRequest) + }.assertBadRequest() // Bad BASE32 len wtid client.post("/accounts/foo/taler-wire-gateway/transfer") { @@ -155,7 +155,7 @@ class TalerApiTest { put("wtid", randBase32Crockford(31)) } ) - }.assertStatus(HttpStatusCode.BadRequest) + }.assertBadRequest() // Bad BASE32 request_uid client.post("/accounts/foo/taler-wire-gateway/transfer") { @@ -165,7 +165,7 @@ class TalerApiTest { put("request_uid", "I love chocolate") } ) - }.assertStatus(HttpStatusCode.BadRequest) + }.assertBadRequest() // Bad BASE32 len wtid client.post("/accounts/foo/taler-wire-gateway/transfer") { @@ -175,7 +175,7 @@ class TalerApiTest { put("request_uid", randBase32Crockford(65)) } ) - }.assertStatus(HttpStatusCode.BadRequest) + }.assertBadRequest() } } @@ -279,14 +279,31 @@ class TalerApiTest { application { corebankWebApp(db, ctx) } + val valid_req = json { + put("amount", "KUDOS:44") + put("reserve_pub", randEddsaPublicKey()) + put("debit_account", "${"payto://iban/BAR-IBAN-ABC"}") + }; client.post("/accounts/foo/taler-wire-gateway/admin/add-incoming") { basicAuth("foo", "pw") - jsonBody(AddIncomingRequest( - amount = TalerAmount(value = 44, frac = 0, currency = "KUDOS"), - reserve_pub = "RESERVE-PUB-TEST", - debit_account = "${"payto://iban/BAR-IBAN-ABC"}" - ), deflate = true) + jsonBody(valid_req, deflate = true) }.assertOk() + + // Bad BASE32 reserve_pub + client.post("/accounts/foo/taler-wire-gateway/admin/add-incoming") { + basicAuth("foo", "pw") + jsonBody(json(valid_req) { + put("reserve_pub", "I love chocolate") + }) + }.assertBadRequest() + + // Bad BASE32 len reserve_pub + client.post("/accounts/foo/taler-wire-gateway/admin/add-incoming") { + basicAuth("foo", "pw") + jsonBody(json(valid_req) { + put("reserve_pub", randBase32Crockford(31)) + }) + }.assertBadRequest() } } // Selecting withdrawal details from the Integration API endpoint. diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt index df0669bd..c156ddd4 100644 --- a/bank/src/test/kotlin/helpers.kt +++ b/bank/src/test/kotlin/helpers.kt @@ -16,8 +16,8 @@ fun HttpResponse.assertStatus(status: HttpStatusCode): HttpResponse { assertEquals(status, this.status); return this } - fun HttpResponse.assertOk(): HttpResponse = assertStatus(HttpStatusCode.OK) +fun HttpResponse.assertBadRequest(): HttpResponse = assertStatus(HttpStatusCode.BadRequest) fun BankTransactionResult.assertSuccess() { @@ -39,14 +39,13 @@ inline fun <reified B> HttpRequestBuilder.jsonBody(b: B, deflate: Boolean = fals /* ----- Json DSL ----- */ -inline fun json(from: JsonObject = JsonObject(emptyMap()), builderAction: JsonBuilder2.() -> Unit): JsonObject { - val builder = JsonBuilder2(from) +inline fun json(from: JsonObject = JsonObject(emptyMap()), builderAction: JsonBuilder.() -> Unit): JsonObject { + val builder = JsonBuilder(from) builder.apply(builderAction) - println(builder.content) return JsonObject(builder.content) } -class JsonBuilder2(from: JsonObject) { +class JsonBuilder(from: JsonObject) { val content: MutableMap<String, JsonElement> = from.toMutableMap() inline fun <reified B> put(name: String, b: B) { @@ -69,4 +68,8 @@ fun randHashCode(): HashCode { fun randShortHashCode(): ShortHashCode { return ShortHashCode(randBase32Crockford(32)) +} + +fun randEddsaPublicKey(): EddsaPublicKey { + return EddsaPublicKey(randBase32Crockford(32)) }
\ No newline at end of file |