gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated: refactoring / code cleanup WIP


From: gnunet
Subject: [libeufin] branch master updated: refactoring / code cleanup WIP
Date: Wed, 05 Feb 2020 18:13:44 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new 735135e  refactoring / code cleanup WIP
735135e is described below

commit 735135e718a8b6ebf35cd479851af71d0471ef24
Author: Florian Dold <address@hidden>
AuthorDate: Wed Feb 5 18:13:29 2020 +0100

    refactoring / code cleanup WIP
---
 .../main/kotlin/tech/libeufin/nexus/Containers.kt  |  26 --
 .../kotlin/tech/libeufin/nexus/{DB.kt => Db.kt}    |   0
 .../main/kotlin/tech/libeufin/nexus/EbicsClient.kt | 248 ++++++++++
 .../src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 124 +----
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  | 510 +++++++++------------
 .../tech/libeufin/sandbox/EbicsProtocolBackend.kt  | 467 ++++++++++---------
 sandbox/src/main/python/libeufin-cli               | 143 +++++-
 util/src/main/kotlin/ebics_h004/EbicsRequest.kt    |  12 +
 util/src/main/kotlin/time.kt                       |   4 +-
 util/src/test/kotlin/SignatureDataTest.kt          |   4 +-
 10 files changed, 869 insertions(+), 669 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Containers.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Containers.kt
deleted file mode 100644
index a19be7d..0000000
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Containers.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package tech.libeufin.nexus
-
-import java.security.interfaces.RSAPrivateCrtKey
-import java.security.interfaces.RSAPublicKey
-
-/**
- * This class is a mere container that keeps data found
- * in the database and that is further needed to sign / verify
- * / make messages.  And not all the values are needed all
- * the time.
- */
-data class EbicsContainer(
-    val partnerId: String,
-    val userId: String,
-    var bankAuthPub: RSAPublicKey?,
-    var bankEncPub: RSAPublicKey?,
-    // needed to send the message
-    val ebicsUrl: String,
-    // needed to craft further messages
-    val hostId: String,
-    // needed to decrypt data coming from the bank
-    val customerEncPriv: RSAPrivateCrtKey,
-    // needed to sign documents
-    val customerAuthPriv: RSAPrivateCrtKey,
-    val customerSignPriv: RSAPrivateCrtKey
-)
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Db.kt
similarity index 100%
rename from nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
rename to nexus/src/main/kotlin/tech/libeufin/nexus/Db.kt
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
new file mode 100644
index 0000000..2bd0476
--- /dev/null
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
@@ -0,0 +1,248 @@
+package tech.libeufin.nexus
+
+import io.ktor.application.call
+import io.ktor.client.HttpClient
+import io.ktor.http.ContentType
+import io.ktor.http.HttpStatusCode
+import io.ktor.response.respondText
+import tech.libeufin.util.CryptoUtil
+import tech.libeufin.util.EbicsOrderUtil
+import tech.libeufin.util.ebics_h004.EbicsRequest
+import tech.libeufin.util.ebics_h004.EbicsResponse
+import tech.libeufin.util.ebics_h004.EbicsTypes
+import tech.libeufin.util.getGregorianCalendarNow
+import java.lang.StringBuilder
+import java.math.BigInteger
+import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
+import java.util.*
+import java.util.zip.DeflaterInputStream
+import javax.xml.datatype.XMLGregorianCalendar
+
+/**
+ * This class is a mere container that keeps data found
+ * in the database and that is further needed to sign / verify
+ * / make messages.  And not all the values are needed all
+ * the time.
+ */
+data class EbicsSubscriberDetails(
+    val partnerId: String,
+    val userId: String,
+    var bankAuthPub: RSAPublicKey?,
+    var bankEncPub: RSAPublicKey?,
+    // needed to send the message
+    val ebicsUrl: String,
+    // needed to craft further messages
+    val hostId: String,
+    // needed to decrypt data coming from the bank
+    val customerEncPriv: RSAPrivateCrtKey,
+    // needed to sign documents
+    val customerAuthPriv: RSAPrivateCrtKey,
+    val customerSignPriv: RSAPrivateCrtKey
+)
+
+
+fun createDownloadInitializationPhase(
+    subscriberData: EbicsSubscriberDetails,
+    orderType: String,
+    nonce: ByteArray,
+    date: XMLGregorianCalendar
+): EbicsRequest {
+    return EbicsRequest.createForDownloadInitializationPhase(
+        subscriberData.userId,
+        subscriberData.partnerId,
+        subscriberData.hostId,
+        nonce,
+        date,
+        subscriberData.bankEncPub ?: throw BankKeyMissing(
+            HttpStatusCode.PreconditionFailed
+        ),
+        subscriberData.bankAuthPub ?: throw BankKeyMissing(
+            HttpStatusCode.PreconditionFailed
+        ),
+        orderType
+    )
+}
+
+
+fun createUploadInitializationPhase(
+    subscriberData: EbicsSubscriberDetails,
+    orderType: String,
+    cryptoBundle: CryptoUtil.EncryptionResult
+): EbicsRequest {
+    return EbicsRequest.createForUploadInitializationPhase(
+        cryptoBundle,
+        subscriberData.hostId,
+        getNonce(128),
+        subscriberData.partnerId,
+        subscriberData.userId,
+        getGregorianCalendarNow(),
+        subscriberData.bankAuthPub!!,
+        subscriberData.bankEncPub!!,
+        BigInteger.ONE,
+        orderType
+    )
+}
+
+
+/**
+ * Wrapper around the lower decryption routine, that takes a EBICS response
+ * object containing a encrypted payload, and return the plain version of it
+ * (including decompression).
+ */
+fun decryptAndDecompressResponse(chunks: List<String>, transactionKey: 
ByteArray, privateKey: RSAPrivateCrtKey, pubDigest: ByteArray): ByteArray {
+    val buf = StringBuilder()
+    chunks.forEach { buf.append(it) }
+    val decoded = Base64.getDecoder().decode(buf.toString())
+    val er = CryptoUtil.EncryptionResult(
+        transactionKey,
+        pubDigest,
+        decoded
+    )
+    val dataCompr = CryptoUtil.decryptEbicsE002(
+        er,
+        privateKey
+    )
+    return EbicsOrderUtil.decodeOrderData(dataCompr)
+}
+
+
+/**
+ * Get the private key that matches the given public key digest.
+ */
+fun getDecryptionKey(subscriberDetails: EbicsSubscriberDetails, pubDigest: 
ByteArray): RSAPrivateCrtKey {
+    val authPub = 
CryptoUtil.getRsaPublicFromPrivate(subscriberDetails.customerAuthPriv)
+    val encPub = 
CryptoUtil.getRsaPublicFromPrivate(subscriberDetails.customerEncPriv)
+    val authPubDigest = CryptoUtil.getEbicsPublicKeyHash(authPub)
+    val encPubDigest = CryptoUtil.getEbicsPublicKeyHash(encPub)
+    if (pubDigest.contentEquals(authPubDigest)) {
+        return subscriberDetails.customerAuthPriv
+    }
+    if (pubDigest.contentEquals(encPubDigest)) {
+        return subscriberDetails.customerEncPriv
+    }
+    throw Exception("no matching private key to decrypt response")
+}
+
+
+/**
+ * Do an EBICS download transaction.  This includes the initialization phase, 
transaction phase
+ * and receipt phase.
+ */
+suspend fun doEbicsDownloadTransaction(
+    client: HttpClient,
+    subscriberDetails: EbicsSubscriberDetails,
+    orderType: String
+): ByteArray {
+    val initDownloadRequest = createDownloadInitializationPhase(
+        subscriberDetails,
+        orderType,
+        getNonce(128),
+        getGregorianCalendarNow()
+    )
+    val payloadChunks = LinkedList<String>();
+    val initResponse = client.postToBankSigned<EbicsRequest, EbicsResponse>(
+        subscriberDetails.ebicsUrl,
+        initDownloadRequest,
+        subscriberDetails.customerAuthPriv
+    )
+    if (initResponse.value.body.returnCode.value != "000000") {
+        throw EbicsError(initResponse.value.body.returnCode.value)
+    }
+    val initDataTransfer = initResponse.value.body.dataTransfer
+        ?: throw ProtocolViolationError("initial response for download 
transaction does not contain data transfer")
+    val dataEncryptionInfo = initDataTransfer.dataEncryptionInfo
+        ?: throw ProtocolViolationError("initial response for download 
transaction does not contain date encryption info")
+    val initOrderData = initDataTransfer.orderData.value
+    // FIXME: Also verify that algorithm matches!
+    val decryptionKey = getDecryptionKey(subscriberDetails, 
dataEncryptionInfo.encryptionPubKeyDigest.value)
+    payloadChunks.add(initOrderData)
+    val respPayload = decryptAndDecompressResponse(
+        payloadChunks,
+        dataEncryptionInfo.transactionKey,
+        decryptionKey,
+        dataEncryptionInfo.encryptionPubKeyDigest.value
+    )
+    val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
+        initResponse.value.header._static.transactionID ?: throw 
BankInvalidResponse(
+            HttpStatusCode.ExpectationFailed
+        ),
+        subscriberDetails.hostId
+    )
+    val ackResponse = client.postToBankSignedAndVerify<EbicsRequest, 
EbicsResponse>(
+        subscriberDetails.ebicsUrl,
+        ackRequest,
+        subscriberDetails.bankAuthPub ?: throw BankKeyMissing(
+            HttpStatusCode.PreconditionFailed
+        ),
+        subscriberDetails.customerAuthPriv
+    )
+    if (ackResponse.value.body.returnCode.value != "000000") {
+        throw EbicsError(ackResponse.value.body.returnCode.value)
+    }
+    return respPayload
+}
+
+
+suspend fun doEbicsUploadTransaction(
+    client: HttpClient,
+    subscriberDetails: EbicsSubscriberDetails,
+    orderType: String,
+    payload: ByteArray
+) {
+    if (subscriberDetails.bankEncPub == null) {
+        throw InvalidSubscriberStateError("bank encryption key unknown, 
request HPB first")
+    }
+    val usd_encrypted = CryptoUtil.encryptEbicsE002(
+        EbicsOrderUtil.encodeOrderDataXml(
+            signOrder(
+                payload,
+                subscriberDetails.customerSignPriv,
+                subscriberDetails.partnerId,
+                subscriberDetails.userId
+            )
+        ),
+        subscriberDetails.bankEncPub!!
+    )
+    val response = client.postToBankSignedAndVerify<EbicsRequest, 
EbicsResponse>(
+        subscriberDetails.ebicsUrl,
+        createUploadInitializationPhase(
+            subscriberDetails,
+            orderType,
+            usd_encrypted
+        ),
+        subscriberDetails.bankAuthPub!!,
+        subscriberDetails.customerAuthPriv
+    )
+    if (response.value.header.mutable.returnCode != "000000") {
+        throw EbicsError(response.value.header.mutable.returnCode)
+    }
+    if (response.value.body.returnCode.value != "000000") {
+        throw EbicsError(response.value.body.returnCode.value)
+    }
+    logger.debug("INIT phase passed!")
+    /* now send actual payload */
+    val compressedInnerPayload = DeflaterInputStream(
+        payload.inputStream()
+    ).use { it.readAllBytes() }
+    val encryptedPayload = CryptoUtil.encryptEbicsE002withTransactionKey(
+        compressedInnerPayload,
+        subscriberDetails.bankEncPub!!,
+        usd_encrypted.plainTransactionKey!!
+    )
+    val tmp = EbicsRequest.createForUploadTransferPhase(
+        subscriberDetails.hostId,
+        response.value.header._static.transactionID!!,
+        BigInteger.ONE,
+        encryptedPayload.encryptedData
+    )
+    val responseTransaction = client.postToBankSignedAndVerify<EbicsRequest, 
EbicsResponse>(
+        subscriberDetails.ebicsUrl,
+        tmp,
+        subscriberDetails.bankAuthPub!!,
+        subscriberDetails.customerAuthPriv
+    )
+    if (responseTransaction.value.body.returnCode.value != "000000") {
+        throw EbicsError(response.value.body.returnCode.value)
+    }
+}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index df64b63..ed65340 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -17,126 +17,6 @@ import java.util.*
 import javax.xml.bind.JAXBElement
 import javax.xml.datatype.XMLGregorianCalendar
 
-
-/**
- * Wrapper around the lower decryption routine, that takes a EBICS response
- * object containing a encrypted payload, and return the plain version of it
- * (including decompression).
- */
-fun decryptAndDecompressResponse(response: EbicsResponse, privateKey: 
RSAPrivateCrtKey): ByteArray {
-    val er = CryptoUtil.EncryptionResult(
-        response.body.dataTransfer!!.dataEncryptionInfo!!.transactionKey,
-        (response.body.dataTransfer!!.dataEncryptionInfo as 
EbicsTypes.DataEncryptionInfo)
-            .encryptionPubKeyDigest.value,
-        
Base64.getDecoder().decode(response.body.dataTransfer!!.orderData.value)
-    )
-    val dataCompr = CryptoUtil.decryptEbicsE002(
-        er,
-        privateKey
-    )
-    return EbicsOrderUtil.decodeOrderData(dataCompr)
-}
-
-fun createDownloadInitializationPhase(
-    subscriberData: EbicsContainer,
-    orderType: String,
-    nonce: ByteArray,
-    date: XMLGregorianCalendar
-): EbicsRequest {
-    return EbicsRequest.createForDownloadInitializationPhase(
-        subscriberData.userId,
-        subscriberData.partnerId,
-        subscriberData.hostId,
-        nonce,
-        date,
-        subscriberData.bankEncPub ?: throw BankKeyMissing(
-            HttpStatusCode.PreconditionFailed
-        ),
-        subscriberData.bankAuthPub ?: throw BankKeyMissing(
-            HttpStatusCode.PreconditionFailed
-        ),
-        orderType
-    )
-}
-
-fun createDownloadInitializationPhase(
-    subscriberData: EbicsContainer,
-    orderType: String,
-    nonce: ByteArray,
-    date: XMLGregorianCalendar,
-    dateStart: XMLGregorianCalendar,
-    dateEnd: XMLGregorianCalendar
-): EbicsRequest {
-    return EbicsRequest.createForDownloadInitializationPhase(
-        subscriberData.userId,
-        subscriberData.partnerId,
-        subscriberData.hostId,
-        nonce,
-        date,
-        subscriberData.bankEncPub ?: throw BankKeyMissing(
-            HttpStatusCode.PreconditionFailed
-        ),
-        subscriberData.bankAuthPub ?: throw BankKeyMissing(
-            HttpStatusCode.PreconditionFailed
-        ),
-        orderType,
-        dateStart,
-        dateEnd
-    )
-}
-
-fun createUploadInitializationPhase(
-    subscriberData: EbicsContainer,
-    orderType: String,
-    cryptoBundle: CryptoUtil.EncryptionResult
-): EbicsRequest {
-    return EbicsRequest.createForUploadInitializationPhase(
-        cryptoBundle,
-        subscriberData.hostId,
-        getNonce(128),
-        subscriberData.partnerId,
-        subscriberData.userId,
-        getGregorianDate(),
-        subscriberData.bankAuthPub!!,
-        subscriberData.bankEncPub!!,
-        BigInteger.ONE,
-        orderType
-    )
-}
-
-/**
- * Usually, queries must return lots of data from within a transaction
- * block.  For convenience, we wrap such data into a EbicsContainer, so
- * that only one object is always returned from the transaction block.
- */
-fun containerInit(subscriber: EbicsSubscriberEntity): EbicsContainer {
-    var bankAuthPubValue: RSAPublicKey? = null
-    if (subscriber.bankAuthenticationPublicKey != null) {
-        bankAuthPubValue = CryptoUtil.loadRsaPublicKey(
-            subscriber.bankAuthenticationPublicKey?.toByteArray()!!
-        )
-    }
-    var bankEncPubValue: RSAPublicKey? = null
-    if (subscriber.bankEncryptionPublicKey != null) {
-        bankEncPubValue = CryptoUtil.loadRsaPublicKey(
-            subscriber.bankEncryptionPublicKey?.toByteArray()!!
-        )
-    }
-    return EbicsContainer(
-        bankAuthPub = bankAuthPubValue,
-        bankEncPub = bankEncPubValue,
-
-        ebicsUrl = subscriber.ebicsURL,
-        hostId = subscriber.hostID,
-        userId = subscriber.userID,
-        partnerId = subscriber.partnerID,
-
-        customerSignPriv = 
CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()),
-        customerAuthPriv = 
CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()),
-        customerEncPriv = 
CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray())
-    )
-}
-
 /**
  * Inserts spaces every 2 characters, and a newline after 8 pairs.
  */
@@ -191,7 +71,7 @@ fun signOrder(
  * response already converted in JAXB.
  */
 suspend inline fun HttpClient.postToBank(url: String, body: String): String {
-    LOGGER.debug("Posting: $body")
+    logger.debug("Posting: $body")
     val response = try {
         this.post<String>(
             urlString = url,
@@ -217,7 +97,7 @@ suspend inline fun <reified T, reified S> 
HttpClient.postToBankSignedAndVerify(
     val doc = XMLUtil.convertJaxbToDocument(body)
     XMLUtil.signEbicsDocument(doc, priv)
     val response: String = this.postToBank(url, 
XMLUtil.convertDomToString(doc))
-    LOGGER.debug("About to verify: ${response}")
+    logger.debug("About to verify: ${response}")
     val responseDocument = try {
         XMLUtil.parseStringIntoDom(response)
     } catch (e: Exception) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index f065190..6d33f4e 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -38,13 +38,10 @@ import io.ktor.response.respondText
 import io.ktor.routing.*
 import io.ktor.server.engine.embeddedServer
 import io.ktor.server.netty.Netty
-import io.ktor.util.encodeBase64
-import org.jetbrains.exposed.dao.EntityID
 import org.jetbrains.exposed.exceptions.ExposedSQLException
 import org.jetbrains.exposed.sql.StdOutSqlLogger
 import org.jetbrains.exposed.sql.addLogger
 import org.jetbrains.exposed.sql.transactions.transaction
-import org.joda.time.DateTime
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.slf4j.event.Level
@@ -55,10 +52,10 @@ import javax.sql.rowset.serial.SerialBlob
 import tech.libeufin.util.toHexString
 import tech.libeufin.util.CryptoUtil
 import tech.libeufin.util.EbicsOrderUtil
-import tech.libeufin.util.XMLUtil
 import tech.libeufin.util.ebics_hev.HEVRequest
 import tech.libeufin.util.ebics_hev.HEVResponse
 import java.math.BigInteger
+import java.security.interfaces.RSAPublicKey
 import java.text.SimpleDateFormat
 import java.util.*
 import java.util.zip.DeflaterInputStream
@@ -82,7 +79,7 @@ fun testData() {
             }
         }
     } catch (e: ExposedSQLException) {
-        LOGGER.info("Likely primary key collision for sample data: accepted")
+        logger.info("Likely primary key collision for sample data: accepted")
     }
 }
 
@@ -90,26 +87,63 @@ data class NotAnIdError(val statusCode: HttpStatusCode) : 
Exception("String ID n
 data class BankKeyMissing(val statusCode: HttpStatusCode) : 
Exception("Impossible operation: bank keys are missing")
 data class SubscriberNotFoundError(val statusCode: HttpStatusCode) : 
Exception("Subscriber not found in database")
 data class UnreachableBankError(val statusCode: HttpStatusCode) : 
Exception("Could not reach the bank")
-data class UnparsableResponse(val statusCode: HttpStatusCode, val rawResponse: 
String) : Exception("bank responded: ${rawResponse}")
+data class UnparsableResponse(val statusCode: HttpStatusCode, val rawResponse: 
String) :
+    Exception("bank responded: ${rawResponse}")
+
+class ProtocolViolationError(message: String) : Exception("protocol violation: 
${message}")
+class InvalidSubscriberStateError(message: String) : Exception("invalid 
subscriber state: ${message}")
 data class EbicsError(val codeError: String) : Exception("Bank did not 
accepted EBICS request, error is: ${codeError}")
 data class BadSignature(val statusCode: HttpStatusCode) : Exception("Signature 
verification unsuccessful")
 data class BadBackup(val statusCode: HttpStatusCode) : Exception("Could not 
restore backed up keys")
 data class BankInvalidResponse(val statusCode: HttpStatusCode) : 
Exception("Missing data from bank response")
 
-val LOGGER: Logger = LoggerFactory.getLogger("tech.libeufin.nexus")
+val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus")
+
+fun getSubscriberDetailsFromId(id: String): EbicsSubscriberDetails {
+    return transaction {
+        val subscriber = EbicsSubscriberEntity.findById(
+            id
+        ) ?: throw SubscriberNotFoundError(
+            HttpStatusCode.NotFound
+        )
+        var bankAuthPubValue: RSAPublicKey? = null
+        if (subscriber.bankAuthenticationPublicKey != null) {
+            bankAuthPubValue = CryptoUtil.loadRsaPublicKey(
+                subscriber.bankAuthenticationPublicKey?.toByteArray()!!
+            )
+        }
+        var bankEncPubValue: RSAPublicKey? = null
+        if (subscriber.bankEncryptionPublicKey != null) {
+            bankEncPubValue = CryptoUtil.loadRsaPublicKey(
+                subscriber.bankEncryptionPublicKey?.toByteArray()!!
+            )
+        }
+        EbicsSubscriberDetails(
+            bankAuthPub = bankAuthPubValue,
+            bankEncPub = bankEncPubValue,
+
+            ebicsUrl = subscriber.ebicsURL,
+            hostId = subscriber.hostID,
+            userId = subscriber.userID,
+            partnerId = subscriber.partnerID,
+
+            customerSignPriv = 
CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()),
+            customerAuthPriv = 
CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()),
+            customerEncPriv = 
CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray())
+        )
+    }
+}
 
 fun main() {
     dbCreateTables()
     testData()
-    val client = HttpClient(){
+    val client = HttpClient() {
         expectSuccess = false // this way, it does not throw exceptions on != 
200 responses.
     }
-    val logger = LoggerFactory.getLogger("tech.libeufin.nexus")
     val server = embeddedServer(Netty, port = 5001) {
         install(CallLogging) {
             this.level = Level.DEBUG
-            this.logger = LOGGER
-
+            this.logger = tech.libeufin.nexus.logger
         }
         install(ContentNegotiation) {
             moshi {
@@ -137,18 +171,28 @@ fun main() {
 
             exception<BadBackup> { cause ->
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
-                call.respondText("Bad backup, or passphrase incorrect\n", 
ContentType.Text.Plain, HttpStatusCode.BadRequest)
+                call.respondText(
+                    "Bad backup, or passphrase incorrect\n",
+                    ContentType.Text.Plain,
+                    HttpStatusCode.BadRequest
+                )
             }
 
             exception<UnparsableResponse> { cause ->
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
-                call.respondText("Could not parse bank response 
(${cause.message})\n", ContentType.Text.Plain, HttpStatusCode
-                    .InternalServerError)
+                call.respondText(
+                    "Could not parse bank response (${cause.message})\n", 
ContentType.Text.Plain, HttpStatusCode
+                        .InternalServerError
+                )
             }
 
             exception<UnreachableBankError> { cause ->
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
-                call.respondText("Could not reach the bank\n", 
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
+                call.respondText(
+                    "Could not reach the bank\n",
+                    ContentType.Text.Plain,
+                    HttpStatusCode.InternalServerError
+                )
             }
 
             exception<SubscriberNotFoundError> { cause ->
@@ -158,17 +202,29 @@ fun main() {
 
             exception<BadSignature> { cause ->
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
-                call.respondText("Signature verification unsuccessful\n", 
ContentType.Text.Plain, HttpStatusCode.NotAcceptable)
+                call.respondText(
+                    "Signature verification unsuccessful\n",
+                    ContentType.Text.Plain,
+                    HttpStatusCode.NotAcceptable
+                )
             }
 
             exception<EbicsError> { cause ->
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
-                call.respondText("Bank gave EBICS-error response\n", 
ContentType.Text.Plain, HttpStatusCode.NotAcceptable)
+                call.respondText(
+                    "Bank gave EBICS-error response\n",
+                    ContentType.Text.Plain,
+                    HttpStatusCode.NotAcceptable
+                )
             }
 
             exception<BankKeyMissing> { cause ->
                 logger.error("Exception while handling '${call.request.uri}'", 
cause)
-                call.respondText("Impossible operation: get bank keys 
first\n", ContentType.Text.Plain, HttpStatusCode.NotAcceptable)
+                call.respondText(
+                    "Impossible operation: get bank keys first\n",
+                    ContentType.Text.Plain,
+                    HttpStatusCode.NotAcceptable
+                )
             }
 
             exception<javax.xml.bind.UnmarshalException> { cause ->
@@ -180,12 +236,14 @@ fun main() {
                 )
             }
         }
+
         intercept(ApplicationCallPipeline.Fallback) {
             if (this.call.response.status() == null) {
                 call.respondText("Not found (no route matched).\n", 
ContentType.Text.Plain, HttpStatusCode.NotFound)
                 return@intercept finish()
             }
         }
+
         routing {
             get("/") {
                 call.respondText("Hello by Nexus!\n")
@@ -194,203 +252,124 @@ fun main() {
 
             post("/ebics/subscribers/{id}/sendPTK") {
                 val id = expectId(call.parameters["id"])
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(
-                            id
-                        ) ?: throw SubscriberNotFoundError(
-                            HttpStatusCode.NotFound
-                        )
-                    )
-                }
-                val response = client.postToBankSigned<EbicsRequest, 
EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    createDownloadInitializationPhase(
-                        subscriberData,
-                        "PTK",
-                        getNonce(128),
-                        getGregorianDate()
-                    ),
-                    subscriberData.customerAuthPriv
-                )
-                val payload: ByteArray =
-                    decryptAndDecompressResponse(
-                        response.value,
-                        subscriberData.customerAuthPriv
-                    )
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "PTK")
                 call.respondText(
-                    payload.toString(Charsets.UTF_8),
+                    response.toString(Charsets.UTF_8),
                     ContentType.Text.Plain,
-                    HttpStatusCode.OK)
-
+                    HttpStatusCode.OK
+                )
                 return@post
             }
 
             post("/ebics/subscribers/{id}/sendHAC") {
                 val id = expectId(call.parameters["id"])
-
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(
-                            id
-                        ) ?: throw SubscriberNotFoundError(
-                            HttpStatusCode.NotFound
-                        )
-                    )
-                }
-                val response = client.postToBankSigned<EbicsRequest, 
EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    createDownloadInitializationPhase(
-                        subscriberData,
-                        "HAC",
-                        getNonce(128),
-                        getGregorianDate()
-                    ),
-                    subscriberData.customerAuthPriv
-                )
-                val payload: ByteArray =
-                    decryptAndDecompressResponse(
-                        response.value,
-                        subscriberData.customerAuthPriv
-                    )
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HAC")
                 call.respondText(
-                    payload.toString(Charsets.UTF_8),
+                    response.toString(Charsets.UTF_8),
                     ContentType.Text.Plain,
-                    HttpStatusCode.OK)
-
+                    HttpStatusCode.OK
+                )
                 return@post
             }
 
-
             post("/ebics/subscribers/{id}/sendC52") {
                 val id = expectId(call.parameters["id"])
-                val body = call.receive<EbicsDateRange>()
-
-                val startDate = DateTime.parse(body.start)
-                val endDate = DateTime.parse(body.end)
-                // will throw DateTimeParseException if strings are malformed.
-
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(
-                            id
-                        ) ?: throw SubscriberNotFoundError(
-                            HttpStatusCode.NotFound
-                        )
-                    )
-                }
-                val response = client.postToBankSigned<EbicsRequest, 
EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    createDownloadInitializationPhase(
-                        subscriberData,
-                        "C52",
-                        getNonce(128),
-                        getGregorianDate(),
-                        getGregorianDate(startDate.year, startDate.monthOfYear 
- 1, startDate.dayOfMonth),
-                        getGregorianDate(endDate.year, endDate.monthOfYear - 
1, endDate.dayOfMonth)
-                    ),
-                    subscriberData.customerAuthPriv
-                )
-                val payload: ByteArray =
-                    decryptAndDecompressResponse(
-                        response.value,
-                        subscriberData.customerAuthPriv
-                    )
-                val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
-                    response.value.header._static.transactionID ?: throw 
BankInvalidResponse(
-                        HttpStatusCode.ExpectationFailed
-                    ),
-                    subscriberData.hostId
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "C52")
+                call.respondText(
+                    response.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
                 )
+                return@post
+            }
 
-                val ackResponse = 
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    ackRequest,
-                    subscriberData.bankAuthPub ?: throw BankKeyMissing(
-                        HttpStatusCode.PreconditionFailed
-                    ),
-                    subscriberData.customerAuthPriv
+            post("/ebics/subscribers/{id}/sendHtd") {
+                val id = expectId(call.parameters["id"])
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HTD")
+                call.respondText(
+                    response.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
                 )
-                logger.debug("C52 final response: " + 
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
-                if (ackResponse.value.body.returnCode.value != "000000") {
-                    throw EbicsError(response.value.body.returnCode.value)
-                }
+                return@post
+            }
 
+            post("/ebics/subscribers/{id}/sendHAA") {
+                val id = expectId(call.parameters["id"])
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HAA")
                 call.respondText(
-                    payload.toString(Charsets.UTF_8),
+                    response.toString(Charsets.UTF_8),
                     ContentType.Text.Plain,
-                    HttpStatusCode.OK)
-
+                    HttpStatusCode.OK
+                )
                 return@post
             }
-            get("/ebics/subscribers/{id}/sendHtd") {
+
+            post("/ebics/subscribers/{id}/sendHVZ") {
                 val id = expectId(call.parameters["id"])
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(
-                            id
-                        ) ?: throw SubscriberNotFoundError(
-                            HttpStatusCode.NotFound
-                        )
-                    )
-                }
-                val response = client.postToBankSigned<EbicsRequest, 
EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    createDownloadInitializationPhase(
-                        subscriberData,
-                        "HTD",
-                        getNonce(128),
-                        getGregorianDate()
-                    ),
-                    subscriberData.customerAuthPriv
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HVZ")
+                call.respondText(
+                    response.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
                 )
-                logger.debug("HTD response: " + 
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
-                if (response.value.body.returnCode.value != "000000") {
-                    throw EbicsError(response.value.body.returnCode.value)
-                }
-
-                val encPubKeyDigestViaBank = 
(response.value.body.dataTransfer!!.dataEncryptionInfo as 
EbicsTypes.DataEncryptionInfo)
-                    .encryptionPubKeyDigest.value;
-                println("encPubKeyDigestViaBank 
${encPubKeyDigestViaBank.toHexString()}")
+                return@post
+            }
 
-                val er = CryptoUtil.EncryptionResult(
-                    
response.value.body.dataTransfer!!.dataEncryptionInfo!!.transactionKey,
-                    (response.value.body.dataTransfer!!.dataEncryptionInfo as 
EbicsTypes.DataEncryptionInfo)
-                        .encryptionPubKeyDigest.value,
-                    
Base64.getDecoder().decode(response.value.body.dataTransfer!!.orderData.value)
+            post("/ebics/subscribers/{id}/sendHVU") {
+                val id = expectId(call.parameters["id"])
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HVU")
+                call.respondText(
+                    response.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
                 )
+                return@post
+            }
 
-                val dataCompr = CryptoUtil.decryptEbicsE002(
-                    er,
-                    subscriberData.customerAuthPriv
-                )
-                val data = 
EbicsOrderUtil.decodeOrderDataXml<HTDResponseOrderData>(dataCompr)
-                logger.debug("HTD payload is: 
${XMLUtil.convertJaxbToString(data)}")
-                val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
-                    response.value.header._static.transactionID ?: throw 
BankInvalidResponse(
-                        HttpStatusCode.ExpectationFailed
-                    ),
-                    subscriberData.hostId
+            post("/ebics/subscribers/{id}/sendHPD") {
+                val id = expectId(call.parameters["id"])
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HPD")
+                call.respondText(
+                    response.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
                 )
+                return@post
+            }
 
-                val ackResponse = 
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    ackRequest,
-                    subscriberData.bankAuthPub ?: throw BankKeyMissing(
-                        HttpStatusCode.PreconditionFailed
-                    ),
-                    subscriberData.customerAuthPriv
+            post("/ebics/subscribers/{id}/sendHKD") {
+                val id = expectId(call.parameters["id"])
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "HKD")
+                call.respondText(
+                    response.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
                 )
-                logger.debug("HTD final response: " + 
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
-                if (ackResponse.value.body.returnCode.value != "000000") {
-                    throw EbicsError(response.value.body.returnCode.value)
-                }
+                return@post
+            }
+
+            post("/ebics/subscribers/{id}/sendTSD") {
+                val id = expectId(call.parameters["id"])
+                val subscriberData = getSubscriberDetailsFromId(id)
+                val response = doEbicsDownloadTransaction(client, 
subscriberData, "TSD")
                 call.respondText(
-                    "Success! Details (temporarily) reported on the Nexus 
console.",
+                    response.toString(Charsets.UTF_8),
                     ContentType.Text.Plain,
                     HttpStatusCode.OK
                 )
+                return@post
             }
+
             get("/ebics/subscribers/{id}/keyletter") {
                 val id = expectId(call.parameters["id"])
                 var usernameLine = "TODO"
@@ -520,8 +499,9 @@ fun main() {
                     HttpStatusCode.OK
                 )
             }
+
             get("/ebics/subscribers") {
-                var ret = EbicsSubscribersResponse()
+                val ret = EbicsSubscribersResponse()
                 transaction {
                     EbicsSubscriberEntity.all().forEach {
                         ret.ebicsSubscribers.add(
@@ -539,6 +519,7 @@ fun main() {
                 call.respond(ret)
                 return@get
             }
+
             get("/ebics/subscribers/{id}") {
                 val id = expectId(call.parameters["id"])
                 val response = transaction {
@@ -557,6 +538,7 @@ fun main() {
                 call.respond(HttpStatusCode.OK, response)
                 return@get
             }
+
             get("/ebics/{id}/sendHev") {
                 val id = expectId(call.parameters["id"])
                 val (ebicsUrl, hostID) = transaction {
@@ -572,10 +554,12 @@ fun main() {
                 call.respond(
                     HttpStatusCode.OK,
                     EbicsHevResponse(response.value.versionNumber!!.map {
-                        ProtocolAndVersion(it.value, it.protocolVersion, 
hostID) })
+                        ProtocolAndVersion(it.value, it.protocolVersion, 
hostID)
+                    })
                 )
                 return@get
             }
+
             post("/ebics/{id}/subscribers") {
                 val body = call.receive<EbicsSubscriberInfoRequest>()
                 val pairA = CryptoUtil.generateRsaKeyPair(2048)
@@ -606,16 +590,10 @@ fun main() {
                 )
                 return@post
             }
+
             post("/ebics/subscribers/{id}/sendIni") {
                 val id = expectId(call.parameters["id"])
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(id)
-                            ?: throw SubscriberNotFoundError(
-                                HttpStatusCode.NotFound
-                            )
-                    )
-                }
+                val subscriberData = getSubscriberDetailsFromId(id)
                 val iniRequest = EbicsUnsecuredRequest.createIni(
                     subscriberData.hostId,
                     subscriberData.userId,
@@ -632,6 +610,7 @@ fun main() {
                 call.respondText("Bank accepted signature key\n", 
ContentType.Text.Plain, HttpStatusCode.OK)
                 return@post
             }
+
             post("/ebics/subscribers/{id}/restoreBackup") {
                 val body = call.receive<EbicsKeysBackup>()
                 val id = expectId(call.parameters["id"])
@@ -659,10 +638,10 @@ fun main() {
                     )
                 } catch (e: Exception) {
                     e.printStackTrace()
-                    LOGGER.info("Restoring keys failed, probably due to wrong 
passphrase")
+                    logger.info("Restoring keys failed, probably due to wrong 
passphrase")
                     throw BadBackup(HttpStatusCode.BadRequest)
                 }
-                LOGGER.info("Restoring keys, creating new user: $id")
+                logger.info("Restoring keys, creating new user: $id")
                 try {
                     transaction {
                         EbicsSubscriberEntity.new(id = 
expectId(call.parameters["id"])) {
@@ -704,7 +683,7 @@ fun main() {
                         bytesToBase64(authPub.encoded),
                         bytesToBase64(encPub.encoded),
                         bytesToBase64(sigPub.encoded)
-                        )
+                    )
                 }
                 call.respond(
                     HttpStatusCode.OK,
@@ -725,18 +704,24 @@ fun main() {
                         hostID = subscriber.hostID,
                         partnerID = subscriber.partnerID,
                         ebicsURL = subscriber.ebicsURL,
-                        authBlob = bytesToBase64(CryptoUtil.encryptKey(
-                            subscriber.authenticationPrivateKey.toByteArray(),
-                            body.passphrase
-                        )),
-                        encBlob = bytesToBase64(CryptoUtil.encryptKey(
-                            subscriber.encryptionPrivateKey.toByteArray(),
-                            body.passphrase
-                        )),
-                        sigBlob = bytesToBase64(CryptoUtil.encryptKey(
-                            subscriber.signaturePrivateKey.toByteArray(),
-                            body.passphrase
-                        ))
+                        authBlob = bytesToBase64(
+                            CryptoUtil.encryptKey(
+                                
subscriber.authenticationPrivateKey.toByteArray(),
+                                body.passphrase
+                            )
+                        ),
+                        encBlob = bytesToBase64(
+                            CryptoUtil.encryptKey(
+                                subscriber.encryptionPrivateKey.toByteArray(),
+                                body.passphrase
+                            )
+                        ),
+                        sigBlob = bytesToBase64(
+                            CryptoUtil.encryptKey(
+                                subscriber.signaturePrivateKey.toByteArray(),
+                                body.passphrase
+                            )
+                        )
                     )
                 }
                 call.response.headers.append("Content-Disposition", 
"attachment")
@@ -745,76 +730,14 @@ fun main() {
                     response
                 )
             }
-            post("/ebics/subscribers/{id}/sendTst") {
+
+            post("/ebics/subscribers/{id}/sendTSU") {
                 val id = expectId(call.parameters["id"])
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(id)
-                            ?: throw SubscriberNotFoundError(
-                                HttpStatusCode.NotFound
-                            )
-                    )
-                }
+                val subscriberData = getSubscriberDetailsFromId(id)
                 val payload = "PAYLOAD"
-                if (subscriberData.bankEncPub == null) {
-                    call.respondText(
-                        "Bank encryption key not found, request HPB first!\n",
-                        ContentType.Text.Plain,
-                        HttpStatusCode.NotFound
-                    )
-                    return@post
-                }
-                val usd_encrypted = CryptoUtil.encryptEbicsE002(
-                    EbicsOrderUtil.encodeOrderDataXml(
-
-                        signOrder(
-                            payload.toByteArray(),
-                            subscriberData.customerSignPriv,
-                            subscriberData.partnerId,
-                            subscriberData.userId
-                        )
-                    ),
-                    subscriberData.bankEncPub!!
-                )
-                val response = client.postToBankSignedAndVerify<EbicsRequest, 
EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    createUploadInitializationPhase(
-                        subscriberData,
-                        "TST",
-                        usd_encrypted
-                    ),
-                    subscriberData.bankAuthPub!!,
-                    subscriberData.customerAuthPriv
-                )
-                if (response.value.body.returnCode.value != "000000") {
-                    throw EbicsError(response.value.body.returnCode.value)
-                }
-                logger.debug("INIT phase passed!")
-                /* now send actual payload */
-                val compressedInnerPayload = DeflaterInputStream(
-                    payload.toByteArray().inputStream()
-
-                ).use { it.readAllBytes() }
-                val encryptedPayload = 
CryptoUtil.encryptEbicsE002withTransactionKey(
-                    compressedInnerPayload,
-                    subscriberData.bankEncPub!!,
-                    usd_encrypted.plainTransactionKey!!
-                )
-                val tmp = EbicsRequest.createForUploadTransferPhase(
-                    subscriberData.hostId,
-                    response.value.header._static.transactionID!!,
-                    BigInteger.ONE,
-                    encryptedPayload.encryptedData
-                )
-                val responseTransaction = 
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
-                    subscriberData.ebicsUrl,
-                    tmp,
-                    subscriberData.bankAuthPub!!,
-                    subscriberData.customerAuthPriv
-                )
-                if (responseTransaction.value.body.returnCode.value != 
"000000") {
-                    throw EbicsError(response.value.body.returnCode.value)
-                }
+
+                doEbicsUploadTransaction(client, subscriberData, "TSU", 
payload.toByteArray(Charsets.UTF_8))
+
                 call.respondText(
                     "TST INITIALIZATION & TRANSACTION phases succeeded\n",
                     ContentType.Text.Plain,
@@ -824,34 +747,29 @@ fun main() {
 
             post("/ebics/subscribers/{id}/sync") {
                 val id = expectId(call.parameters["id"])
-                val bundle = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(id)
-                            ?: throw SubscriberNotFoundError(
-                                HttpStatusCode.NotFound
-                            )
-                    )
-                }
+                val subscriberDetails = getSubscriberDetailsFromId(id)
                 val response = client.postToBankSigned<EbicsNpkdRequest, 
EbicsKeyManagementResponse>(
-                    bundle.ebicsUrl,
+                    subscriberDetails.ebicsUrl,
                     EbicsNpkdRequest.createRequest(
-                        bundle.hostId,
-                        bundle.partnerId,
-                        bundle.userId,
+                        subscriberDetails.hostId,
+                        subscriberDetails.partnerId,
+                        subscriberDetails.userId,
                         getNonce(128),
-                        getGregorianDate()
+                        getGregorianCalendarNow()
                     ),
-                    bundle.customerAuthPriv
+                    subscriberDetails.customerAuthPriv
                 )
                 if (response.value.body.returnCode.value != "000000") {
                     throw EbicsError(response.value.body.returnCode.value)
                 }
-                val encPubKeyDigestViaBank = 
(response.value.body.dataTransfer!!.dataEncryptionInfo as 
EbicsTypes.DataEncryptionInfo)
-                    .encryptionPubKeyDigest.value;
-                val customerEncPub = 
CryptoUtil.getRsaPublicFromPrivate(bundle.customerEncPriv);
+                val encPubKeyDigestViaBank =
+                    (response.value.body.dataTransfer!!.dataEncryptionInfo as 
EbicsTypes.DataEncryptionInfo)
+                        .encryptionPubKeyDigest.value;
+                val customerEncPub = 
CryptoUtil.getRsaPublicFromPrivate(subscriberDetails.customerEncPriv);
                 val encPubKeyDigestViaNexus = 
CryptoUtil.getEbicsPublicKeyHash(customerEncPub)
                 println("encPubKeyDigestViaBank: 
${encPubKeyDigestViaBank.toHexString()}")
                 println("encPubKeyDigestViaNexus: 
${encPubKeyDigestViaNexus.toHexString()}")
+                val decryptionKey = getDecryptionKey(subscriberDetails, 
encPubKeyDigestViaBank)
                 val er = CryptoUtil.EncryptionResult(
                     
response.value.body.dataTransfer!!.dataEncryptionInfo!!.transactionKey,
                     encPubKeyDigestViaBank,
@@ -859,7 +777,7 @@ fun main() {
                 )
                 val dataCompr = CryptoUtil.decryptEbicsE002(
                     er,
-                    bundle.customerAuthPriv
+                    decryptionKey
                 )
                 val data = 
EbicsOrderUtil.decodeOrderDataXml<HPBResponseOrderData>(dataCompr)
                 // put bank's keys into database.
@@ -883,16 +801,10 @@ fun main() {
                 call.respondText("Bank keys stored in database\n", 
ContentType.Text.Plain, HttpStatusCode.OK)
                 return@post
             }
+
             post("/ebics/subscribers/{id}/sendHia") {
                 val id = expectId(call.parameters["id"])
-                val subscriberData = transaction {
-                    containerInit(
-                        EbicsSubscriberEntity.findById(id)
-                            ?: throw SubscriberNotFoundError(
-                                HttpStatusCode.NotFound
-                            )
-                    )
-                }
+                val subscriberData = getSubscriberDetailsFromId(id)
                 val responseJaxb = 
client.postToBankUnsigned<EbicsUnsecuredRequest, EbicsKeyManagementResponse>(
                     subscriberData.ebicsUrl,
                     EbicsUnsecuredRequest.createHia(
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index 162353c..ec1fd5b 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -40,17 +40,18 @@ import tech.libeufin.util.EbicsOrderUtil
 import tech.libeufin.util.XMLUtil
 import tech.libeufin.util.*
 import tech.libeufin.util.XMLUtil.Companion.signEbicsResponse
-import java.awt.List
 import java.math.BigDecimal
 import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
 import java.util.*
 import java.util.zip.DeflaterInputStream
 import java.util.zip.InflaterInputStream
 import javax.sql.rowset.serial.SerialBlob
 
 
-open class EbicsRequestError(val errorText: String, val errorCode: String) :
-    Exception("EBICS request management error: $errorText ($errorCode)")
+open class EbicsRequestError(errorText: String, errorCode: String) :
+    Exception("EBICS request  error: $errorText ($errorCode)")
+
 
 class EbicsInvalidRequestError : EbicsRequestError(
     "[EBICS_INVALID_REQUEST] Invalid request",
@@ -146,13 +147,13 @@ private fun iterHistory(customerId: Int, header: 
EbicsRequest.Header, base: XmlE
             (header.static.orderDetails?.orderParams as 
EbicsRequest.StandardOrderParams).dateRange!!.start.toString()
         } catch (e: Exception) {
             LOGGER.debug("Asked to iterate over history with NO start date; 
default to now")
-            getGregorianDate().toString()
+            getGregorianCalendarNow().toString()
         },
         try {
             (header.static.orderDetails?.orderParams as 
EbicsRequest.StandardOrderParams).dateRange!!.end.toString()
         } catch (e: Exception) {
             LOGGER.debug("Asked to iterate over history with NO end date; 
default to now")
-            getGregorianDate().toString()
+            getGregorianCalendarNow().toString()
         }
     ) {
 
@@ -254,9 +255,7 @@ private fun balance(base: XmlElementBuilder) {
  * @param type 52 or 53.
  */
 private fun constructCamtResponse(type: Int, customerId: Int, header: 
EbicsRequest.Header): String {
-
     val camt = constructXml(indent = true) {
-
         namespace("foo", "bar") // FIXME: set right namespace!
         root("foo:BkToCstmrAcctRpt") {
             element("GrpHdr") {
@@ -273,21 +272,18 @@ private fun constructCamtResponse(type: Int, customerId: 
Int, header: EbicsReque
             }
         }
     }
-
   return camt
 }
 
-private fun ApplicationCall.handleEbicsC52(header: EbicsRequest.Header): 
ByteArray {
 
-    val userId = header.static.userID!!
+private fun handleEbicsTSD(requestContext: RequestContext): ByteArray {
+    return "Hello World".toByteArray()
+}
 
-    val subscriber = transaction {
-        EbicsSubscriberEntity.find {
-            stringParam(userId) eq EbicsSubscribersTable.userId // will have 
to match partner and system IDs
-        }
-    }.firstOrNull() ?: throw Exception("Unknown subscriber")
 
-    return constructCamtResponse(52, subscriber.bankCustomer.id.value, 
header).toByteArray()
+private fun handleEbicsC52(requestContext: RequestContext): ByteArray {
+    val subscriber = requestContext.subscriber
+    return constructCamtResponse(52, subscriber.bankCustomer.id.value, 
requestContext.requestObject.header).toByteArray()
 }
 
 private suspend fun ApplicationCall.handleEbicsHia(header: 
EbicsUnsecuredRequest.Header, orderData: ByteArray) {
@@ -629,6 +625,238 @@ fun handleEbicsHkd(): ByteArray {
 }
 
 
+private data class RequestContext(
+    val ebicsHost: EbicsHostEntity,
+    val subscriber: EbicsSubscriberEntity,
+    val clientEncPub: RSAPublicKey,
+    val clientAuthPub: RSAPublicKey,
+    val clientSigPub: RSAPublicKey,
+    val hostEncPriv: RSAPrivateCrtKey,
+    val hostAuthPriv: RSAPrivateCrtKey,
+    val requestObject: EbicsRequest,
+    val uploadTransaction: EbicsUploadTransactionEntity?,
+    val downloadTransaction: EbicsDownloadTransactionEntity?
+)
+
+
+private fun handleEbicsDownloadTransactionInitialization(requestContext: 
RequestContext): EbicsResponse {
+    val orderType =
+        requestContext.requestObject.header.static.orderDetails?.orderType ?: 
throw EbicsInvalidRequestError()
+    println("handling initialization for order type $orderType")
+    val response = when (orderType) {
+        "HTD" -> handleEbicsHtd()
+        "HKD" -> handleEbicsHkd()
+        /* Temporarily handling C52/C53 with same logic */
+        "C52" -> handleEbicsC52(requestContext)
+        "C53" -> handleEbicsC52(requestContext)
+        "TSD" -> handleEbicsTSD(requestContext)
+        else -> throw EbicsInvalidXmlError()
+    }
+
+    val transactionID = EbicsOrderUtil.generateTransactionId()
+
+    val compressedResponse = DeflaterInputStream(response.inputStream()).use {
+        it.readAllBytes()
+    }
+
+    val enc = CryptoUtil.encryptEbicsE002(compressedResponse, 
requestContext.clientEncPub)
+    val encodedResponse = Base64.getEncoder().encodeToString(enc.encryptedData)
+
+    val segmentSize = 4096
+    val totalSize = encodedResponse.length
+    val numSegments = ((totalSize + segmentSize - 1) / segmentSize)
+
+    EbicsDownloadTransactionEntity.new(transactionID) {
+        this.subscriber = requestContext.subscriber
+        this.host = requestContext.ebicsHost
+        this.orderType = orderType
+        this.segmentSize = segmentSize
+        this.transactionKeyEnc = SerialBlob(enc.encryptedTransactionKey)
+        this.encodedResponse = encodedResponse
+        this.numSegments = numSegments
+        this.receiptReceived = false
+    }
+    return EbicsResponse.createForDownloadInitializationPhase(
+        transactionID,
+        numSegments,
+        segmentSize,
+        enc,
+        encodedResponse
+    )
+}
+
+
+private fun handleEbicsUploadTransactionInitialization(requestContext: 
RequestContext): EbicsResponse {
+    val orderType =
+        requestContext.requestObject.header.static.orderDetails?.orderType ?: 
throw EbicsInvalidRequestError()
+    val transactionID = EbicsOrderUtil.generateTransactionId()
+    val oidn = requestContext.subscriber.nextOrderID++
+    if (EbicsOrderUtil.checkOrderIDOverflow(oidn)) throw NotImplementedError()
+    val orderID = EbicsOrderUtil.computeOrderIDFromNumber(oidn)
+    val numSegments =
+        requestContext.requestObject.header.static.numSegments ?: throw 
EbicsInvalidRequestError()
+    val transactionKeyEnc =
+        
requestContext.requestObject.body.dataTransfer?.dataEncryptionInfo?.transactionKey
+            ?: throw EbicsInvalidRequestError()
+    val encPubKeyDigest =
+        
requestContext.requestObject.body.dataTransfer?.dataEncryptionInfo?.encryptionPubKeyDigest?.value
+            ?: throw EbicsInvalidRequestError()
+    val encSigData = 
requestContext.requestObject.body.dataTransfer?.signatureData?.value
+        ?: throw EbicsInvalidRequestError()
+    val decryptedSignatureData = CryptoUtil.decryptEbicsE002(
+        CryptoUtil.EncryptionResult(
+            transactionKeyEnc,
+            encPubKeyDigest,
+            encSigData
+        ), requestContext.hostEncPriv
+    )
+    val plainSigData = 
InflaterInputStream(decryptedSignatureData.inputStream()).use {
+        it.readAllBytes()
+    }
+
+    println("creating upload transaction for transactionID $transactionID")
+    EbicsUploadTransactionEntity.new(transactionID) {
+        this.host = requestContext.ebicsHost
+        this.subscriber = requestContext.subscriber
+        this.lastSeenSegment = 0
+        this.orderType = orderType
+        this.orderID = orderID
+        this.numSegments = numSegments.toInt()
+        this.transactionKeyEnc = SerialBlob(transactionKeyEnc)
+    }
+    val sigObj = 
XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8))
+    println("got UserSignatureData: ${plainSigData.toString(Charsets.UTF_8)}")
+    for (sig in sigObj.value.orderSignatureList ?: listOf()) {
+        println("inserting order signature for orderID $orderID and orderType 
$orderType")
+        EbicsOrderSignatureEntity.new {
+            this.orderID = orderID
+            this.orderType = orderType
+            this.partnerID = sig.partnerID
+            this.userID = sig.userID
+            this.signatureAlgorithm = sig.signatureVersion
+            this.signatureValue = SerialBlob(sig.signatureValue)
+        }
+    }
+
+    return EbicsResponse.createForUploadInitializationPhase(transactionID, 
orderID)
+}
+
+
+private fun handleEbicsUploadTransactionTransmission(requestContext: 
RequestContext): EbicsResponse {
+    val uploadTransaction = requestContext.uploadTransaction ?: throw 
EbicsInvalidRequestError()
+    val requestObject = requestContext.requestObject
+    val requestSegmentNumber =
+        
requestContext.requestObject.header.mutable.segmentNumber?.value?.toInt() ?: 
throw EbicsInvalidRequestError()
+    val requestTransactionID = requestObject.header.static.transactionID ?: 
throw EbicsInvalidRequestError()
+    if (requestSegmentNumber == 1 && uploadTransaction.numSegments == 1) {
+        val encOrderData =
+            requestObject.body.dataTransfer?.orderData ?: throw 
EbicsInvalidRequestError()
+        val zippedData = CryptoUtil.decryptEbicsE002(
+            uploadTransaction.transactionKeyEnc.toByteArray(),
+            encOrderData,
+            requestContext.hostEncPriv
+        )
+        val unzippedData =
+            InflaterInputStream(zippedData.inputStream()).use { 
it.readAllBytes() }
+        println("got upload data: ${unzippedData.toString(Charsets.UTF_8)}")
+
+        val sigs  = EbicsOrderSignatureEntity.find {
+            (EbicsOrderSignaturesTable.orderID eq uploadTransaction.orderID) 
and
+                    (EbicsOrderSignaturesTable.orderType eq 
uploadTransaction.orderType)
+        }
+
+        if (sigs.count() == 0) {
+            throw EbicsInvalidRequestError()
+        }
+
+        for (sig in sigs) {
+            if (sig.signatureAlgorithm == "A006") {
+
+                val signedData = CryptoUtil.digestEbicsOrderA006(unzippedData)
+                val res1 = 
CryptoUtil.verifyEbicsA006(sig.signatureValue.toByteArray(), signedData, 
requestContext.clientSigPub)
+
+                if (!res1) {
+                    throw EbicsInvalidRequestError()
+                }
+
+            } else {
+                throw NotImplementedError()
+            }
+        }
+
+        return EbicsResponse.createForUploadTransferPhase(
+            requestTransactionID,
+            requestSegmentNumber,
+            true,
+            uploadTransaction.orderID
+        )
+    } else {
+        throw NotImplementedError()
+    }
+}
+
+private fun makeReqestContext(requestObject: EbicsRequest): RequestContext {
+    val staticHeader = requestObject.header.static
+    val requestedHostId = staticHeader.hostID
+    val ebicsHost =
+        EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq 
requestedHostId.toUpperCase() }
+            .firstOrNull()
+    val requestTransactionID = requestObject.header.static.transactionID
+    var downloadTransaction: EbicsDownloadTransactionEntity? = null
+    var uploadTransaction: EbicsUploadTransactionEntity? = null
+    val subscriber = if (requestTransactionID != null) {
+        println("finding subscriber by transactionID $requestTransactionID")
+        downloadTransaction = 
EbicsDownloadTransactionEntity.findById(requestTransactionID.toUpperCase())
+        if (downloadTransaction != null) {
+            downloadTransaction.subscriber
+        } else {
+            uploadTransaction = 
EbicsUploadTransactionEntity.findById(requestTransactionID)
+            uploadTransaction?.subscriber
+        }
+    } else {
+        val partnerID = staticHeader.partnerID ?: throw 
EbicsInvalidRequestError()
+        val userID = staticHeader.userID ?: throw EbicsInvalidRequestError()
+        findEbicsSubscriber(partnerID, userID, staticHeader.systemID)
+    }
+
+    if (ebicsHost == null) throw EbicsInvalidRequestError()
+
+    /**
+     * NOTE: production logic must check against READY state (the
+     * one activated after the subscriber confirms their keys via post)
+     */
+    if (subscriber == null || subscriber.state != SubscriberState.INITIALIZED)
+        throw EbicsSubscriberStateError()
+
+    val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
+        ebicsHost.authenticationPrivateKey
+            .toByteArray()
+    )
+    val hostEncPriv = CryptoUtil.loadRsaPrivateKey(
+        ebicsHost.encryptionPrivateKey
+            .toByteArray()
+    )
+    val clientAuthPub =
+        
CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
+    val clientEncPub =
+        
CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
+    val clientSigPub =
+        
CryptoUtil.loadRsaPublicKey(subscriber.signatureKey!!.rsaPublicKey.toByteArray())
+
+    return RequestContext(
+        hostAuthPriv = hostAuthPriv,
+        hostEncPriv = hostEncPriv,
+        clientAuthPub = clientAuthPub,
+        clientEncPub = clientEncPub,
+        clientSigPub = clientSigPub,
+        ebicsHost = ebicsHost,
+        requestObject = requestObject,
+        subscriber = subscriber,
+        downloadTransaction = downloadTransaction,
+        uploadTransaction = uploadTransaction
+    )
+}
+
 
 suspend fun ApplicationCall.ebicsweb() {
     val requestDocument = receiveEbicsXml()
@@ -673,229 +901,46 @@ suspend fun ApplicationCall.ebicsweb() {
         "ebicsRequest" -> {
             println("ebicsRequest 
${XMLUtil.convertDomToString(requestDocument)}")
             val requestObject = requestDocument.toObject<EbicsRequest>()
-            val staticHeader = requestObject.header.static
-            val requestedHostId = staticHeader.hostID
 
             val responseXmlStr = transaction {
                 // Step 1 of 3:  Get information about the host and subscriber
 
-                val ebicsHost =
-                    EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() 
eq requestedHostId.toUpperCase() }
-                        .firstOrNull()
-                val requestTransactionID = 
requestObject.header.static.transactionID
-                var downloadTransaction: EbicsDownloadTransactionEntity? = null
-                var uploadTransaction: EbicsUploadTransactionEntity? =
-                    null
-                val subscriber = if (requestTransactionID != null) {
-                    println("finding subscriber by transactionID 
$requestTransactionID")
-                    downloadTransaction = 
EbicsDownloadTransactionEntity.findById(requestTransactionID.toUpperCase())
-                    if (downloadTransaction != null) {
-                        downloadTransaction.subscriber
-                    } else {
-                        uploadTransaction = 
EbicsUploadTransactionEntity.findById(requestTransactionID)
-                        uploadTransaction?.subscriber
-                    }
-                } else {
-                    val partnerID = staticHeader.partnerID ?: throw 
EbicsInvalidRequestError()
-                    val userID = staticHeader.userID ?: throw 
EbicsInvalidRequestError()
-                    findEbicsSubscriber(partnerID, userID, 
staticHeader.systemID)
-                }
-
-                if (ebicsHost == null) throw EbicsInvalidRequestError()
-
-                /**
-                 * NOTE: production logic must check against READY state (the
-                 * one activated after the subscriber confirms their keys via 
post)
-                 */
-                if (subscriber == null || subscriber.state != 
SubscriberState.INITIALIZED)
-                    throw EbicsSubscriberStateError()
-
-                val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
-                    ebicsHost.authenticationPrivateKey
-                        .toByteArray()
-                )
-                val hostEncPriv = CryptoUtil.loadRsaPrivateKey(
-                    ebicsHost.encryptionPrivateKey
-                        .toByteArray()
-                )
-                val clientAuthPub =
-                    
CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
-                val clientEncPub =
-                    
CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
-                val clientSigPub =
-                    
CryptoUtil.loadRsaPublicKey(subscriber.signatureKey!!.rsaPublicKey.toByteArray())
+                val requestContext = makeReqestContext(requestObject)
 
                 // Step 2 of 3:  Validate the signature
-                val verifyResult = 
XMLUtil.verifyEbicsDocument(requestDocument, clientAuthPub)
+                val verifyResult = 
XMLUtil.verifyEbicsDocument(requestDocument, requestContext.clientAuthPub)
                 if (!verifyResult) {
                     throw EbicsInvalidRequestError()
                 }
 
+                // Step 3 of 3:  Generate response
                 val ebicsResponse: EbicsResponse = when 
(requestObject.header.mutable.transactionPhase) {
                     EbicsTypes.TransactionPhaseType.INITIALISATION -> {
-                        val transactionID = 
EbicsOrderUtil.generateTransactionId()
-                        val orderType =
-                            
requestObject.header.static.orderDetails?.orderType ?: throw 
EbicsInvalidRequestError()
-                        if (staticHeader.numSegments == null) {
-                            println("handling initialization for order type 
$orderType")
-                            val response = when (orderType) {
-                                "HTD" -> handleEbicsHtd()
-                                "HKD" -> handleEbicsHkd()
-
-                                /* Temporarily handling C52/C53 with same 
logic */
-                                "C52" -> handleEbicsC52(requestObject.header)
-                                "C53" -> handleEbicsC52(requestObject.header)
-                                else -> throw EbicsInvalidXmlError()
-                            }
-
-                            val compressedResponse = 
DeflaterInputStream(response.inputStream()).use {
-                                it.readAllBytes()
-                            }
-
-                            val enc = 
CryptoUtil.encryptEbicsE002(compressedResponse, clientEncPub)
-                            val encodedResponse = 
Base64.getEncoder().encodeToString(enc.encryptedData)
-
-                            val segmentSize = 4096
-                            val totalSize = encodedResponse.length
-                            val numSegments = ((totalSize + segmentSize - 1) / 
segmentSize)
-
-                            EbicsDownloadTransactionEntity.new(transactionID) {
-                                this.subscriber = subscriber
-                                this.host = ebicsHost
-                                this.orderType = orderType
-                                this.segmentSize = segmentSize
-                                this.transactionKeyEnc = 
SerialBlob(enc.encryptedTransactionKey)
-                                this.encodedResponse = encodedResponse
-                                this.numSegments = numSegments
-                                this.receiptReceived = false
-                            }
-                            EbicsResponse.createForDownloadInitializationPhase(
-                                transactionID,
-                                numSegments,
-                                segmentSize,
-                                enc,
-                                encodedResponse
-                            )
+                        if (requestObject.header.static.numSegments == null) {
+                            
handleEbicsDownloadTransactionInitialization(requestContext)
                         } else {
-                            val oidn = subscriber.nextOrderID++
-                            if (EbicsOrderUtil.checkOrderIDOverflow(oidn)) 
throw NotImplementedError()
-                            val orderID = 
EbicsOrderUtil.computeOrderIDFromNumber(oidn)
-                            val numSegments =
-                                requestObject.header.static.numSegments ?: 
throw EbicsInvalidRequestError()
-                            val transactionKeyEnc =
-                                
requestObject.body.dataTransfer?.dataEncryptionInfo?.transactionKey
-                                    ?: throw EbicsInvalidRequestError()
-                            val encPubKeyDigest =
-                                
requestObject.body.dataTransfer?.dataEncryptionInfo?.encryptionPubKeyDigest?.value
-                            if (encPubKeyDigest == null)
-                                throw EbicsInvalidRequestError()
-                            val encSigData = 
requestObject.body.dataTransfer?.signatureData?.value
-                            if (encSigData == null)
-                                throw EbicsInvalidRequestError()
-                            val decryptedSignatureData = 
CryptoUtil.decryptEbicsE002(
-                                CryptoUtil.EncryptionResult(
-                                    transactionKeyEnc,
-                                    encPubKeyDigest,
-                                    encSigData
-                                ), hostEncPriv
-                            )
-                            val plainSigData = 
InflaterInputStream(decryptedSignatureData.inputStream()).use {
-                                it.readAllBytes()
-                            }
-
-                            println("creating upload transaction for 
transactionID $transactionID")
-                            EbicsUploadTransactionEntity.new(transactionID) {
-                                this.host = ebicsHost
-                                this.subscriber = subscriber
-                                this.lastSeenSegment = 0
-                                this.orderType = orderType
-                                this.orderID = orderID
-                                this.numSegments = numSegments.toInt()
-                                this.transactionKeyEnc = 
SerialBlob(transactionKeyEnc)
-                            }
-                            val sigObj = 
XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8))
-                            println("got UserSignatureData: 
${plainSigData.toString(Charsets.UTF_8)}")
-                            for (sig in sigObj.value.orderSignatureList ?: 
listOf()) {
-                                println("inserting order signature for orderID 
$orderID and orderType $orderType")
-                                EbicsOrderSignatureEntity.new {
-                                    this.orderID = orderID
-                                    this.orderType = orderType
-                                    this.partnerID = sig.partnerID
-                                    this.userID = sig.userID
-                                    this.signatureAlgorithm = 
sig.signatureVersion
-                                    this.signatureValue = 
SerialBlob(sig.signatureValue)
-                                }
-                            }
-
-                            
EbicsResponse.createForUploadInitializationPhase(transactionID, orderID)
+                            
handleEbicsUploadTransactionInitialization(requestContext)
                         }
                     }
                     EbicsTypes.TransactionPhaseType.TRANSFER -> {
-                        requestTransactionID ?: throw 
EbicsInvalidRequestError()
-                        val requestSegmentNumber =
-                            
requestObject.header.mutable.segmentNumber?.value?.toInt() ?: throw 
EbicsInvalidRequestError()
-                        if (uploadTransaction != null) {
-                            if (requestSegmentNumber == 1 && 
uploadTransaction.numSegments == 1) {
-                                val encOrderData =
-                                    requestObject.body.dataTransfer?.orderData 
?: throw EbicsInvalidRequestError()
-                                val zippedData = CryptoUtil.decryptEbicsE002(
-                                    
uploadTransaction.transactionKeyEnc.toByteArray(),
-                                    encOrderData,
-                                    hostEncPriv
-                                )
-                                val unzippedData =
-                                    
InflaterInputStream(zippedData.inputStream()).use { it.readAllBytes() }
-                                println("got upload data: 
${unzippedData.toString(Charsets.UTF_8)}")
-
-                                val sigs  = EbicsOrderSignatureEntity.find {
-                                    (EbicsOrderSignaturesTable.orderID eq 
uploadTransaction.orderID) and
-                                            
(EbicsOrderSignaturesTable.orderType eq uploadTransaction.orderType)
-                                }
-
-                                if (sigs.count() == 0) {
-                                    throw EbicsInvalidRequestError()
-                                }
-
-                                for (sig in sigs) {
-                                    if (sig.signatureAlgorithm == "A006") {
-
-                                        val signedData = 
CryptoUtil.digestEbicsOrderA006(unzippedData)
-                                        val res1 = 
CryptoUtil.verifyEbicsA006(sig.signatureValue.toByteArray(), signedData, 
clientSigPub)
-
-                                        if (!res1) {
-                                            throw EbicsInvalidRequestError()
-                                        }
-
-                                    } else {
-                                        throw NotImplementedError()
-                                    }
-                                }
-
-                                EbicsResponse.createForUploadTransferPhase(
-                                    requestTransactionID,
-                                    requestSegmentNumber,
-                                    true,
-                                    uploadTransaction.orderID
-                                )
-                            } else {
-                                throw NotImplementedError()
-                            }
-                        } else if (downloadTransaction != null) {
+                        if (requestContext.uploadTransaction != null) {
+                            
handleEbicsUploadTransactionTransmission(requestContext)
+                        } else if (requestContext.downloadTransaction != null) 
{
                             throw NotImplementedError()
                         } else {
                             throw AssertionError()
                         }
                     }
                     EbicsTypes.TransactionPhaseType.RECEIPT -> {
-                        requestTransactionID ?: throw 
EbicsInvalidRequestError()
-                        if (downloadTransaction == null)
+                        val requestTransactionID = 
requestObject.header.static.transactionID ?: throw EbicsInvalidRequestError()
+                        if (requestContext.downloadTransaction == null)
                             throw EbicsInvalidRequestError()
                         val receiptCode =
                             requestObject.body.transferReceipt?.receiptCode ?: 
throw EbicsInvalidRequestError()
                         
EbicsResponse.createForDownloadReceiptPhase(requestTransactionID, receiptCode 
== 0)
                     }
                 }
-                signEbicsResponse(ebicsResponse, hostAuthPriv)
+                signEbicsResponse(ebicsResponse, requestContext.hostAuthPriv)
             }
             respondText(responseXmlStr, ContentType.Application.Xml, 
HttpStatusCode.OK)
         }
diff --git a/sandbox/src/main/python/libeufin-cli 
b/sandbox/src/main/python/libeufin-cli
index c621635..9b33bdc 100755
--- a/sandbox/src/main/python/libeufin-cli
+++ b/sandbox/src/main/python/libeufin-cli
@@ -19,7 +19,7 @@ def cli():
 def admin(ctx):
     pass
 
-@admin.command(help="Instruct the Bank to create a new EBICS host ID.")
+@admin.command(help="Instruct the sandbox bank to create a new EBICS host ID.")
 @click.option(
     "--host-id",
     help="EBICS host ID",
@@ -48,7 +48,7 @@ def add_host(obj, host_id, ebics_version, bank_base_url):
 
     print(resp.content.decode("utf-8"))
 
-@admin.command(help="Instruct the Sandbox to create a new EBICS Subscriber")
+@admin.command(help="Instruct the sandbox bank to create a new EBICS 
Subscriber")
 @click.pass_obj
 @click.option(
     "--user-id",
@@ -210,7 +210,7 @@ def backup(obj, account_id, output_file, nexus_base_url):
         return
 
     if response.status_code != 200:
-        print("Unsuccessful status code gotten: 
{}".format(response.status_code))
+        print("Received unsuccessful status code: 
{}".format(response.status_code))
         return
     
     output = open(output_file, "w+")
@@ -220,7 +220,7 @@ def backup(obj, account_id, output_file, nexus_base_url):
     print("Backup stored in {}".format(output_file))
 
 
-@ebics.command(help="Send TST message")
+@ebics.command(help="Send test upload message (TSU)")
 @click.pass_obj
 @click.option(
     "--account-id",
@@ -230,9 +230,138 @@ def backup(obj, account_id, output_file, nexus_base_url):
 @click.argument(
   "nexus-base-url"
 )
-def tst(obj, account_id, nexus_base_url):
+def tsu(obj, account_id, nexus_base_url):
     
-    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendTst".format(account_id))
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendTSU".format(account_id))
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach the nexus")
+        return
+
+    print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send test download message (TSD)")
+@click.pass_obj
+@click.option(
+    "--account-id",
+    help="Numerical ID of the customer at the Nexus",
+    required=True
+)
+@click.argument(
+  "nexus-base-url"
+)
+def tsd(obj, account_id, nexus_base_url):
+    
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendTSD".format(account_id))
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach the nexus")
+        return
+
+    print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send HAA message")
+@click.pass_obj
+@click.option(
+    "--account-id",
+    help="Numerical ID of the customer at the Nexus",
+    required=True
+)
+@click.argument(
+  "nexus-base-url"
+)
+def haa(obj, account_id, nexus_base_url):
+    
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendHAA".format(account_id))
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach the bank")
+        return
+
+    print(resp.content.decode("utf-8"))
+
+@ebics.command(help="Send HVZ message")
+@click.pass_obj
+@click.option(
+    "--account-id",
+    help="Numerical ID of the customer at the Nexus",
+    required=True
+)
+@click.argument(
+  "nexus-base-url"
+)
+def hvz(obj, account_id, nexus_base_url):
+    
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendHVZ".format(account_id))
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach the bank")
+        return
+
+    print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send HVU message")
+@click.pass_obj
+@click.option(
+    "--account-id",
+    help="Numerical ID of the customer at the Nexus",
+    required=True
+)
+@click.argument(
+  "nexus-base-url"
+)
+def hvu(obj, account_id, nexus_base_url):
+    
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendHVU".format(account_id))
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach the bank")
+        return
+
+    print(resp.content.decode("utf-8"))
+
+@ebics.command(help="Send HPD message")
+@click.pass_obj
+@click.option(
+    "--account-id",
+    help="Numerical ID of the customer at the Nexus",
+    required=True
+)
+@click.argument(
+  "nexus-base-url"
+)
+def hpd(obj, account_id, nexus_base_url):
+    
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendHPD".format(account_id))
+    try:
+        resp = post(url)
+    except Exception:
+        print("Could not reach the bank")
+        return
+
+    print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send HKD message")
+@click.pass_obj
+@click.option(
+    "--account-id",
+    help="Numerical ID of the customer at the Nexus",
+    required=True
+)
+@click.argument(
+  "nexus-base-url"
+)
+def hkd(obj, account_id, nexus_base_url):
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendHKD".format(account_id))
     try:
         resp = post(url)
     except Exception:
@@ -364,7 +493,7 @@ def htd(ctx, account_id, prepare, nexus_base_url):
         ctx.invoke(sync)
     url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/sendHtd".format(account_id))
     try:
-        resp = get(url)
+        resp = post(url)
     except Exception:
         print("Could not reach the bank")
         return
diff --git a/util/src/main/kotlin/ebics_h004/EbicsRequest.kt 
b/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
index 3a4cefd..6ae487d 100644
--- a/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
+++ b/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
@@ -6,6 +6,7 @@ import tech.libeufin.util.LOGGER
 import tech.libeufin.util.XMLUtil
 import java.math.BigInteger
 import java.security.interfaces.RSAPublicKey
+import java.util.*
 import javax.xml.bind.annotation.*
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter
 import javax.xml.bind.annotation.adapters.HexBinaryAdapter
@@ -180,6 +181,10 @@ class EbicsRequest {
             XmlElement(
                 name = "StandardOrderParams",
                 type = StandardOrderParams::class
+            ),
+            XmlElement(
+                name = "GenericOrderParams",
+                type = GenericOrderParams::class
             )
         )
         var orderParams: OrderParams? = null
@@ -254,6 +259,13 @@ class EbicsRequest {
         var dateRange: DateRange? = null
     }
 
+    @XmlAccessorType(XmlAccessType.NONE)
+    @XmlType(name = "", propOrder = ["parameterList"])
+    class GenericOrderParams : OrderParams() {
+        @get:XmlElement(type = EbicsTypes.Parameter::class)
+        var parameterList: List<EbicsTypes.Parameter> = LinkedList()
+    }
+
     @XmlAccessorType(XmlAccessType.NONE)
     @XmlType(name = "", propOrder = ["start", "end"])
     class DateRange {
diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt
index 2488118..9db12d8 100644
--- a/util/src/main/kotlin/time.kt
+++ b/util/src/main/kotlin/time.kt
@@ -5,14 +5,14 @@ import javax.xml.datatype.DatatypeFactory
 import javax.xml.datatype.XMLGregorianCalendar
 
 /* now */
-fun getGregorianDate(): XMLGregorianCalendar {
+fun getGregorianCalendarNow(): XMLGregorianCalendar {
     val gregorianCalendar = GregorianCalendar()
     val datatypeFactory = DatatypeFactory.newInstance()
     return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
 }
 
 /* explicit point in time */
-fun getGregorianDate(year: Int, month: Int, day: Int): XMLGregorianCalendar {
+fun getGregorianCalendar(year: Int, month: Int, day: Int): 
XMLGregorianCalendar {
     val gregorianCalendar = GregorianCalendar(year, month, day)
     val datatypeFactory = DatatypeFactory.newInstance()
     return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
diff --git a/util/src/test/kotlin/SignatureDataTest.kt 
b/util/src/test/kotlin/SignatureDataTest.kt
index 323b6bd..c4239cc 100644
--- a/util/src/test/kotlin/SignatureDataTest.kt
+++ b/util/src/test/kotlin/SignatureDataTest.kt
@@ -4,7 +4,7 @@ import tech.libeufin.util.CryptoUtil
 import tech.libeufin.util.XMLUtil
 import tech.libeufin.util.ebics_h004.EbicsRequest
 import tech.libeufin.util.ebics_h004.EbicsTypes
-import tech.libeufin.util.getGregorianDate
+import tech.libeufin.util.getGregorianCalendarNow
 import java.math.BigInteger
 
 class SignatureDataTest {
@@ -22,7 +22,7 @@ class SignatureDataTest {
                 static = EbicsRequest.StaticHeaderType().apply {
                     hostID = "some host ID"
                     nonce = "nonce".toByteArray()
-                    timestamp = getGregorianDate()
+                    timestamp = getGregorianCalendarNow()
                     partnerID = "some partner ID"
                     userID = "some user ID"
                     orderDetails = EbicsRequest.OrderDetails().apply {

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]