summaryrefslogtreecommitdiff
path: root/wallet/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
diff options
context:
space:
mode:
Diffstat (limited to 'wallet/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt')
-rw-r--r--wallet/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt155
1 files changed, 155 insertions, 0 deletions
diff --git a/wallet/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt b/wallet/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
new file mode 100644
index 0000000..9b06756
--- /dev/null
+++ b/wallet/src/commonMain/kotlin/net/taler/wallet/kotlin/crypto/Signature.kt
@@ -0,0 +1,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)
+ }
+
+}