diff options
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.kt | 155 |
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) + } + +} |