summaryrefslogtreecommitdiff
path: root/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
blob: 9b06756720b409222e87273b544cf3f2914b799f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * 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.wallet.kotlin.crypto

import net.taler.wallet.kotlin.Base32Crockford
import net.taler.wallet.kotlin.crypto.CryptoImpl.Companion.toByteArray
import net.taler.wallet.kotlin.exchange.DenominationRecord
import net.taler.wallet.kotlin.exchange.WireFee

internal class Signature(private val crypto: Crypto) {

    @Suppress("unused")
    companion object {
        const val RESERVE_WITHDRAW = 1200
        const val WALLET_COIN_DEPOSIT = 1201
        const val MASTER_DENOMINATION_KEY_VALIDITY = 1025
        const val MASTER_WIRE_FEES = 1028
        const val MASTER_WIRE_DETAILS = 1030
        const val WALLET_COIN_MELT = 1202
        const val TEST = 4242
        const val MERCHANT_PAYMENT_OK = 1104
        const val WALLET_COIN_RECOUP = 1203
        const val WALLET_COIN_LINK = 1204
        const val EXCHANGE_CONFIRM_RECOUP = 1039
        const val EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041
    }

    internal class PurposeBuilder(private val purposeNum: Int) {
        private val chunks = ArrayList<ByteArray>()

        fun put(bytes: ByteArray): PurposeBuilder {
            chunks.add(bytes)
            return this
        }

        fun build(): ByteArray {
            var payloadLen = 0
            for (c in chunks) payloadLen += c.size
            val size = 4 + 4 + payloadLen
            val bytes = ByteArray(size)
            size.toByteArray().copyInto(bytes, 0)
            purposeNum.toByteArray().copyInto(bytes, 4)
            var offset = 8
            for (c in chunks) {
                c.copyInto(bytes, offset)
                offset += c.size
            }
            return bytes
        }
    }

    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)
    }

}