gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: ebics transactions WIP


From: gnunet
Subject: [libeufin] branch master updated: ebics transactions WIP
Date: Thu, 07 Nov 2019 14:37:57 +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 d1e60b1  ebics transactions WIP
d1e60b1 is described below

commit d1e60b16cb9fef51729a5d0fed9d09c56b5f8836
Author: Florian Dold <address@hidden>
AuthorDate: Thu Nov 7 14:37:47 2019 +0100

    ebics transactions WIP
---
 .idea/uiDesigner.xml                               | 124 +++++++++
 .../kotlin/tech/libeufin/sandbox/CryptoUtil.kt     |   6 -
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |  17 +-
 .../kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt |  17 ++
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 284 ++++++++++++++++++++-
 .../main/kotlin/tech/libeufin/sandbox/XMLUtil.kt   |  21 +-
 .../kotlin/tech/libeufin/sandbox/XmlBinding.kt     |  23 ++
 .../src/main/kotlin/tech/libeufin/sandbox/hex.kt   |   7 +
 .../libeufin/schema/ebics_h004/EbicsResponse.kt    |  11 +-
 .../tech/libeufin/schema/ebics_h004/EbicsTypes.kt  |   5 +-
 sandbox/src/test/kotlin/XmlUtilTest.kt             |  16 ++
 11 files changed, 490 insertions(+), 41 deletions(-)

diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal 
Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" 
fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical 
Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" 
fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" 
icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" 
fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" 
icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" 
auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" 
fill="3" />
+      </item>
+      <item class="javax.swing.JButton" 
icon="/com/intellij/uiDesigner/icons/button.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" 
fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" 
icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" 
fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" 
icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" 
fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" 
icon="/com/intellij/uiDesigner/icons/label.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" 
fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" 
icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" 
fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" 
icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" 
fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" 
icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" 
fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" 
icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" 
fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" 
icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" 
fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" 
icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" 
fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" 
icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" 
fill="1" />
+      </item>
+      <item class="javax.swing.JTable" 
icon="/com/intellij/uiDesigner/icons/table.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" 
fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" 
icon="/com/intellij/uiDesigner/icons/list.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" 
fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" 
icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" 
fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" 
icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" 
fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" 
icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" 
fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" 
icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" 
auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" 
fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" 
icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" 
fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" 
icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" 
fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" 
icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" 
fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" 
icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" 
fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" 
icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" 
auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" 
fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" 
icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" 
auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" 
fill="2" />
+      </item>
+    </group>
+  </component>
+</project>
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
index adfa371..db4eee5 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
@@ -164,11 +164,5 @@ class CryptoUtil {
             val data = symmetricCipher.doFinal(enc.encryptedData)
             return data
         }
-
-        fun ByteArray.toHexString() : String {
-            return this.joinToString("") {
-                java.lang.String.format("%02x", it)
-            }
-        }
     }
 }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 74a2475..8e1d678 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -154,6 +154,8 @@ object EbicsSubscribersTable : IntIdTable() {
     val encryptionKey = reference("encryptionKey", 
EbicsSubscriberPublicKeysTable).nullable()
     val authenticationKey = reference("authorizationKey", 
EbicsSubscriberPublicKeysTable).nullable()
 
+    val nextOrderID = integer("nextOrderID")
+
     val state = enumeration("state", SubscriberState::class)
 }
 
@@ -168,6 +170,8 @@ class EbicsSubscriberEntity(id: EntityID<Int>) : 
IntEntity(id) {
     var encryptionKey by EbicsSubscriberPublicKeyEntity optionalReferencedOn 
EbicsSubscribersTable.encryptionKey
     var authenticationKey by EbicsSubscriberPublicKeyEntity 
optionalReferencedOn EbicsSubscribersTable.authenticationKey
 
+    var nextOrderID by EbicsSubscribersTable.nextOrderID
+
     var state by EbicsSubscribersTable.state
 }
 
@@ -177,8 +181,8 @@ object EbicsDownloadTransactionsTable : IdTable<String>() {
     val orderType = text("orderType")
     val host = reference("host", EbicsHostsTable)
     val subscriber = reference("subscriber", EbicsSubscribersTable)
-    val encodedResponse = blob("encodedResponse")
-    val orderID = text("orderID")
+    val encodedResponse = text("encodedResponse")
+    val transactionKeyEnc = blob("transactionKeyEnc")
     val numSegments = integer("numSegments")
     val segmentSize = integer("segmentSize")
     val receiptReceived = bool("receiptReceived")
@@ -189,11 +193,11 @@ class EbicsDownloadTransactionEntity(id: 
EntityID<String>) : Entity<String>(id)
     companion object : EntityClass<String, 
EbicsDownloadTransactionEntity>(EbicsDownloadTransactionsTable)
 
     var orderType by EbicsDownloadTransactionsTable.orderType
-    var host by EbicsDownloadTransactionsTable.host
-    var subscriber by EbicsDownloadTransactionsTable.host
+    var host by EbicsHostEntity referencedOn 
EbicsDownloadTransactionsTable.host
+    var subscriber by EbicsSubscriberEntity referencedOn 
EbicsDownloadTransactionsTable.subscriber
     var encodedResponse by EbicsDownloadTransactionsTable.encodedResponse
-    var orderID by EbicsDownloadTransactionsTable.orderID
     var numSegments by EbicsDownloadTransactionsTable.numSegments
+    var transactionKeyEnc by EbicsDownloadTransactionsTable.transactionKeyEnc
     var segmentSize by EbicsDownloadTransactionsTable.segmentSize
     var receiptReceived by EbicsDownloadTransactionsTable.receiptReceived
 }
@@ -208,7 +212,8 @@ fun dbCreateTables() {
         SchemaUtils.create(
             BankCustomersTable,
             EbicsSubscribersTable,
-            EbicsHostsTable
+            EbicsHostsTable,
+            EbicsDownloadTransactionsTable
         )
     }
 }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
index 2763afe..5652011 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
@@ -19,6 +19,7 @@
 
 package tech.libeufin.sandbox
 
+import java.security.SecureRandom
 import java.util.zip.DeflaterInputStream
 import java.util.zip.InflaterInputStream
 
@@ -40,5 +41,21 @@ class EbicsOrderUtil private constructor() {
                 it.readAllBytes()
             }
         }
+
+        fun generateTransactionId(): String {
+            val rng = SecureRandom()
+            val res = ByteArray(16)
+            rng.nextBytes(res)
+            return res.toHexString()
+        }
+
+        /**
+         * Calculate the resulting size of base64-encoding data of the given 
length,
+         * including padding.
+         */
+        fun calculateBase64EncodedLength(dataLength: Int): Int {
+            val blocks = (dataLength + 3 - 1) / 3
+            return blocks * 4
+        }
     }
 }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 11db448..435a4e8 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -40,6 +40,7 @@ import io.ktor.routing.routing
 import io.ktor.server.engine.embeddedServer
 import io.ktor.server.netty.Netty
 import org.apache.xml.security.binding.xmldsig.RSAKeyValueType
+import org.apache.xml.security.binding.xmldsig.SignatureType
 import org.jetbrains.exposed.sql.and
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.slf4j.Logger
@@ -49,20 +50,28 @@ import tech.libeufin.schema.ebics_h004.*
 import tech.libeufin.schema.ebics_hev.HEVResponse
 import tech.libeufin.schema.ebics_hev.SystemReturnCodeType
 import tech.libeufin.schema.ebics_s001.SignaturePubKeyOrderData
+import java.math.BigInteger
 import java.security.interfaces.RSAPublicKey
 import java.text.DateFormat
+import java.util.*
+import java.util.zip.DeflaterInputStream
 import javax.sql.rowset.serial.SerialBlob
 import javax.xml.bind.JAXBContext
 
 val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
 
-data class EbicsRequestError(val statusCode: HttpStatusCode) : 
Exception("Ebics request error")
+open class EbicsRequestError(val errorText: String, val errorCode: String) :
+    Exception("EBICS request management error: $errorText ($errorCode)")
+
+class EbicsInvalidRequestError : EbicsRequestError("[EBICS_INVALID_REQUEST] 
Invalid request", "060102")
 
 open class EbicsKeyManagementError(val errorText: String, val errorCode: 
String) :
     Exception("EBICS key management error: $errorText ($errorCode)")
 
 class EbicsInvalidXmlError : EbicsKeyManagementError("[EBICS_INVALID_XML]", 
"091010")
 
+class EbicsInvalidOrderType : 
EbicsRequestError("[EBICS_UNSUPPORTED_ORDER_TYPE] Order type not supported", 
"091005")
+
 private suspend fun ApplicationCall.respondEbicsKeyManagement(
     errorText: String,
     errorCode: String,
@@ -158,7 +167,7 @@ private suspend fun ApplicationCall.handleEbicsHia(header: 
EbicsUnsecuredRequest
         val ebicsSubscriber = findEbicsSubscriber(header.static.partnerID, 
header.static.userID, header.static.systemID)
         if (ebicsSubscriber == null) {
             logger.warn("ebics subscriber not found")
-            throw EbicsRequestError(HttpStatusCode.NotFound)
+            throw EbicsInvalidRequestError()
         }
         ebicsSubscriber.authenticationKey = EbicsSubscriberPublicKeyEntity.new 
{
             this.rsaPublicKey = SerialBlob(authPub.encoded)
@@ -188,7 +197,7 @@ private suspend fun ApplicationCall.handleEbicsIni(header: 
EbicsUnsecuredRequest
             findEbicsSubscriber(header.static.partnerID, header.static.userID, 
header.static.systemID)
         if (ebicsSubscriber == null) {
             logger.warn("ebics subscriber ('${header.static.partnerID}' / 
'${header.static.userID}' / '${header.static.systemID}') not found")
-            throw EbicsRequestError(HttpStatusCode.NotFound)
+            throw EbicsInvalidRequestError()
         }
         ebicsSubscriber.signatureKey = EbicsSubscriberPublicKeyEntity.new {
             this.rsaPublicKey = SerialBlob(sigPub.encoded)
@@ -213,10 +222,10 @@ private suspend fun ApplicationCall.handleEbicsHpb(
         val ebicsSubscriber =
             findEbicsSubscriber(header.static.partnerID, header.static.userID, 
header.static.systemID)
         if (ebicsSubscriber == null) {
-            throw EbicsRequestError(HttpStatusCode.Unauthorized)
+            throw EbicsInvalidRequestError()
         }
         if (ebicsSubscriber.state != SubscriberState.INITIALIZED) {
-            throw EbicsRequestError(HttpStatusCode.Forbidden)
+            throw EbicsInvalidRequestError()
         }
         val authPubBlob = ebicsSubscriber.authenticationKey!!.rsaPublicKey
         val encPubBlob = ebicsSubscriber.encryptionKey!!.rsaPublicKey
@@ -301,6 +310,176 @@ inline fun <reified T> Document.toObject(): T {
 }
 
 
+fun handleEbicsHtd(): ByteArray {
+    val htd = HTDResponseOrderData().apply {
+        this.partnerInfo = HTDResponseOrderData.PartnerInfo().apply {
+            this.accountInfoList = listOf(
+                HTDResponseOrderData.AccountInfo().apply {
+                    this.id = "acctid1"
+                    this.accountHolder = "Mina Musterfrau"
+                    this.accountNumberList = listOf(
+                        HTDResponseOrderData.GeneralAccountNumber().apply {
+                            this.international = true
+                            this.value = "DE21500105174751659277"
+                        }
+                    )
+                    this.currency = "EUR"
+                    this.description = "ACCT"
+                    this.bankCodeList = listOf(
+                        HTDResponseOrderData.GeneralBankCode().apply {
+                            this.international = true
+                            this.value = "INGDDEFFXXX"
+                        }
+                    )
+                }
+            )
+            this.addressInfo = HTDResponseOrderData.AddressInfo().apply {
+                this.name = "Foo"
+            }
+            this.bankInfo = HTDResponseOrderData.BankInfo().apply {
+                this.hostID = "host01"
+            }
+            this.orderInfoList = listOf(
+                HTDResponseOrderData.AuthOrderInfoType().apply {
+                    this.description = "foo"
+                    this.orderType = "C53"
+                    this.transferType = "Download"
+                },
+                HTDResponseOrderData.AuthOrderInfoType().apply {
+                    this.description = "foo"
+                    this.orderType = "C52"
+                    this.transferType = "Download"
+                },
+                HTDResponseOrderData.AuthOrderInfoType().apply {
+                    this.description = "foo"
+                    this.orderType = "CCC"
+                    this.transferType = "Upload"
+                }
+            )
+        }
+        this.userInfo = HTDResponseOrderData.UserInfo().apply {
+            this.name = "Some User"
+            this.userID = HTDResponseOrderData.UserIDType().apply {
+                this.status = 5
+                this.value = "USER1"
+            }
+            this.permissionList = listOf(
+                HTDResponseOrderData.UserPermission().apply {
+                    this.orderTypes = "C54 C53 C52 CCC"
+                }
+            )
+        }
+    }
+
+    val str = XMLUtil.convertJaxbToString(htd)
+    return str.toByteArray()
+}
+
+
+fun createEbicsResponseForDownloadInitializationPhase(
+    transactionID: String,
+    numSegments: Int,
+    segmentSize: Int,
+    enc: CryptoUtil.EncryptionResult,
+    encodedData: String
+): EbicsResponse {
+    return EbicsResponse().apply {
+        this.version = "H004"
+        this.revision = 1
+        this.header = EbicsResponse.Header().apply {
+            this.authenticate = true
+            this._static = EbicsResponse.StaticHeaderType().apply {
+                this.transactionID = transactionID
+                this.numSegments = BigInteger.valueOf(numSegments.toLong())
+            }
+            this.mutable = EbicsResponse.MutableHeaderType().apply {
+                this.transactionPhase = 
EbicsTypes.TransactionPhaseType.INITIALISATION
+                this.segmentNumber = EbicsResponse.SegmentNumber().apply {
+                    this.lastSegment = (numSegments == 1)
+                    this.value = BigInteger.valueOf(1)
+                }
+                this.reportText = "[EBICS_OK] OK"
+                this.returnCode = "000000"
+            }
+        }
+        this.authSignature = SignatureType()
+        this.body = EbicsResponse.Body().apply {
+            this.returnCode = EbicsResponse.ReturnCode().apply {
+                this.authenticate = true
+                this.value = "000000"
+            }
+            this.dataTransfer = EbicsResponse.DataTransferResponseType().apply 
{
+                this.dataEncryptionInfo = 
EbicsTypes.DataEncryptionInfo().apply {
+                    this.authenticate = true
+                    this.encryptionPubKeyDigest = 
EbicsTypes.PubKeyDigest().apply {
+                        this.algorithm = 
"http://www.w3.org/2001/04/xmlenc#sha256";
+                        this.version = "E002"
+                        this.value = enc.pubKeyDigest
+                    }
+                    this.transactionKey = enc.encryptedTransactionKey
+                }
+                this.orderData = EbicsResponse.OrderData().apply {
+                    this.value = encodedData.substring(0, 
Math.min(segmentSize, encodedData.length))
+                }
+            }
+        }
+    }
+}
+
+
+fun createEbicsResponseForDownloadTransferPhase() {
+
+}
+
+
+fun createEbicsResponseForDownloadReceiptPhase(transactionID: String, 
positiveAck: Boolean): EbicsResponse {
+    return EbicsResponse().apply {
+        this.version = "H004"
+        this.revision = 1
+        this.header = EbicsResponse.Header().apply {
+            this.authenticate = true
+            this._static = EbicsResponse.StaticHeaderType().apply {
+                this.transactionID = transactionID
+            }
+            this.mutable = EbicsResponse.MutableHeaderType().apply {
+                this.transactionPhase = EbicsTypes.TransactionPhaseType.RECEIPT
+                if (positiveAck) {
+                    this.reportText = "[EBICS_DOWNLOAD_POSTPROCESS_DONE] 
Received positive receipt"
+                    this.returnCode = "011000"
+                } else {
+                    this.reportText = "[EBICS_DOWNLOAD_POSTPROCESS_DONE] 
Received negative receipt"
+                    this.returnCode = "011001"
+                }
+            }
+        }
+        this.authSignature = SignatureType()
+        this.body = EbicsResponse.Body().apply {
+            this.returnCode = EbicsResponse.ReturnCode().apply {
+                this.authenticate = true
+                this.value = "000000"
+            }
+        }
+    }
+}
+
+
+private suspend fun ApplicationCall.handleEbicsDownloadInitialization() {
+
+}
+
+private suspend fun ApplicationCall.handleEbicsDownloadTransfer() {
+
+}
+
+private suspend fun ApplicationCall.handleEbicsDownloadReceipt() {
+
+}
+
+private suspend fun ApplicationCall.handleEbicsUploadInitialization() {
+
+}
+
+
 private suspend fun ApplicationCall.ebicsweb() {
     val requestDocument = receiveEbicsXml()
 
@@ -344,24 +523,111 @@ private suspend fun ApplicationCall.ebicsweb() {
             println("ebicsRequest 
${XMLUtil.convertDomToString(requestDocument)}")
             val requestObject = requestDocument.toObject<EbicsRequest>()
             val staticHeader = requestObject.header.static
+
             when (requestObject.header.mutable.transactionPhase) {
                 EbicsTypes.TransactionPhaseType.INITIALISATION -> {
                     val partnerID = staticHeader.partnerID ?: throw 
EbicsInvalidXmlError()
                     val userID = staticHeader.userID ?: throw 
EbicsInvalidXmlError()
-                    transaction {
+                    val respText = transaction {
                         val subscriber =
                             findEbicsSubscriber(partnerID, userID, 
staticHeader.systemID)
                                 ?: throw EbicsInvalidXmlError()
-                        val authPub =
+                        val requestedHostId = 
requestObject.header.static.hostID
+                        val ebicsHost = EbicsHostEntity.find { 
EbicsHostsTable.hostID eq requestedHostId }.firstOrNull()
+                        if (ebicsHost == null)
+                            throw EbicsInvalidRequestError()
+                        val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
+                            ebicsHost.authenticationPrivateKey
+                                .toByteArray()
+                        )
+                        val clientAuthPub =
                             
CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
-                        val verifyResult = 
XMLUtil.verifyEbicsDocument(requestDocument, authPub)
+                        val clientEncPub =
+                            
CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
+                        val verifyResult = 
XMLUtil.verifyEbicsDocument(requestDocument, clientAuthPub)
                         println("ebicsRequest verification result: 
$verifyResult")
+                        val transactionID = 
EbicsOrderUtil.generateTransactionId()
+                        val orderType = 
requestObject.header.static.orderDetails?.orderType
+
+                        val response = when (orderType) {
+                            "HTD" -> handleEbicsHtd()
+                            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)
+
+                        println("inner response: " + 
response.toString(Charsets.UTF_8))
+
+                        println("total size: $totalSize")
+                        println("num segments: $numSegments")
+
+                        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
+                        }
+
+                        val ebicsResponse = 
createEbicsResponseForDownloadInitializationPhase(
+                            transactionID,
+                            numSegments, segmentSize, enc, encodedResponse
+                        )
+                        val docText = 
XMLUtil.convertJaxbToString(ebicsResponse)
+                        val doc = XMLUtil.parseStringIntoDom(docText)
+                        XMLUtil.signEbicsDocument(doc, hostAuthPriv)
+                        val signedDoc = XMLUtil.convertDomToString(doc)
+                        println("response: $signedDoc")
+                        docText
                     }
+                    respondText(respText, ContentType.Application.Xml, 
HttpStatusCode.OK)
+                    return
                 }
                 EbicsTypes.TransactionPhaseType.TRANSFER -> {
 
                 }
                 EbicsTypes.TransactionPhaseType.RECEIPT -> {
+                    val respText = transaction {
+                        val requestedHostId = 
requestObject.header.static.hostID
+                        val ebicsHost = EbicsHostEntity.find { 
EbicsHostsTable.hostID eq requestedHostId }.firstOrNull()
+                        if (ebicsHost == null)
+                            throw EbicsInvalidRequestError()
+                        val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
+                            ebicsHost.authenticationPrivateKey
+                                .toByteArray()
+                        )
+                        val transactionID = 
requestObject.header.static.transactionID
+                        if (transactionID == null)
+                            throw EbicsInvalidRequestError()
+                        val downloadTransaction = 
EbicsDownloadTransactionEntity.findById(transactionID)
+                        if (downloadTransaction == null)
+                            throw EbicsInvalidRequestError()
+                        println("sending receipt for transaction ID 
$transactionID")
+                        val receiptCode = 
requestObject.body.transferReceipt?.receiptCode
+                        if (receiptCode == null)
+                            throw EbicsInvalidRequestError()
+                        val ebicsResponse = 
createEbicsResponseForDownloadReceiptPhase(transactionID, receiptCode == 0)
+                        val docText = 
XMLUtil.convertJaxbToString(ebicsResponse)
+                        val doc = XMLUtil.parseStringIntoDom(docText)
+                        XMLUtil.signEbicsDocument(doc, hostAuthPriv)
+                        val signedDoc = XMLUtil.convertDomToString(doc)
+                        println("response: $signedDoc")
+                        docText
+                    }
+                    respondText(respText, ContentType.Application.Xml, 
HttpStatusCode.OK)
+                    return
 
                 }
             }
@@ -397,6 +663,7 @@ fun main() {
             userId = "USER1"
             systemId = null
             state = SubscriberState.NEW
+            nextOrderID = 1
         }
     }
 
@@ -414,6 +681,7 @@ fun main() {
                 call.respondText("Internal server error.", 
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
             }
         }
+        // TODO: add another intercept call that adds schema validation before 
the response is sent
         intercept(ApplicationCallPipeline.Fallback) {
             if (this.call.response.status() == null) {
                 call.respondText("Not found (no route matched).\n", 
ContentType.Text.Plain, HttpStatusCode.NotFound)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
index 5eb8bc8..436375c 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
@@ -74,24 +74,20 @@ class XMLUtil private constructor() {
             if (myRef.uri != "#xpointer($ebicsXpathExpr)")
                 throw Exception("invalid EBICS XML signature URI: 
'${myRef.uri}'")
             val xp: XPath = XPathFactory.newInstance().newXPath()
-            val nodeSet = 
xp.compile(ebicsXpathExpr).evaluate(myRef.here.ownerDocument, 
XPathConstants.NODESET)
+            val nodeSet = 
xp.compile("//*[@authenticate='true']/descendant-or-self::node()").evaluate(myRef.here
+                .ownerDocument, XPathConstants
+                .NODESET)
             if (nodeSet !is NodeList)
                 throw Exception("invalid type")
             if (nodeSet.length <= 0) {
                 throw Exception("no nodes to sign")
             }
-            val bytes = ByteArrayOutputStream()
+            val nodeList = ArrayList<Node>()
             for (i in 0 until nodeSet.length) {
                 val node = nodeSet.item(i)
-                org.apache.xml.security.Init.init()
-                // Despite the transform later, this canonicalization step is 
absolutely necessary,
-                // as the canonicalizeSubtree method preserves namespaces that 
are not in the subtree
-                // being canonicalized, but in the parent hierarchy of the 
document.
-                val canon: Canonicalizer = 
Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)
-                val cxml = canon.canonicalizeSubtree(node)
-                bytes.writeBytes(cxml)
+                nodeList.add(node)
             }
-            return OctetStreamData(ByteArrayInputStream(bytes.toByteArray()))
+            return NodeSetData { nodeList.iterator() }
         }
     }
 
@@ -336,8 +332,12 @@ class XMLUtil private constructor() {
             dsc.defaultNamespacePrefix = "ds"
             dsc.uriDereferencer = EbicsSigUriDereferencer()
 
+            dsc.setProperty("javax.xml.crypto.dsig.cacheReference", true)
+
             sig.sign(dsc)
 
+            println("canon data: " + 
sig.signedInfo.canonicalizedData.readAllBytes().toString(Charsets.UTF_8))
+
             val innerSig = authSigNode.firstChild
             while (innerSig.hasChildNodes()) {
                 authSigNode.appendChild(innerSig.firstChild)
@@ -375,6 +375,7 @@ class XMLUtil private constructor() {
             authSigNode.parentNode.removeChild(authSigNode)
             val fac = XMLSignatureFactory.getInstance("DOM")
             val dvc = DOMValidateContext(signingPub, sigEl)
+            dvc.setProperty("javax.xml.crypto.dsig.cacheReference", true)
             dvc.uriDereferencer = EbicsSigUriDereferencer()
             val sig = fac.unmarshalXMLSignature(dvc)
             // FIXME: check that parameters are okay!s
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XmlBinding.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XmlBinding.kt
new file mode 100644
index 0000000..79a77ca
--- /dev/null
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XmlBinding.kt
@@ -0,0 +1,23 @@
+package tech.libeufin.sandbox
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlSchemaContext
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlElement
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlAttribute
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlValue
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlWrapper
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlAdapter
+
+class XmlBinding<T> {
+
+}
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/hex.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/hex.kt
new file mode 100644
index 0000000..11b4afb
--- /dev/null
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/hex.kt
@@ -0,0 +1,7 @@
+package tech.libeufin.sandbox
+
+fun ByteArray.toHexString() : String {
+    return this.joinToString("") {
+        java.lang.String.format("%02x", it)
+    }
+}
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt 
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
index 32ace05..4ef5d2e 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
@@ -29,9 +29,6 @@ class EbicsResponse {
     @get:XmlElement(required = true)
     lateinit var body: Body
 
-    @get:XmlAnyAttribute
-    var otherAttributes = HashMap<QName, String>()
-
     @XmlAccessorType(XmlAccessType.NONE)
     @XmlType(name = "", propOrder = ["_static", "mutable"])
     class Header {
@@ -101,7 +98,7 @@ class EbicsResponse {
     @XmlAccessorType(XmlAccessType.NONE)
     class OrderData {
         @get:XmlValue
-        lateinit var value: ByteArray
+        lateinit var value: String
     }
 
     @XmlAccessorType(XmlAccessType.NONE)
@@ -128,10 +125,8 @@ class EbicsResponse {
     @XmlAccessorType(XmlAccessType.NONE)
     @XmlType(name = "ResponseStaticHeaderType", propOrder = ["transactionID", 
"numSegments"])
     class StaticHeaderType {
-        @get:XmlElement(name = "TransactionID", type = String::class)
-        @get:XmlJavaTypeAdapter(HexBinaryAdapter::class)
-        @get:XmlSchemaType(name = "hexBinary")
-        var transactionID: ByteArray? = null
+        @get:XmlElement(name = "TransactionID")
+        var transactionID: String? = null
 
         @get:XmlElement(name = "NumSegments")
         @get:XmlSchemaType(name = "positiveInteger")
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt 
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt
index ce9f543..b8b7b2c 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt
@@ -52,6 +52,7 @@ class EbicsTypes private constructor() {
     }
 
 
+    @XmlType(name = "", propOrder = ["encryptionPubKeyDigest", 
"transactionKey"])
     @XmlAccessorType(XmlAccessType.NONE)
     class DataEncryptionInfo {
         @get:XmlAttribute(name = "authenticate", required = true)
@@ -62,12 +63,10 @@ class EbicsTypes private constructor() {
 
         @get:XmlElement(name = "TransactionKey", required = true)
         lateinit var transactionKey: ByteArray
-
-        @get:XmlAnyElement(lax = true)
-        var any: List<Any>? = null
     }
 
     @XmlAccessorType(XmlAccessType.NONE)
+    @XmlType(name = "", propOrder = ["value"])
     class PubKeyDigest {
         /**
          * Version of the *digest* of the public key.
diff --git a/sandbox/src/test/kotlin/XmlUtilTest.kt 
b/sandbox/src/test/kotlin/XmlUtilTest.kt
index 0bf182f..9661e37 100644
--- a/sandbox/src/test/kotlin/XmlUtilTest.kt
+++ b/sandbox/src/test/kotlin/XmlUtilTest.kt
@@ -56,6 +56,22 @@ class XmlUtilTest {
         kotlin.test.assertFalse(XMLUtil.verifyEbicsDocument(doc, 
otherPair.public))
     }
 
+    @Test
+    fun multiAuthSigningTest() {
+        val doc = XMLUtil.parseStringIntoDom("""
+            <myMessage xmlns:ebics="urn:org:ebics:H004">
+                <ebics:AuthSignature />
+                <foo authenticate="true">Hello World</foo>
+                <bar authenticate="true">Another one!</bar>
+            </myMessage>
+        """.trimIndent())
+        val kpg = KeyPairGenerator.getInstance("RSA")
+        kpg.initialize(2048)
+        val pair = kpg.genKeyPair()
+        XMLUtil.signEbicsDocument(doc, pair.private)
+        kotlin.test.assertTrue(XMLUtil.verifyEbicsDocument(doc, pair.public))
+    }
+
     @Test
     fun testRefSignature() {
         val classLoader = ClassLoader.getSystemClassLoader()

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



reply via email to

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