summaryrefslogtreecommitdiff
path: root/wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-08-14 17:33:22 -0300
committerTorsten Grote <t@grobox.de>2020-08-14 17:33:22 -0300
commita307a498dc8a42df129e8eaff591e9144ed96298 (patch)
treea55182b4793b673e433d0e431e9d971d45c9a6a7 /wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt
parent2ac13b19a5c7fc3531447333fe1772a78ca35795 (diff)
downloadwallet-kotlin-a307a498dc8a42df129e8eaff591e9144ed96298.tar.gz
wallet-kotlin-a307a498dc8a42df129e8eaff591e9144ed96298.tar.bz2
wallet-kotlin-a307a498dc8a42df129e8eaff591e9144ed96298.zip
Make the wallet lib actually use the common lib
Diffstat (limited to 'wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt')
-rw-r--r--wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt493
1 files changed, 493 insertions, 0 deletions
diff --git a/wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt b/wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt
new file mode 100644
index 0000000..b3228b9
--- /dev/null
+++ b/wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt
@@ -0,0 +1,493 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.lib.wallet.crypto
+
+import net.taler.lib.common.Amount
+import net.taler.lib.common.Timestamp
+import net.taler.lib.wallet.Base32Crockford
+import net.taler.lib.wallet.crypto.Signature.PurposeBuilder
+import net.taler.lib.wallet.exchange.DenominationRecord
+import net.taler.lib.wallet.exchange.DenominationStatus.Unverified
+import net.taler.lib.wallet.exchange.DenominationStatus.VerifiedBad
+import net.taler.lib.wallet.exchange.WireFee
+import kotlin.random.Random
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class SignatureTest {
+
+ private val crypto = CryptoFactory.getCrypto()
+ private val signature = Signature(crypto)
+
+ private class PurposeBuilderVector(val purposeNum: Int, val chunks: List<String>, val result: String)
+
+ @Test
+ fun testSignaturePurposeBuilder() {
+ val vectors = listOf(
+ PurposeBuilderVector(42, listOf("46D6FNS4VAFW0"), "000004000002M8CTCZBJ9PMZR0"),
+ PurposeBuilderVector(
+ 1375883724,
+ listOf("7H2ENBHG1AVRD5QVKYCXKAZQQQGCSEAAZ4"),
+ "000007AJ098WRF24XAQ302NQGTBFQ7WSV6NZFFF0SJWMNY8"
+ ),
+ PurposeBuilderVector(251873401, listOf("KVSRVCV6J7QGYR0"), "0000048F0D47K7QKHPSPD4FF1XG0"),
+ PurposeBuilderVector(
+ 2126178285,
+ listOf("EJN927GRT1HNS0K53TBPWYB4", "QGSXW3NGHPJCVTBX3KHGW7GBC1SJWPK0", "XK7GA"),
+ "00000BKYQBKYTX5AJ4F1HM33BJ16A7MQDSWP9F1KVR7B13D4SQMQT7731RF0PR3K5SD61V6F0M"
+ ),
+ PurposeBuilderVector(
+ 871975602,
+ listOf("JC26GG7M", "D5GER58NSW3MX3AB2EA0", "HVB81PFV0YYQ2170H77TQPZTB21T314P9B1N3VSXEHZ88VEPHW"),
+ "00000E1KZ55B54R4D10F8TB0XGAHBKR79T6MP4WMHVB81PFV0YYQ2170H77TQPZTB21T314P9B1N3VSXEHZ88VEPHW"
+ ),
+ PurposeBuilderVector(
+ 366986877,
+ listOf("28", "48213TXMKY8H8EGRJ9XP00D9MHRHQG5N02W5GG0", "VR4CDW7WQ2FYQF3WBG60"),
+ "00000B8NVZ37T4H20G8YQD4ZJ4A3M64JFDG03AD4E4DW1D80Q1C41QG8RVRFSE4ZXEY7RQ0C"
+ ),
+ PurposeBuilderVector(
+ 1369171164,
+ listOf(
+ "SJTSKNX7W7CAP848GCESA09HQQQYQN50G1GY5AHPP12D4GMZJCXMCFPYECGEBF8",
+ "J70SVFRH8WN1DP7DZ60BZW0",
+ "275VB93D2MV6NN7V97QXC16VMX07JNCC5R3HBH1TSWTW5V72G1E66HP73ANWFXE6NG"
+ ),
+ "00000SJHKFJDSK5NK7BTFRERNCG8H0RXJM0K3FFFXFAA1031WAN3DC24T919Z4SV8RZDWWS0WPYS3GCXQW8MEAGPV3PZK05ZY08WQDD4DMAKCTPMZD4YZNG4VEKM0YANHGQ0E5E47B7KBGQCWA05RRT6RWDAQHZNRTP0"
+ ),
+ PurposeBuilderVector(
+ 2115762114,
+ listOf("Z7A3PQ7BV5G5D1P3F2EY9KN4GKVEG95KZD50", "SZ3PA4BSWF8T22TJMDZN5877Z9DQFC67K9E0"),
+ "00000D3Y3FVW5YEM7DEEQPB0AT3C6Y4XWK7A917PX0JB7YTASZ3PA4BSWF8T22TJMDZN5877Z9DQFC67K9E0"
+ ),
+ PurposeBuilderVector(
+ 1382722498,
+ listOf("0WEEY7GRA8Q8ZWS2NRP0", "FQ5YY3ZWJ64014A0SPWMCE4B58", "G03C6M9PZ45D99G4M2HZJ"),
+ "00000CAJDANW41RWXWF1GMHEHZSJ5BHCFQ5YY3ZWJ64014A0SPWMCE4B5A00DGTH6VWGNN560JGA7Y8"
+ ),
+ PurposeBuilderVector(
+ 1557782497,
+ listOf(
+ "CEB9E7Q87X12R172RZ8DAHSGGS60",
+ "M17JXQ290P209Q7EH759PW558FYZYTEHR40J18M81W",
+ "B918DJVWTERCHS54S0G93DFPVK9TV383DMRJR43ZTFZBAE3CVSFG"
+ ),
+ "00000MTWV7FY2RWPJWFEGFT25G2E5HYGTN3K11JCM17JXQ290P209Q7EH759PW558FYZYTEHR40J18M81XD451PBFK9V1J74MK4214DNYVED7BCD0DPK2B0GFZ9ZXD9RDKF5Y"
+ ),
+ PurposeBuilderVector(1662106597, listOf("621Y24CA3ZCHJJ8"), "000004B326XYAC43W48RM7YS354G"),
+ PurposeBuilderVector(
+ 393436430,
+ listOf(
+ "6WMDVK6XKGEAX152ZM4SHQ77DCRQB4RYJX0MGCZFVVK3XTG",
+ "8TKEY2F1PMAT396GC3E191182F34FXXR12MR7Z6WMG4S10GZX7RJG"
+ ),
+ "00000HGQEDEGWDS8VQ6DV70WNT2A5Z89K3EEETSHEP9HX5T190SYZQQ67VN4D9QF17GVA5D1MK861Q0MGGM17HJ7YYW0HAC3ZKEA82CGG8FYKW98"
+ ),
+ PurposeBuilderVector(
+ 1616746709,
+ listOf("ZQT1F901YWFDX6XZZB6R8PSQ1QJANCPF63T8RE09HRK2XQ7EE2K0F74GERH4GQ0"),
+ "00000BV0BPCDBZFM2YJ03XRYVTDVZYPDGHDKE3F4NASCYC7MHGW0K3H65VEEWW560YE90XH291E0"
+ ),
+ PurposeBuilderVector(
+ 691857007,
+ listOf("5JT5DM7KWVW7A7P764NTG0FJDWEQTQAGB253J547GXWX93204NM0"),
+ "00000A197KK6YB5MAV8F7SQREMFCEC9BN00Z4VRXFNEN0P4A74A8F1VSTJ6409B8"
+ ),
+ PurposeBuilderVector(
+ 582885064,
+ listOf("W5P2HP14MCNSKX4CZ4FZ0BDPSX2SV7MCE45V8PF7DW9EXAGK3TC0"),
+ "00000A12QRFCHRBC53C298SBK7T8SY8ZY0PVDKT5KPF8RW8BPHCYEVRJXTN167MR"
+ ),
+ PurposeBuilderVector(
+ 480383332,
+ listOf(
+ "49DZJY0K1WJFDK7P86Q1R2F150S50JQR8HQW059HBG9CR615N4V82",
+ "D0XXQKX9BB0HVJK34TKCZJ1E2JQKF06EQVXRRD2ZKX4QGXG",
+ "99JY6SS2HJ1J29GS1793CGE5BKSG"
+ ),
+ "00000NRWM88P88JVZ5W163S4YV6FCGDE3G4Y2A1JA15FGH3FR0AK2Q0JSGC2BA9PG5M3QPYFN5DC27EACCKADKY85RAAYDW0STZFQ31MBYFMJY3P99JY6SS2HJ1J29GS1793CGE5BKSG"
+ ),
+ PurposeBuilderVector(
+ 877078324,
+ listOf("RC", "K1PKX78BPGNQSSZCVRB3B6PJYR", "74"),
+ "000006HM8WKK9GWRDMZ9T2XM5DYEFV6Y2RTSNMQP74"
+ ),
+ PurposeBuilderVector(
+ 25488155,
+ listOf(
+ "FNG58X0W2A4HV4ZY5TKS73RGNHA8KZ3B4WWDJS2V1B27F3G6G08FNHM8NYBZ45Q8",
+ "NQGNDPDNW4G9AEHXB1BKXQXK84"
+ ),
+ "00000G01GKNHPZB0AHT1R4M93P9ZWBN7JE7H1B2MH7Y6P9SRV5J5P2P4EY70D00GZB38HBWQY8BEHBF1AVCVBR90JMX3TP2Q7VFV6G8"
+ ),
+ PurposeBuilderVector(
+ 749389285,
+ listOf(
+ "FVD8X18A3S2KHJ7143TCEHE3K083DRBG2CA0D766NMRVHX7MSN78NSYHZSWNH9V2DM",
+ "1TSPZ4VBKEBKCQEB2673H2FJHCZ6M19R2691NY79A0",
+ "P9VW044VY2TGKNNF4ZWZ1S2ACM4VJYCWP7ZGWRKR1264ZS2CQZGSXZ775AC41X5778"
+ ),
+ "00000X1CNB2YAZPTHT2GM7J5734E287MRX2W760G6VGQ04RM0TECDB9HQ3TF9KAEHBKX3ZKSB2KP4V8EPDQS6TWVJWV5VJRHHRW8KWMB7SN0AE0HJ8DFHTAGP9VW044VY2TGKNNF4ZWZ1S2ACM4VJYCWP7ZGWRKR1264ZS2CQZGSXZ775AC41X5778"
+ ),
+ PurposeBuilderVector(
+ 434249902,
+ listOf(
+ "VFERBNSH7X70",
+ "T5DWNBWMF3BPAGWSESKF7WGTTS3JHVKAAR3DR",
+ "SMCA0184DN3WE934BGFE4MCE14279NYCFF0PJDQ20JRHFZXSGXE6CP1BSMGR479HAC"
+ ),
+ "00000KRSW8GAXPYXGQBK2FTET5DWNBWMF3BPAGWSESKF7WGTTS3JHVKAAR3DSK8RM02G8VA7RWJ68Q0YW98RW284EKBWRYY1D4VE415H2ZZVK1TWCSC2QK91G8EK2MR"
+ ),
+ PurposeBuilderVector(
+ 1710327887,
+ listOf("S64XB965NEAQQ5287XHNT126BW"),
+ "00000635Y644ZJC9TPJCBAWNFEA4GFV3BM24CQR"
+ ),
+ PurposeBuilderVector(
+ 1247371119,
+ listOf("CSXX5W59E3S03412M29J0HG0R67BW4B3X946GS2T0TVYPH9HFP9FC0M7", "931KSGS0ZJG4A79BT55G0Q4Q"),
+ "00000EJAB5FPYSKVTBRAJW7J0682584K41301GCEQR8P7TJ8D1J5M1NQXD2K2ZCJYR18EJ637K1J1Z508MEJQMAB01E9E"
+ ),
+ PurposeBuilderVector(
+ 2024386767,
+ listOf(
+ "R2WZA0FKD57D418NB2KK84PJKHAVPEC6219X2KQJ78JFB76F0R",
+ "CNYD5TAC7Y3N58CN6VK9F7NBKD2NJJK3VJ91CZKBFZWDVVZB28",
+ "G7WQJVXXYT4JHA1NK22RXS45BM0W5H5JD7JNX3PWD2WZ27Y5Y1FB4"
+ ),
+ "00000SVRN6RCZG5SYM0Z6TAET82HAP576G9D572NQCWRC42KT57F4EH4YPECY1K5FK9EJK1ZGX9A359PWTBSXAWV8NCMMRYWJ8B7WTVZZ3EYZTRJG7WQJVXXYT4JHA1NK22RXS45BM0W5H5JD7JNX3PWD2WZ27Y5Y1FB4"
+ ),
+ PurposeBuilderVector(1182512118, listOf("KH32THSNNZGJPC0"), "000004A6FESZD7265N3KBBZ15CR0"),
+ PurposeBuilderVector(
+ 2029783771,
+ listOf("N7468QH02K49BTD16S1F6FB4NKNXB0Q410"),
+ "000007BRZG5DQAE8CHF20568JQMT2DJ2YCYP9B7BTP1E820"
+ ),
+ PurposeBuilderVector(
+ 1111140596,
+ listOf("6BN45PA2RCVRR8PJ6XKRJ9ZER2BR3MY9RQ290FADAG"),
+ "000008J27AMF8CQA8BCM5GSQHGHD4DV7H4KYXG4QG79WKHE4J0YMTN0"
+ )
+ )
+ for (v in vectors) {
+ val builder = PurposeBuilder(v.purposeNum)
+ for (chunk in v.chunks) builder.put(Base32Crockford.decode(chunk))
+ val encodedBytes = Base32Crockford.encode(builder.build())
+ assertEquals(v.result, encodedBytes)
+ }
+ }
+
+ private class PaymentSignatureVector(val publicKey: String, val hash: String, val signature: String)
+
+ @Test
+ fun testVerifyPaymentSignature() {
+ val vectors = listOf(
+ PaymentSignatureVector(
+ "8GSJZ649T2PXMKZC01Y4ANNBE7MF14QVK9SQEC4E46ZHKCVG8AS0",
+ "CW96WR74JS8T53EC8GKSGD49QKH4ZNFTZXDAWMMV5GJ1E4BM6B8GPN5NVHDJ8ZVXNCW7Q4WBYCV61HCA3PZC2YJD850DT29RHHN7ESR",
+ "JSNG99MX5W4AS7AEA8D4ADCHYTHFER0GX1N064E1XX48N513AXAEHDTG8ZT7ANWQK5HGCAXGEWN7TCBTYVG3RDPBTAS5HEP608KQ40R"
+ ),
+ PaymentSignatureVector(
+ "6WC3MPYM5XKPKRA2Z6SYB81CPFV3E7EC6S2GVE095X8XH63QTZCG",
+ "Z6H76JXPJFP3JBGSF54XBF0BVXDJ0CJBK4YT9GVR1AT916ZD57KP53YZN5G67A4YN95WGMZKQW7744483P5JDF06B6S7TMK195QGP20",
+ "T2Y4KJJPZ0F2DMNF5S81V042T20VHB5VRXQYX4RF8KRH6H2Z4JRBD05CCDJ6C625MHM5FQET00RDX2NF5QX63S9YDXEP0710VBYHY10"
+ ),
+ PaymentSignatureVector(
+ "2X3PSPT7D6TEM97R98C0DHZREFVAVA3XTH11D5A2Z2K7GBKQ7AEG",
+ "JSNG99MX5W4AS7AEA8D4ADCHYTHFER0GX1N064E1XX48N513AXAEHDTG8ZT7ANWQK5HGCAXGEWN7TCBTYVG3RDPBTAS5HEP608KQ40R",
+ "W0783H1KZ6GX58T5ZEH5VYMTXP7P7EA3KBKQ4Y8CN8M20GY8RNA4RX1AZG6TQ70NR4XG4EZ9D606P4RDAARD2SXTKA90BJMN9VEAC00"
+ )
+ )
+ for (v in vectors) {
+ // verification succeeds as expected
+ assertTrue(signature.verifyPayment(v.signature, v.hash, v.publicKey))
+ // verification fails which different signature
+ val size = Base32Crockford.calculateDecodedDataLength(v.signature.length)
+ val sig = Base32Crockford.encode(Random.nextBytes(size))
+ assertFalse(signature.verifyPayment(sig, v.hash, v.publicKey))
+ // verification fails which different hash
+ val hash = Base32Crockford.encode(Random.nextBytes(64))
+ assertFalse(signature.verifyPayment(v.signature, hash, v.publicKey))
+ // verification fails which different public key
+ val publicKey = Base32Crockford.encode(Random.nextBytes(32))
+ assertFalse(signature.verifyPayment(v.signature, v.hash, publicKey))
+ }
+ }
+
+ private class WireFeeSignatureVector(val wireFee: WireFee, val masterPub: String)
+
+ @Test
+ fun testVerifyWireFeeSignature() {
+ val type = "x-taler-bank"
+ val vectors = listOf(
+ WireFeeSignatureVector(
+ WireFee(
+ wireFee = Amount("TESTKUDOS", 0, 1000000),
+ closingFee = Amount("TESTKUDOS", 0, 1000000),
+ startStamp = Timestamp(1609470000000),
+ endStamp = Timestamp(1641006000000),
+ signature = "C77EZ56ZT4ACNPGP3PX881S42413N37NJVTRPNMM9BWRVGQBK2C157HHRF61RMYPAWW6QVWV5RVKEVBD3XAVJFXAEBM2HPNS5AMPY18"
+ ), "Y8JGNCPMM7XTF44FT7V1JRNNJQ6F4R7DZPXAKYYCJZJVEX2C45QG"
+ ),
+ WireFeeSignatureVector(
+ WireFee(
+ wireFee = Amount("TESTKUDOS", 0, 1000000),
+ closingFee = Amount("TESTKUDOS", 0, 1000000),
+ startStamp = Timestamp(1577847600000),
+ endStamp = Timestamp(1609470000000),
+ signature = "4F87Z12WQM7JXEKPRPGFZVGCZPNN6Q9RVP2NA0PZ57CYQP4QXV38EQW59X3K5WAQN82BX95X57775ZJGA5EB3VA5GV9S3JBA3RGX41G"
+ ), "0BJ4SX13W4Q64QDDSRBVTYSWT8C0X2HVB61QYSZM1B1W9JVCE0C0"
+ ),
+ WireFeeSignatureVector(
+ WireFee(
+ wireFee = Amount("TESTKUDOS", 0, 1000000),
+ closingFee = Amount("TESTKUDOS", 0, 1000000),
+ startStamp = Timestamp(1672542000000),
+ endStamp = Timestamp(1704078000000),
+ signature = "9YWES9YPR2W5ACCYE4ZE4S3PE62CVX01AXXSC9KT9PZ6B52D5GSBP4DG95Y024EDE5HFGP6FEZVPKQM8PA6J9WD2NXW3QBA34MEV61R"
+ ), "FWPD4E312RB8XZ22FGHCZGV2YPT7AX02XVRZEC7F53QKZJHY7H50"
+ )
+ )
+ for (v in vectors) {
+ // verification succeeds as expected
+ assertTrue(signature.verifyWireFee(type, v.wireFee, v.masterPub))
+ // different type fails verification
+ assertFalse(signature.verifyWireFee("foo", v.wireFee, v.masterPub))
+ // different WireFee wireFee fails verification
+ var wireFee = v.wireFee.copy(wireFee = Amount("TESTKUDOS", 0, 100))
+ assertFalse(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // different WireFee closingFee fails verification
+ wireFee = v.wireFee.copy(closingFee = Amount("TESTKUDOS", 0, 100))
+ assertFalse(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // different WireFee startStamp fails verification
+ wireFee = v.wireFee.copy(startStamp = Timestamp(v.wireFee.startStamp.ms + 1000))
+ assertFalse(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // different WireFee endStamp fails verification
+ wireFee = v.wireFee.copy(endStamp = Timestamp(v.wireFee.endStamp.ms + 1000))
+ assertFalse(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // different WireFee signature fails verification
+ val size = Base32Crockford.calculateDecodedDataLength(v.wireFee.signature.length)
+ wireFee = v.wireFee.copy(signature = Base32Crockford.encode(Random.nextBytes(size)))
+ assertFalse(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // startStamp changes below one second don't affect verification
+ wireFee = v.wireFee.copy(startStamp = Timestamp(v.wireFee.startStamp.ms + 999))
+ assertTrue(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // startStamp changes below one second don't affect verification
+ wireFee = v.wireFee.copy(endStamp = Timestamp(v.wireFee.endStamp.ms + 999))
+ assertTrue(signature.verifyWireFee(type, wireFee, v.masterPub))
+ // different masterPub fails verification
+ val masterPub = Base32Crockford.encode(Random.nextBytes(32))
+ assertFalse(signature.verifyWireFee(type, v.wireFee, masterPub))
+ }
+ }
+
+ private class DenominationRecordSignatureVector(val denominationRecord: DenominationRecord, val masterPub: String)
+
+ @Test
+ fun testVerifyDenominationRecordSignature() {
+ val vectors = listOf(
+ DenominationRecordSignatureVector(
+ DenominationRecord(
+ value = Amount("TESTKUDOS", 4, 0),
+ denomPub = "020000XX26WN5X7ECKERVGBWTFM3KJ7AT0N8T7RQB7W7G4Q5K0W1BT8QFQBMMTR925TC6RX4QGVVVXH2ZMJVWDRR58BRXNDCFBZTH7RNS1KVWZQGEWME1D6QX79R0V6V9S0NC9H8YP0W6MJD7YSV5VQZWCR1JXRTSS0HPHDV4V6AVX34TSMHGRDQXZYVTEBGVWNF8TNR2P5296TCZR0G2001",
+ denomPubHash = "MY54RXQ1WTZPFD3VZ4QJH6RHFPTYE78Y4DQ3GANTWK2Z0SQ99AK90K5H0P419EY4QWV6S4Q6QG52HYFCVRCPEQNG22RM3E7XW2YFJ9R",
+ feeWithdraw = Amount("TESTKUDOS", 0, 3000000),
+ feeDeposit = Amount("TESTKUDOS", 0, 3000000),
+ feeRefresh = Amount("TESTKUDOS", 0, 4000000),
+ feeRefund = Amount("TESTKUDOS", 0, 2000000),
+ stampStart = Timestamp(1593449948000),
+ stampExpireWithdraw = Timestamp(1594054748000),
+ stampExpireLegal = Timestamp(1688057948000),
+ stampExpireDeposit = Timestamp(1656521948000),
+ masterSig = "2PEF4EHTKE2R89KHQBZ6NAQW34YEMAP044DTJXJ9TFX224YW07BB2X1VHEYH425N2RNDZS9XFEAQK54GV6Q6CTNSE1Z038TZ4V3MM1R",
+ status = Unverified, isOffered = false, isRevoked = false, exchangeBaseUrl = "example.org"
+ ),
+ "C5K3YXPHSDVYPCB79D5AC4FYCKQ9D0DN3N3MBA5D54BG42SX0ZRG"
+ ),
+ DenominationRecordSignatureVector(
+ DenominationRecord(
+ value = Amount("TESTKUDOS", 0, 10000000),
+ denomPub = "020000XWWS2DFXM1G8YR6NXVF07R0DH7GRE1J7ZC2YENN7Q60ZHX9S7FEVQDBFP1041DN0GFASZR6A7RSJ3EYRV1YD5ACAZRDQNH5P5KEBTSK5XA76YYWTW5VA7N1V1VRVF0CF7VZ8GSJNQ91FSJGNKGR82DB7H82TPAGMR1B5DG5SY2WP3NB5YJD90H64E09C0YC8FCCWRGC09HFNPG2001",
+ denomPubHash = "J73HZJTR76GRZKBEKQC4X289056V5RMY4JS932XM8BAENQ83YAF9W2WYKRBN87TN8ENXP61JC7HMC7PYK9MZ8S289FCD07EM95AJGMR",
+ feeWithdraw = Amount("TESTKUDOS", 0, 1000000),
+ feeDeposit = Amount("TESTKUDOS", 0, 1000000),
+ feeRefresh = Amount("TESTKUDOS", 0, 3000000),
+ feeRefund = Amount("TESTKUDOS", 0, 1000000),
+ stampStart = Timestamp(1593450307000),
+ stampExpireWithdraw = Timestamp(1594055107000),
+ stampExpireLegal = Timestamp(1688058307000),
+ stampExpireDeposit = Timestamp(1656522307000),
+ masterSig = "HPBAY19C1B5H3FAYWKRQFS8VC693658SNSK3BB304BNAG950BS881GMVPVN0YBG67K9J9E4A9BFN7VAQEY1D5G6YAGVPFQWX0GRQ62G",
+ status = Unverified, isOffered = false, isRevoked = false, exchangeBaseUrl = "example.org"
+ ),
+ "NF8G14QFVWJSQPNFC3M2JKNMGJESS8ZWZX9V43PG40YXWRWA88VG"
+ )
+ )
+ for (v in vectors) {
+ // verification succeeds as expected
+ assertTrue(signature.verifyDenominationRecord(v.denominationRecord, v.masterPub))
+ // different masterPub fails verification
+ val masterPub = Base32Crockford.encode(Random.nextBytes(32))
+ assertFalse(signature.verifyDenominationRecord(v.denominationRecord, masterPub))
+ // different value fails verification
+ val value = v.denominationRecord.value + Amount.min(v.denominationRecord.value.currency)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(value = value),
+ v.masterPub
+ )
+ )
+ // different denomPubHash fails verification
+ val calculatedDenomPubHash = crypto.sha512(Base32Crockford.decode(v.denominationRecord.denomPub))
+ assertEquals(v.denominationRecord.denomPubHash, Base32Crockford.encode(calculatedDenomPubHash))
+ val denomPubHash = Base32Crockford.encode(Random.nextBytes(calculatedDenomPubHash.size))
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(denomPubHash = denomPubHash),
+ v.masterPub
+ )
+ )
+ // different feeWithdraw fails verification
+ val feeWithdraw = v.denominationRecord.feeWithdraw + Amount.min(v.denominationRecord.feeWithdraw.currency)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(feeWithdraw = feeWithdraw),
+ v.masterPub
+ )
+ )
+ // different feeDeposit fails verification
+ val feeDeposit = v.denominationRecord.feeDeposit + Amount.min(v.denominationRecord.feeDeposit.currency)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(feeDeposit = feeDeposit),
+ v.masterPub
+ )
+ )
+ // different feeRefresh fails verification
+ val feeRefresh = v.denominationRecord.feeRefresh + Amount.min(v.denominationRecord.feeRefresh.currency)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(feeRefresh = feeRefresh),
+ v.masterPub
+ )
+ )
+ // different feeRefund fails verification
+ val feeRefund = v.denominationRecord.feeRefund + Amount.min(v.denominationRecord.feeRefund.currency)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(feeRefund = feeRefund),
+ v.masterPub
+ )
+ )
+ // different stampStart fails verification
+ val stampStart = Timestamp(v.denominationRecord.stampStart.ms + 1000)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(stampStart = stampStart),
+ v.masterPub
+ )
+ )
+ // different stampExpireWithdraw fails verification
+ val stampExpireWithdraw = Timestamp(v.denominationRecord.stampExpireWithdraw.ms + 1000)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(stampExpireWithdraw = stampExpireWithdraw),
+ v.masterPub
+ )
+ )
+ // different stampExpireLegal fails verification
+ val stampExpireLegal = Timestamp(v.denominationRecord.stampExpireLegal.ms + 1000)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(stampExpireLegal = stampExpireLegal),
+ v.masterPub
+ )
+ )
+ // different stampExpireDeposit fails verification
+ val stampExpireDeposit = Timestamp(v.denominationRecord.stampExpireDeposit.ms + 1000)
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(stampExpireDeposit = stampExpireDeposit),
+ v.masterPub
+ )
+ )
+ // different masterPub fails verification
+ val size = Base32Crockford.calculateDecodedDataLength(v.denominationRecord.masterSig.length)
+ val masterSig = Base32Crockford.encode(Random.nextBytes(size))
+ assertFalse(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(masterSig = masterSig),
+ v.masterPub
+ )
+ )
+ // different status does not affect verification
+ assertTrue(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(status = VerifiedBad),
+ v.masterPub
+ )
+ )
+ // different exchangeBaseUrl does not affect verification
+ assertTrue(
+ signature.verifyDenominationRecord(
+ v.denominationRecord.copy(exchangeBaseUrl = "foo.bar"),
+ v.masterPub
+ )
+ )
+ }
+ }
+
+ private class WireAccountSignatureVector(val paytoUri: String, val signature: String, val masterPub: String)
+
+ @Test
+ fun testVerifyWireAccount() {
+ val paytoUri = "payto://x-taler-bank/localhost/Exchange"
+ val vectors = listOf(
+ WireAccountSignatureVector(
+ paytoUri,
+ "7THNYN5G12GXZABHP187XJZ3ACTDKAWGHWYM2ERA1VGE4JMGPADXT37ZM8D4DVAQTSP8CR61VAD4ZZSWKRPP5KQ12JCTVHCKDD3KA3R",
+ "QPJSEA4SM8E67106C6MN2TMG4308J20C1T0D411WED1FJ00VF8ZG"
+ ),
+ WireAccountSignatureVector(
+ paytoUri,
+ "6JJDJ0HG3AGRTEY1FFGHR89367GPE4FS7TC8Z26N34PHFAMSRXQ4FA7P96CDS6625SRNAN4DY1NK4TNBQXKRXQ9QR82HEVCB2FF1E18",
+ "8QBE0SRR84GWJ1FX2QCGGNRZTWA2FCV6YQC98Q26DRRJ0QBQE930"
+ ),
+ WireAccountSignatureVector(
+ paytoUri,
+ "X5EYQJ388P8AH1PPYD2GFQ9Y1NGA7HV0TXAW6GJ7C0D6R6GAY0059HCDBE98TKJJT6MB1S660FV13DV46JDKJ622MR961XVGP6DG618",
+ "802M037TN8GHBXEGBPC7J41HJC06TWBWXYH36AYQHRJ7NVHJBQQ0"
+ )
+ )
+ for (v in vectors) {
+ // verification succeeds as expected
+ assertTrue(signature.verifyWireAccount(v.paytoUri, v.signature, v.masterPub))
+ // different paytoUri fails verification
+ assertFalse(signature.verifyWireAccount("foo", v.signature, v.masterPub))
+ // different paytoUri fails verification
+ val size = Base32Crockford.calculateDecodedDataLength(v.signature.length)
+ val sig = Base32Crockford.encode(Random.nextBytes(size))
+ assertFalse(signature.verifyWireAccount(v.paytoUri, sig, v.masterPub))
+ // different paytoUri fails verification
+ val masterPub = Base32Crockford.encode(Random.nextBytes(32))
+ assertFalse(signature.verifyWireAccount(v.paytoUri, sig, masterPub))
+ }
+ }
+
+}