From 231bf41cb33e743e0d00af81e988ed057c0b8685 Mon Sep 17 00:00:00 2001 From: dankito Date: Sat, 16 May 2020 17:55:01 +0200 Subject: [PATCH] Fixed that challengeHHD_UC sometimes got decoded wrong, there was then a byte missing as binary data got unmasked --- .../dankito/fints/response/ResponseParser.kt | 2 +- .../net/dankito/fints/tan/TanImageDecoder.kt | 6 ++--- .../kotlin/net/dankito/fints/FinTsTestBase.kt | 18 ++++++++++++++ .../fints/response/ResponseParserTest.kt | 23 ++++++++++++++++++ .../Mt940AccountTransactionsParserTest.kt | 11 +++------ .../fints/transactions/Mt940ParserTest.kt | 6 +---- ..._TanChallengeHhdUc_WithMaskedCharacter.txt | Bin 0 -> 4207 bytes 7 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 fints4k/src/test/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt diff --git a/fints4k/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt b/fints4k/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt index fc7bff78..ebe2f568 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt @@ -491,7 +491,7 @@ open class ResponseParser @JvmOverloads constructor( protected open fun parseTanResponse(segment: String, dataElementGroups: List): TanResponse { val binaryJobHashValue = if (dataElementGroups.size > 2) parseStringToNullIfEmpty(dataElementGroups[2]) else null - val binaryChallengeHHD_UC = if (dataElementGroups.size > 5) parseStringToNullIfEmpty(dataElementGroups[5]) else null + val binaryChallengeHHD_UC = if (dataElementGroups.size > 5 && dataElementGroups[5].isNotEmpty()) dataElementGroups[5] else null return TanResponse( parseCodeEnum(dataElementGroups[1], TanProcess.values()), diff --git a/fints4k/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt b/fints4k/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt index 95ed120c..ebd9a128 100644 --- a/fints4k/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt +++ b/fints4k/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt @@ -20,11 +20,11 @@ open class TanImageDecoder { val mimeType = challengeHHD_UC.substring(2, mimeTypeEnd) - val imageLength = getLength(bytes[mimeTypeEnd], bytes[mimeTypeEnd + 1]) val imageStart = mimeTypeEnd + 2 - val imageString = challengeHHD_UC.substring(imageStart, imageStart + imageLength) - val imageBytes = imageString.toByteArray(HbciCharset.DefaultCharset) + // sometimes it happened that imageStart + getLength(bytes[mimeTypeEnd], bytes[mimeTypeEnd + 1]) + // was greater than challengeHHD_UC.length + 1 -> ignore image length and simply return all bytes starting with imageStart + val imageBytes = bytes.copyOfRange(imageStart, bytes.size) return TanImage(mimeType, imageBytes) } catch (e: Exception) { diff --git a/fints4k/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt b/fints4k/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt index 32ca4318..323218c9 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt @@ -8,13 +8,21 @@ import net.dankito.fints.model.* import net.dankito.fints.response.segments.AccountType import net.dankito.fints.response.segments.ChangeTanMediaParameters import net.dankito.fints.response.segments.JobParameters +import java.io.File import java.math.BigDecimal +import java.nio.charset.Charset import java.util.* abstract class FinTsTestBase { companion object { + + const val TestFilesFolderName = "test_files/" + + const val TransactionsMt940Filename = "TransactionsMt940.txt" + + const val BankCode = "12345678" const val BankCountryCode = Laenderkennzeichen.Germany @@ -87,4 +95,14 @@ abstract class FinTsTestBase { return JobParameters("", 1, 1, 1, ":0:0") } + + /** + * testFilename has to be a file in src/test/resources/test_files/ folder + */ + protected open fun loadTestFile(testFilename: String, charset: Charset = Charsets.UTF_8): String { + val fileStream = FinTsTestBase::class.java.classLoader.getResourceAsStream(File(TestFilesFolderName, testFilename).path) + + return fileStream.reader(charset).readText() + } + } \ No newline at end of file diff --git a/fints4k/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt index ea35dfdc..075c3a36 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt @@ -1,6 +1,7 @@ package net.dankito.fints.response import net.dankito.fints.FinTsTestBase +import net.dankito.fints.messages.HbciCharset import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.HbciVersion import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion @@ -11,6 +12,7 @@ import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Si import net.dankito.fints.messages.segmente.id.ISegmentId import net.dankito.fints.messages.segmente.id.MessageSegmentId import net.dankito.fints.response.segments.* +import net.dankito.fints.tan.TanImageDecoder import org.assertj.core.api.Assertions.assertThat import org.junit.Assert import org.junit.Test @@ -102,6 +104,27 @@ class ResponseParserTest : FinTsTestBase() { assertThat(result.messageFeedback?.feedbacks?.map { it.message }).containsExactly("Nachricht nicht erwartet.", "Dialoginitialisierung abgebrochen.") } + @Test + fun `decode TanChallenge HHD_UC`() { + + // given + val response = loadTestFile("Decode_TanChallengeHhdUc_WithMaskedCharacter.txt", HbciCharset.DefaultCharset) + + + // when + val result = underTest.parse(response) + + + // then + assertThat(result.successful).isTrue() + + assertThat(result.tanResponse).isNotNull + + val decodedChallengeHhdUc = TanImageDecoder().decodeChallenge(result.tanResponse?.challengeHHD_UC ?: "") + assertThat(decodedChallengeHhdUc.decodingSuccessful).isTrue() + assertThat(decodedChallengeHhdUc.imageBytes.size).isEqualTo(3664) + } + @Test fun parseMessageHeader() { diff --git a/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt index 69fd83e2..efe03ea2 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt @@ -1,16 +1,12 @@ package net.dankito.fints.transactions +import net.dankito.fints.FinTsTestBase import net.dankito.fints.model.AccountData import org.assertj.core.api.Assertions.assertThat import org.junit.Test -class Mt940AccountTransactionsParserTest { - companion object { - const val TestFilesFolderName = "test_files/" - - const val TransactionsMt940FileRelativePath = TestFilesFolderName + "TransactionsMt940.txt" // TODO: place in common file - } +class Mt940AccountTransactionsParserTest : FinTsTestBase() { private val underTest = Mt940AccountTransactionsParser() @@ -20,8 +16,7 @@ class Mt940AccountTransactionsParserTest { fun parseTransactions() { // given - val fileStream = Mt940ParserTest::class.java.classLoader.getResourceAsStream(TransactionsMt940FileRelativePath) - val transactionsString = fileStream.reader().readText() + val transactionsString = loadTestFile(TransactionsMt940Filename) // when diff --git a/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940ParserTest.kt b/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940ParserTest.kt index a3563738..923b8c5f 100644 --- a/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940ParserTest.kt +++ b/fints4k/src/test/kotlin/net/dankito/fints/transactions/Mt940ParserTest.kt @@ -15,9 +15,6 @@ import java.util.* class Mt940ParserTest : FinTsTestBase() { companion object { - const val TestFilesFolderName = "test_files/" - - const val TransactionsMt940FileRelativePath = TestFilesFolderName + "TransactionsMt940.txt" const val Currency = "EUR" @@ -134,8 +131,7 @@ class Mt940ParserTest : FinTsTestBase() { fun parseTransactions() { // given - val fileStream = Mt940ParserTest::class.java.classLoader.getResourceAsStream(TransactionsMt940FileRelativePath) - val transactionsString = fileStream.reader().readText() + val transactionsString = loadTestFile(TransactionsMt940Filename) // when diff --git a/fints4k/src/test/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt b/fints4k/src/test/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt new file mode 100644 index 0000000000000000000000000000000000000000..5c15a02761af8ac0ee5aa3ed71078b33092ee013 GIT binary patch literal 4207 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 literal 0 HcmV?d00001