diff options
author | Torsten Grote <t@grobox.de> | 2020-08-14 17:33:22 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-08-14 17:33:22 -0300 |
commit | a307a498dc8a42df129e8eaff591e9144ed96298 (patch) | |
tree | a55182b4793b673e433d0e431e9d971d45c9a6a7 /wallet/src/commonTest/kotlin/net/taler/lib/wallet/crypto/SignatureTest.kt | |
parent | 2ac13b19a5c7fc3531447333fe1772a78ca35795 (diff) | |
download | wallet-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.kt | 493 |
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)) + } + } + +} |