gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-kotlin] 01/02: Add verification methods for various signat


From: gnunet
Subject: [taler-wallet-kotlin] 01/02: Add verification methods for various signatures
Date: Mon, 29 Jun 2020 21:00:47 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository wallet-kotlin.

commit cd46828dd54c9dc17e0c5f5fe0817d1407cc8bd9
Author: Torsten Grote <t@grobox.de>
AuthorDate: Mon Jun 29 14:59:13 2020 -0300

    Add verification methods for various signatures
---
 .idea/dictionaries/user.xml                        |   1 +
 .../net/taler/wallet/kotlin/Base32Crockford.kt     |   2 +-
 .../kotlin/net/taler/wallet/kotlin/Types.kt        | 114 ++++++++
 .../net/taler/wallet/kotlin/crypto/Planchet.kt     |   5 +-
 .../net/taler/wallet/kotlin/crypto/Signature.kt    |  95 ++++++-
 .../taler/wallet/kotlin/crypto/SignatureTest.kt    | 309 +++++++++++++++++++++
 6 files changed, 523 insertions(+), 3 deletions(-)

diff --git a/.idea/dictionaries/user.xml b/.idea/dictionaries/user.xml
index c5ce0d6..69ce746 100644
--- a/.idea/dictionaries/user.xml
+++ b/.idea/dictionaries/user.xml
@@ -5,6 +5,7 @@
       <w>eddsa</w>
       <w>hmac</w>
       <w>nacl</w>
+      <w>payto</w>
       <w>planchet</w>
       <w>planchets</w>
       <w>taler</w>
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/Base32Crockford.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/Base32Crockford.kt
index c966af2..3bcf15a 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/Base32Crockford.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/Base32Crockford.kt
@@ -106,7 +106,7 @@ object Base32Crockford {
      * @param stringSize size of the string to decode
      * @return size of the resulting data in bytes
      */
-    private fun calculateDecodedDataLength(stringSize: Int): Int {
+    fun calculateDecodedDataLength(stringSize: Int): Int {
         return stringSize * 5 / 8
     }
 
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt
new file mode 100644
index 0000000..c8aa990
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt
@@ -0,0 +1,114 @@
+package net.taler.wallet.kotlin
+
+data class WireFee(
+    /**
+     * Fee for wire transfers.
+     */
+    val wireFee: Amount,
+    /**
+     * Fees to close and refund a reserve.
+     */
+    val closingFee: Amount,
+    /**
+     * Start date of the fee.
+     */
+    val startStamp: Timestamp,
+    /**
+     * End date of the fee.
+     */
+    val endStamp: Timestamp,
+    /**
+     * Signature made by the exchange master key.
+     */
+    val signature: String
+)
+
+data class DenominationRecord(
+    /**
+     * Value of one coin of the denomination.
+     */
+    val value: Amount,
+    /**
+     * The denomination public key.
+     */
+    val denomPub: String,
+    /**
+     * Hash of the denomination public key.
+     * Stored in the database for faster lookups.
+     */
+    val denomPubHash: String,
+    /**
+     * Fee for withdrawing.
+     */
+    val feeWithdraw: Amount,
+    /**
+     * Fee for depositing.
+     */
+    val feeDeposit: Amount,
+    /**
+     * Fee for refreshing.
+     */
+    val feeRefresh: Amount,
+    /**
+     * Fee for refunding.
+     */
+    val feeRefund: Amount,
+    /**
+     * Validity start date of the denomination.
+     */
+    val stampStart: Timestamp,
+    /**
+     * Date after which the currency can't be withdrawn anymore.
+     */
+    val stampExpireWithdraw: Timestamp,
+    /**
+     * Date after the denomination officially doesn't exist anymore.
+     */
+    val stampExpireLegal: Timestamp,
+    /**
+     * Data after which coins of this denomination can't be deposited anymore.
+     */
+    val stampExpireDeposit: Timestamp,
+    /**
+     * Signature by the exchange's master key over the denomination
+     * information.
+     */
+    val masterSig: String,
+    /**
+     * Did we verify the signature on the denomination?
+     */
+    val status: DenominationStatus,
+    /**
+     * Was this denomination still offered by the exchange the last time
+     * we checked?
+     * Only false when the exchange redacts a previously published 
denomination.
+     */
+    val isOffered: Boolean,
+    /**
+     * Did the exchange revoke the denomination?
+     * When this field is set to true in the database, the same transaction
+     * should also mark all affected coins as revoked.
+     */
+    val isRevoked: Boolean,
+    /**
+     * Base URL of the exchange.
+     */
+    val exchangeBaseUrl: String
+)
+
+enum class DenominationStatus {
+    /**
+     * Verification was delayed.
+     */
+    Unverified,
+
+    /**
+     * Verified as valid.
+     */
+    VerifiedGood,
+
+    /**
+     * Verified as invalid.
+     */
+    VerifiedBad
+}
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Planchet.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Planchet.kt
index 617441d..8f4fb98 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Planchet.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Planchet.kt
@@ -26,7 +26,7 @@ internal class Planchet(private val crypto: Crypto) {
         val coinEvHash: String
     )
 
-    fun create(req: CreationRequest, coinKeyPair: EddsaKeyPair, 
blindingFactor: ByteArray): CreationResult {
+    internal fun create(req: CreationRequest, coinKeyPair: EddsaKeyPair, 
blindingFactor: ByteArray): CreationResult {
         val reservePub = Base32Crockford.decode(req.reservePub)
         val reservePriv = Base32Crockford.decode(req.reservePriv)
         val denomPub = Base32Crockford.decode(req.denomPub)
@@ -59,6 +59,9 @@ internal class Planchet(private val crypto: Crypto) {
         )
     }
 
+    /**
+     * Create a pre-coin ([Planchet]) of the given [CreationRequest].
+     */
     fun create(req: CreationRequest): CreationResult {
         val coinKeyPair = crypto.createEddsaKeyPair()
         val blindingFactor = crypto.getRandomBytes(32)
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
index 30db04f..c86942e 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
@@ -1,9 +1,13 @@
 package net.taler.wallet.kotlin.crypto
 
+import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.DenominationRecord
+import net.taler.wallet.kotlin.WireFee
 import net.taler.wallet.kotlin.crypto.CryptoImpl.Companion.toByteArray
 
-class Signature {
+internal class Signature(private val crypto: Crypto) {
 
+    @Suppress("unused")
     companion object {
         const val RESERVE_WITHDRAW = 1200
         const val WALLET_COIN_DEPOSIT = 1201
@@ -43,4 +47,93 @@ class Signature {
         }
     }
 
+    private fun verifyPayment(sig: ByteArray, contractHash: ByteArray, 
merchantPub: ByteArray): Boolean {
+        val p = PurposeBuilder(MERCHANT_PAYMENT_OK)
+            .put(contractHash)
+            .build()
+        return crypto.eddsaVerify(p, sig, merchantPub)
+    }
+
+    /**
+     * Verifies an EdDSA payment signature made with [MERCHANT_PAYMENT_OK].
+     *
+     * @param merchantPub an EdDSA public key, usually belonging to a merchant.
+     *
+     * @return true if the signature is valid, false otherwise
+     */
+    fun verifyPayment(sig: String, contractHash: String, merchantPub: String): 
Boolean {
+        val sigBytes = Base32Crockford.decode(sig)
+        val hashBytes = Base32Crockford.decode(contractHash)
+        val pubBytes = Base32Crockford.decode(merchantPub)
+        return verifyPayment(sigBytes, hashBytes, pubBytes)
+    }
+
+    /**
+     * Verifies an EdDSA wire fee signature made with [MASTER_WIRE_FEES].
+     *
+     * @param masterPub an EdDSA public key
+     *
+     * @return true if the signature is valid, false otherwise
+     */
+    fun verifyWireFee(type: String, wireFee: WireFee, masterPub: String): 
Boolean {
+        val p = PurposeBuilder(MASTER_WIRE_FEES)
+            .put(crypto.sha512("$type\u0000".encodeToByteArray()))
+            .put(wireFee.startStamp.roundedToByteArray())
+            .put(wireFee.endStamp.roundedToByteArray())
+            .put(wireFee.wireFee.toByteArray())
+            .put(wireFee.closingFee.toByteArray())
+            .build()
+        val sig = Base32Crockford.decode(wireFee.signature)
+        val pub = Base32Crockford.decode(masterPub)
+        return crypto.eddsaVerify(p, sig, pub)
+    }
+
+    /**
+     * Verifies an EdDSA denomination record signature made with 
[MASTER_DENOMINATION_KEY_VALIDITY].
+     *
+     * @param masterPub an EdDSA public key
+     *
+     * @return true if the signature is valid, false otherwise
+     */
+    fun verifyDenominationRecord(d: DenominationRecord, masterPub: String): 
Boolean {
+        val pub = Base32Crockford.decode(masterPub)
+        val p = PurposeBuilder(MASTER_DENOMINATION_KEY_VALIDITY)
+            .put(pub)
+            .put(d.stampStart.roundedToByteArray())
+            .put(d.stampExpireWithdraw.roundedToByteArray())
+            .put(d.stampExpireDeposit.roundedToByteArray())
+            .put(d.stampExpireLegal.roundedToByteArray())
+            .put(d.value.toByteArray())
+            .put(d.feeWithdraw.toByteArray())
+            .put(d.feeDeposit.toByteArray())
+            .put(d.feeRefresh.toByteArray())
+            .put(d.feeRefund.toByteArray())
+            .put(Base32Crockford.decode(d.denomPubHash))
+            .build()
+        val sig = Base32Crockford.decode(d.masterSig)
+        return crypto.eddsaVerify(p, sig, pub)
+    }
+
+    /**
+     * Verifies an EdDSA wire account signature made with 
[MASTER_WIRE_DETAILS].
+     *
+     * @param masterPub an EdDSA public key
+     *
+     * @return true if the signature is valid, false otherwise
+     */
+    fun verifyWireAccount(paytoUri: String, signature: String, masterPub: 
String): Boolean {
+        val h = crypto.kdf(
+            64,
+            "exchange-wire-signature".encodeToByteArray(),
+            "$paytoUri\u0000".encodeToByteArray(),
+            ByteArray(0)
+        )
+        val p = PurposeBuilder(MASTER_WIRE_DETAILS)
+            .put(h)
+            .build()
+        val sig = Base32Crockford.decode(signature)
+        val pub = Base32Crockford.decode(masterPub)
+        return crypto.eddsaVerify(p, sig, pub)
+    }
+
 }
diff --git 
a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/SignatureTest.kt 
b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/SignatureTest.kt
index 1326cc4..48cbc8d 100644
--- a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/SignatureTest.kt
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/SignatureTest.kt
@@ -16,13 +16,25 @@
 
 package net.taler.wallet.kotlin.crypto
 
+import net.taler.wallet.kotlin.Amount
 import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.DenominationRecord
+import net.taler.wallet.kotlin.DenominationStatus.Unverified
+import net.taler.wallet.kotlin.DenominationStatus.VerifiedBad
+import net.taler.wallet.kotlin.Timestamp
+import net.taler.wallet.kotlin.WireFee
 import net.taler.wallet.kotlin.crypto.Signature.PurposeBuilder
+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
@@ -181,4 +193,301 @@ class SignatureTest {
         }
     }
 
+    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))
+        }
+    }
+
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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