libeufin

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

commit 125b6e8d1efe839b7cc575767b812e2e406e296f
parent bef953d9dd4a02910454d5f882077248fc6967e9
Author: Antoine A <>
Date:   Thu, 14 Mar 2024 02:26:38 +0100

Support chunked EBICS upload

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt | 25+++++++++++++------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt | 18+++++++++++-------
2 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt @@ -112,7 +112,7 @@ class EbicsBTS( } fun downloadTransfer( - howManySegments: Int, + nbSegment: Int, segmentNumber: Int, transactionId: String ): ByteArray { @@ -126,7 +126,8 @@ class EbicsBTS( el("mutable") { el("TransactionPhase", "Transfer") el("SegmentNumber") { - attr("lastSegment", if (howManySegments == segmentNumber) "true" else "false") + attr("lastSegment", if (nbSegment == segmentNumber) "true" else "false") + text(segmentNumber.toString()) } } } @@ -160,7 +161,7 @@ class EbicsBTS( /* ----- Upload ----- */ - fun uploadInitialization(preparedUploadData: PreparedUploadData): ByteArray { + fun uploadInitialization(uploadData: PreparedUploadData): ByteArray { val nonce = getNonce(128) return signedRequest { el("header") { @@ -195,7 +196,7 @@ class EbicsBTS( } } bankDigest() - el("NumSegments", "1") // TODO test upload of many segment + el("NumSegments", uploadData.segments.size.toString()) } el("mutable") { @@ -212,15 +213,15 @@ class EbicsBTS( attr("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256") text(CryptoUtil.getEbicsPublicKeyHash(bankKeys.bank_encryption_public_key).encodeBase64()) } - el("TransactionKey", preparedUploadData.transactionKey.encodeBase64()) + el("TransactionKey", uploadData.transactionKey.encodeBase64()) } el("SignatureData") { attr("authenticate", "true") - text(preparedUploadData.userSignatureDataEncrypted.encodeBase64()) + text(uploadData.userSignatureDataEncrypted.encodeBase64()) } el("DataDigest") { attr("SignatureVersion", "A006") - text(preparedUploadData.dataDigest.encodeBase64()) + text(uploadData.dataDigest.encodeBase64()) } } } @@ -229,9 +230,9 @@ class EbicsBTS( fun uploadTransfer( transactionId: String, - uploadData: PreparedUploadData + uploadData: PreparedUploadData, + segmentNumber: Int ): ByteArray { - val chunkIndex = 1 // TODO test upload of many segment return signedRequest { el("header") { attr("authenticate", "true") @@ -242,13 +243,13 @@ class EbicsBTS( el("mutable") { el("TransactionPhase", "Transfer") el("SegmentNumber") { - attr("lastSegment", "true") - text(chunkIndex.toString()) + attr("lastSegment", if (uploadData.segments.size == segmentNumber) "true" else "false") + text(segmentNumber.toString()) } } } el("AuthSignature") - el("body/DataTransfer/OrderData", uploadData.encryptedPayloadChunks[chunkIndex - 1]) + el("body/DataTransfer/OrderData", uploadData.segments[segmentNumber-1]) } } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -301,13 +301,14 @@ fun prepareUploadPayload( bankKeys.bank_encryption_public_key, encryptionResult.plainTransactionKey ) - val encodedEncryptedPayload = encryptedPayload.encryptedData.encodeBase64() + val segment = encryptedPayload.encryptedData.encodeBase64() + // Split 1MB segment when we have payloads that big return PreparedUploadData( encryptionResult.encryptedTransactionKey, // ephemeral key encryptionResult.encryptedData, // bank-pub-encrypted A006 signature. CryptoUtil.digestEbicsOrderA006(payload), // used by EBICS 3 - listOf(encodedEncryptedPayload) // actual payload E002 encrypted. + listOf(segment) // actual payload E002 encrypted. ) } @@ -341,12 +342,14 @@ suspend fun doEbicsUpload( val tId = requireNotNull(initResp.transactionID) { "Upload init phase: missing transaction ID" } + val orderId = requireNotNull(initResp.orderID) { + "Upload init phase: missing order ID" + } // Transfer phase - val transferXml = impl.uploadTransfer(tId, preparedPayload) - val transferResp = impl.postBTS(client, transferXml, "Upload transfer phase").okOrFail("Upload transfer phase") - val orderId = requireNotNull(transferResp.orderID) { - "Upload transfer phase: missing order ID" + for (i in 1..preparedPayload.segments.size) { + val transferXml = impl.uploadTransfer(tId, preparedPayload, i) + val transferResp = impl.postBTS(client, transferXml, "Upload transfer phase").okOrFail("Upload transfer phase") } orderId } @@ -365,7 +368,7 @@ class PreparedUploadData( val transactionKey: ByteArray, val userSignatureDataEncrypted: ByteArray, val dataDigest: ByteArray, - val encryptedPayloadChunks: List<String> + val segments: List<String> ) class DataEncryptionInfo( @@ -412,6 +415,7 @@ enum class EbicsReturnCode(val code: String) { EBICS_UNSUPPORTED_ORDER_TYPE("091006"), EBICS_INVALID_XML("091010"), EBICS_TX_MESSAGE_REPLAY("091103"), + EBICS_TX_SEGMENT_NUMBER_EXCEEDED("091104"), EBICS_INVALID_REQUEST_CONTENT("091113"), EBICS_PROCESSING_ERROR("091116"), EBICS_ACCOUNT_AUTHORISATION_FAILED("091302"),