gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-kotlin] 02/02: Add signing of RecoupRequest with tests


From: gnunet
Subject: [taler-wallet-kotlin] 02/02: Add signing of RecoupRequest with tests
Date: Mon, 29 Jun 2020 21:00:48 +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 ba7e1cce382b338a746ae3f1c6358f2e60530384
Author: Torsten Grote <t@grobox.de>
AuthorDate: Mon Jun 29 16:00:19 2020 -0300

    Add signing of RecoupRequest with tests
---
 .../kotlin/net/taler/wallet/kotlin/Types.kt        | 78 ++++++++++++++++++++++
 .../net/taler/wallet/kotlin/crypto/Recoup.kt       | 67 +++++++++++++++++++
 .../net/taler/wallet/kotlin/crypto/RecoupTest.kt   | 74 ++++++++++++++++++++
 3 files changed, 219 insertions(+)

diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt
index c8aa990..2aa44da 100644
--- a/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/Types.kt
@@ -112,3 +112,81 @@ enum class DenominationStatus {
      */
     VerifiedBad
 }
+
+class CoinRecord(
+    /**
+     * Where did the coin come from?  Used for recouping coins.
+     */
+    val coinSource: CoinSourceType,
+
+    /**
+     * Public key of the coin.
+     */
+    val coinPub: String,
+
+    /**
+     * Private key to authorize operations on the coin.
+     */
+    val coinPriv: String,
+
+    /**
+     * Key used by the exchange used to sign the coin.
+     */
+    val denomPub: String,
+
+    /**
+     * Hash of the public key that signs the coin.
+     */
+    val denomPubHash: String,
+
+    /**
+     * Unblinded signature by the exchange.
+     */
+    val denomSig: String,
+
+    /**
+     * Amount that's left on the coin.
+     */
+    val currentAmount: Amount,
+
+    /**
+     * Base URL that identifies the exchange from which we got the coin.
+     */
+    val exchangeBaseUrl: String,
+
+    /**
+     * The coin is currently suspended, and will not be used for payments.
+     */
+    val suspended: Boolean,
+
+    /**
+     * Blinding key used when withdrawing the coin.
+     * Potentially send again during payback.
+     */
+    val blindingKey: String,
+
+    /**
+     * Status of the coin.
+     */
+    val status: CoinStatus
+)
+
+enum class CoinSourceType(val value: String) {
+    WITHDRAW("withdraw"),
+    REFRESH("refresh"),
+    TIP("tip")
+}
+
+enum class CoinStatus(val value: String) {
+
+    /**
+     * Withdrawn and never shown to anybody.
+     */
+    FRESH("fresh"),
+
+    /**
+     * A coin that has been spent and refreshed.
+     */
+    DORMANT("dormant")
+
+}
diff --git a/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Recoup.kt 
b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Recoup.kt
new file mode 100644
index 0000000..79612a8
--- /dev/null
+++ b/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Recoup.kt
@@ -0,0 +1,67 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Base32Crockford
+import net.taler.wallet.kotlin.CoinRecord
+import net.taler.wallet.kotlin.CoinSourceType.REFRESH
+import net.taler.wallet.kotlin.crypto.Signature.Companion.WALLET_COIN_RECOUP
+
+internal class Recoup(private val crypto: Crypto) {
+
+    /**
+     * Request that we send to the exchange to get a payback.
+     */
+    data class Request(
+        /**
+         * Hashed denomination public key of the coin we want to get
+         * paid back.
+         */
+        val denomPubHash: String,
+
+        /**
+         * Signature over the coin public key by the denomination.
+         */
+        val denomSig: String,
+
+        /**
+         * Coin public key of the coin we want to refund.
+         */
+        val coinPub: String,
+
+        /**
+         * Blinding key that was used during withdraw,
+         * used to prove that we were actually withdrawing the coin.
+         */
+        val coinBlindKeySecret: String,
+
+        /**
+         * Signature made by the coin, authorizing the payback.
+         */
+        val coinSig: String,
+
+        /**
+         * Was the coin refreshed (and thus the recoup should go to the old 
coin)?
+         */
+        val refreshed: Boolean
+    )
+
+    /**
+     * Create and sign a message to recoup a coin.
+     */
+    fun createRequest(coin: CoinRecord): Request {
+        val p = Signature.PurposeBuilder(WALLET_COIN_RECOUP)
+            .put(Base32Crockford.decode(coin.coinPub))
+            .put(Base32Crockford.decode(coin.denomPubHash))
+            .put(Base32Crockford.decode(coin.blindingKey))
+            .build()
+        val coinSig = crypto.eddsaSign(p, 
Base32Crockford.decode(coin.coinPriv))
+        return Request(
+            coinBlindKeySecret = coin.blindingKey,
+            coinPub = coin.coinPub,
+            coinSig = Base32Crockford.encode(coinSig),
+            denomPubHash = coin.denomPubHash,
+            denomSig = coin.denomSig,
+            refreshed = coin.coinSource === REFRESH
+        )
+    }
+
+}
diff --git a/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/RecoupTest.kt 
b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/RecoupTest.kt
new file mode 100644
index 0000000..865eaa9
--- /dev/null
+++ b/src/commonTest/kotlin/net/taler/wallet/kotlin/crypto/RecoupTest.kt
@@ -0,0 +1,74 @@
+package net.taler.wallet.kotlin.crypto
+
+import net.taler.wallet.kotlin.Amount
+import net.taler.wallet.kotlin.CoinRecord
+import net.taler.wallet.kotlin.CoinSourceType.REFRESH
+import net.taler.wallet.kotlin.CoinSourceType.WITHDRAW
+import net.taler.wallet.kotlin.CoinStatus.FRESH
+import net.taler.wallet.kotlin.crypto.Recoup.Request
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class RecoupTest {
+
+    private val crypto = CryptoFactory.getCrypto()
+    private val recoup = Recoup(crypto)
+
+    private class RecoupRequestVector(val record: CoinRecord, val request: 
Request)
+
+    @Test
+    fun test() {
+        val vectors = listOf(
+            RecoupRequestVector(
+                CoinRecord(
+                    coinSource = WITHDRAW,
+                    coinPub = 
"9YW99NYH54FWG87TP3SKCGR9MRWYSVR75X42FN4YAJC9579CQBJ0",
+                    coinPriv = 
"EPPYWTDVWM4CXW75J8AAGWW620C7DCC3B45TM31KHKPYMT9VM7DG",
+                    denomPub = 
"020000X3T40FNGSM3Y1QFKX9H4JY5EP70Y2CKDHD29B5BEZCTWRMT6AC9SA0G5YJ1XVYY580K6S93SFCKM5PFKP96H3KXDNP58EVQPTYDB5S0QY4V8B873NYA7EYRH25NJ8MR2VP6F7WWVMBK3NR3FSFP17PHPGF279NBSRTXWZSJZFX6RCTR6VS5WMSYFHZCR0P8R6MGHDCB3QW4M3G2001",
+                    denomPubHash = 
"DG3114X57XKHQ1XM6AN0P7D2B6J96SVFG09S3SF43ZXCYYK9PGX84XP3ZY7WY3QD9JE1BWS2T8DGR78QXZZAVGED79HES10HAPTWBX8",
+                    denomSig = 
"AHE8DGMTTKNWGCVQYTV56CBWA81DH10BQEBAM0A5YGRAZXRPVHMZ5FH0XW1523QXSTXT3WMS1X7FDMEZ3BR898YEDTXDTHEMX6RS11KCPBAZCGTNPHKYF6RH9414Q0PYT5BZKGKWJNAFPWQS715NXEFZBY1D6RPTAN520REJ4RTREC9PP5D8WVQ3B66Q4ARYQ3CK49K0ZDME0",
+                    currentAmount = Amount("TESTKUDOS", 0, 0),
+                    exchangeBaseUrl = "example.org",
+                    suspended = false,
+                    blindingKey = 
"1Y29A3ABERGYJR8Y9HS7XS8AYYDAKV6BZSXMZ0WS5VDTS150C100",
+                    status = FRESH
+                ),
+                Request(
+                    denomPubHash = 
"DG3114X57XKHQ1XM6AN0P7D2B6J96SVFG09S3SF43ZXCYYK9PGX84XP3ZY7WY3QD9JE1BWS2T8DGR78QXZZAVGED79HES10HAPTWBX8",
+                    denomSig = 
"AHE8DGMTTKNWGCVQYTV56CBWA81DH10BQEBAM0A5YGRAZXRPVHMZ5FH0XW1523QXSTXT3WMS1X7FDMEZ3BR898YEDTXDTHEMX6RS11KCPBAZCGTNPHKYF6RH9414Q0PYT5BZKGKWJNAFPWQS715NXEFZBY1D6RPTAN520REJ4RTREC9PP5D8WVQ3B66Q4ARYQ3CK49K0ZDME0",
+                    coinPub = 
"9YW99NYH54FWG87TP3SKCGR9MRWYSVR75X42FN4YAJC9579CQBJ0",
+                    coinBlindKeySecret = 
"1Y29A3ABERGYJR8Y9HS7XS8AYYDAKV6BZSXMZ0WS5VDTS150C100",
+                    coinSig = 
"GBN5MVEY6JATGGSTX5YF32G3G204Y1PF9ASVXQFN895DWN5ZK3CBY2NHC8ATB1E9JWSV1QD4ECM0XHP8Y6DFZ1S02MYD5NBKZ45B018",
+                    refreshed = false
+                )
+            ),
+            RecoupRequestVector(
+                CoinRecord(
+                    coinSource = REFRESH,
+                    coinPub = 
"2YE003173JB6WNQ9HS73Z468F11KDHWWVGCPDHDTD6AY5AVJPQPG",
+                    coinPriv = 
"GCR4R26XTCFNS109ZYC0G6M374K1ACNES1YH2CESWD86JBAE22WG",
+                    denomPub = 
"020000X9M8MQVNH28D4J4YFA5ZZNKGNR0423BXQZV00RRN754XTDQMS5YKWQ3KSN8NV4V7CHDM22CRJ4WWQW05FDZC7VN0KK4S8VK9PYPPXNW6FJKHBSEZ2X1FCJKRC3T6PK2BKQ422Y2ASE76ZZAH6RRQT4SQGZTV3TRTSBC5AECJ5Z6C4RX7XFBERKVB45DA7H3V53YCYX1C41ZY5G2001",
+                    denomPubHash = 
"J0G3G880JJJD09923AAWNQQZHJVRQT71ZK8KZGYW7T1P18PCPZ72FBAKDW3EFZ3QFZEW72EYJ9K0FG3RFZTFADQKZDDN9YT6BT2PE70",
+                    denomSig = 
"8HVKAGMKRQRWB1HX9WCPX3FJ0SVE24DCAWQSHX4ZMXZ1KFZDNF4F0Z4K6ZCW142B2WDEH0W848W8WKH8P6A6EJR7J635QEF78CSJFF0EX1FRS5VY484GEX0HH3BDRDFGTHXNQRTTF1DD5ETMEG1QNKA3SAB24XZXZNQ6RDGTK02MRETP859NGMDD2F94F58JH4HYGXMAY0X32",
+                    currentAmount = Amount("TESTKUDOS", 0, 0),
+                    exchangeBaseUrl = "example.org",
+                    suspended = false,
+                    blindingKey = 
"C5VPT5F925ADJWK48PR07KV2W66EZQN4KYE146NY77DFM8GFCTXG",
+                    status = FRESH
+                ),
+                Request(
+                    denomPubHash = 
"J0G3G880JJJD09923AAWNQQZHJVRQT71ZK8KZGYW7T1P18PCPZ72FBAKDW3EFZ3QFZEW72EYJ9K0FG3RFZTFADQKZDDN9YT6BT2PE70",
+                    denomSig = 
"8HVKAGMKRQRWB1HX9WCPX3FJ0SVE24DCAWQSHX4ZMXZ1KFZDNF4F0Z4K6ZCW142B2WDEH0W848W8WKH8P6A6EJR7J635QEF78CSJFF0EX1FRS5VY484GEX0HH3BDRDFGTHXNQRTTF1DD5ETMEG1QNKA3SAB24XZXZNQ6RDGTK02MRETP859NGMDD2F94F58JH4HYGXMAY0X32",
+                    coinPub = 
"2YE003173JB6WNQ9HS73Z468F11KDHWWVGCPDHDTD6AY5AVJPQPG",
+                    coinBlindKeySecret = 
"C5VPT5F925ADJWK48PR07KV2W66EZQN4KYE146NY77DFM8GFCTXG",
+                    coinSig = 
"HGPAWTM2ZXVBZWYVSPS6S9DMSQWSVEJCQ78BN6WG2VND3PA7BQNHVE6142CGYX0VA82G5YP9SAV5YDNPNQJH2FTY5M6VM92QF6CB228",
+                    refreshed = true
+                )
+            )
+        )
+        for (v in vectors) {
+            assertEquals(v.request, recoup.createRequest(v.record))
+        }
+    }
+
+}

-- 
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]