diff options
Diffstat (limited to 'wallet/src/commonMain/kotlin/net/taler/lib/wallet')
19 files changed, 183 insertions, 567 deletions
diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Amount.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Amount.kt index 7273dba..e7ee929 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Amount.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Amount.kt @@ -17,7 +17,7 @@ package net.taler.lib.wallet import net.taler.lib.common.Amount -import net.taler.lib.wallet.crypto.CryptoImpl.Companion.toByteArray +import net.taler.lib.crypto.CryptoImpl.Companion.toByteArray fun Amount.toByteArray() = ByteArray(8 + 4 + 12).apply { value.toByteArray().copyInto(this, 0, 0, 8) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Base32Crockford.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Base32Crockford.kt deleted file mode 100644 index 2517e85..0000000 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Base32Crockford.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.lib.wallet - - -class EncodingException : Exception("Invalid encoding") - - -object Base32Crockford { - - private fun ByteArray.getIntAt(index: Int): Int { - val x = this[index].toInt() - return if (x >= 0) x else (x + 256) - } - - private var encTable = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" - - fun encode(data: ByteArray): String { - val sb = StringBuilder() - val size = data.size - var bitBuf = 0 - var numBits = 0 - var pos = 0 - while (pos < size || numBits > 0) { - if (pos < size && numBits < 5) { - val d = data.getIntAt(pos++) - bitBuf = (bitBuf shl 8) or d - numBits += 8 - } - if (numBits < 5) { - // zero-padding - bitBuf = bitBuf shl (5 - numBits) - numBits = 5 - } - val v = bitBuf.ushr(numBits - 5) and 31 - sb.append(encTable[v]) - numBits -= 5 - } - return sb.toString() - } - - fun decode(encoded: String): ByteArray { - val size = encoded.length - var bitpos = 0 - var bitbuf = 0 - var readPosition = 0 - var writePosition = 0 - val out = ByteArray(calculateDecodedDataLength(size)) - - while (readPosition < size || bitpos > 0) { - //println("at position $readPosition with bitpos $bitpos") - if (readPosition < size) { - val v = getValue(encoded[readPosition++]) - bitbuf = (bitbuf shl 5) or v - bitpos += 5 - } - while (bitpos >= 8) { - val d = (bitbuf ushr (bitpos - 8)) and 0xFF - out[writePosition] = d.toByte() - writePosition++ - bitpos -= 8 - } - if (readPosition == size && bitpos > 0) { - bitbuf = (bitbuf shl (8 - bitpos)) and 0xFF - bitpos = if (bitbuf == 0) 0 else 8 - } - } - return out - } - - private fun getValue(chr: Char): Int { - var a = chr - when (a) { - 'O', 'o' -> a = '0' - 'i', 'I', 'l', 'L' -> a = '1' - 'u', 'U' -> a = 'V' - } - if (a in '0'..'9') - return a - '0' - if (a in 'a'..'z') - a = a.toUpperCase() - var dec = 0 - if (a in 'A'..'Z') { - if ('I' < a) dec++ - if ('L' < a) dec++ - if ('O' < a) dec++ - if ('U' < a) dec++ - return a - 'A' + 10 - dec - } - throw EncodingException() - } - - /** - * Compute the length of the resulting string when encoding data of the given size - * in bytes. - * - * @param dataSize size of the data to encode in bytes - * @return size of the string that would result from encoding - */ - private fun calculateEncodedStringLength(dataSize: Int): Int { - return (dataSize * 8 + 4) / 5 - } - - /** - * Compute the length of the resulting data in bytes when decoding a (valid) string of the - * given size. - * - * @param stringSize size of the string to decode - * @return size of the resulting data in bytes - */ - fun calculateDecodedDataLength(stringSize: Int): Int { - return stringSize * 5 / 8 - } - -} diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Time.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Time.kt index e67474b..6cb5a5c 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Time.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Time.kt @@ -17,7 +17,7 @@ package net.taler.lib.wallet import net.taler.lib.common.Timestamp -import net.taler.lib.wallet.crypto.CryptoImpl.Companion.toByteArray +import net.taler.lib.crypto.CryptoImpl.Companion.toByteArray fun Timestamp.roundedToByteArray(): ByteArray = ByteArray(8).apply { (truncateSeconds().ms * 1000L).toByteArray().copyInto(this) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Exchanges.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Exchanges.kt new file mode 100644 index 0000000..2542dd6 --- /dev/null +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Exchanges.kt @@ -0,0 +1,55 @@ +/* + * 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.lib.wallet.api + +import net.taler.lib.wallet.exchange.ExchangeRecord + +data class ExchangeListItem( + val exchangeBaseUrl: String, + val currency: String, + val paytoUris: List<String> +) { + companion object { + fun fromExchangeRecord(exchange: ExchangeRecord): ExchangeListItem? { + return if (exchange.details == null || exchange.wireInfo == null) null + else ExchangeListItem( + exchangeBaseUrl = exchange.baseUrl, + currency = exchange.details.currency, + paytoUris = exchange.wireInfo.accounts.map { + it.paytoUri + } + ) + } + } +} + +data class GetExchangeTosResult( + /** + * Markdown version of the current ToS. + */ + val tos: String, + + /** + * Version tag of the current ToS. + */ + val currentEtag: String, + + /** + * Version tag of the last ToS that the user has accepted, if any. + */ + val acceptedEtag: String? = null +) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Version.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Version.kt index ba9be3c..12916d3 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/Version.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Version.kt @@ -14,7 +14,7 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.lib.wallet +package net.taler.lib.wallet.api import net.taler.lib.common.Version diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/WalletApi.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/WalletApi.kt new file mode 100644 index 0000000..026c682 --- /dev/null +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/WalletApi.kt @@ -0,0 +1,29 @@ +/* + * 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.lib.wallet.api + +import net.taler.lib.common.Amount + +public interface WalletApi { + fun getVersions(): SupportedVersions + fun getWithdrawalDetailsForUri(talerWithdrawUri: String): WithdrawalDetailsForUri + fun getWithdrawalDetailsForAmount(exchangeBaseUrl: String, amount: Amount): WithdrawalDetails + fun listExchanges(): List<ExchangeListItem> + fun addExchange(exchangeBaseUrl: String): ExchangeListItem + fun getExchangeTos(exchangeBaseUrl: String): GetExchangeTosResult + fun setExchangeTosAccepted(exchangeBaseUrl: String, acceptedEtag: String) +} diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/WalletApi.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/WalletFactory.kt index bfe2825..d56f80c 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/WalletApi.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/WalletFactory.kt @@ -14,23 +14,27 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.lib.wallet +package net.taler.lib.wallet.api import io.ktor.client.HttpClient import net.taler.lib.common.Amount import net.taler.lib.common.Timestamp import net.taler.lib.common.Version -import net.taler.lib.wallet.crypto.Crypto -import net.taler.lib.wallet.crypto.CryptoFactory +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.CryptoFactory +import net.taler.lib.wallet.Db +import net.taler.lib.wallet.DbFactory import net.taler.lib.wallet.crypto.Signature import net.taler.lib.wallet.exchange.Exchange -import net.taler.lib.wallet.exchange.ExchangeListItem -import net.taler.lib.wallet.exchange.GetExchangeTosResult +import net.taler.lib.wallet.getDefaultHttpClient import net.taler.lib.wallet.operations.Withdraw -import net.taler.lib.wallet.operations.WithdrawalDetails -import net.taler.lib.wallet.operations.WithdrawalDetailsForUri -public class WalletApi { + +public expect class WalletFactory { + fun createWalletApi(): WalletApi +} + +internal class WalletApiImpl { private val httpClient: HttpClient = getDefaultHttpClient() private val db: Db = DbFactory().openDb() @@ -39,7 +43,7 @@ public class WalletApi { private val exchangeManager: Exchange = Exchange(crypto, signature, httpClient, db = db) private val withdrawManager = Withdraw(httpClient, db, crypto, signature, exchangeManager) - public fun getVersions(): SupportedVersions { + fun getVersions(): SupportedVersions { return SupportedVersions( walletVersion = Version(8, 0, 0), exchangeVersion = Version(8, 0, 0), @@ -48,7 +52,7 @@ public class WalletApi { ) } - public suspend fun getWithdrawalDetailsForUri(talerWithdrawUri: String): WithdrawalDetailsForUri { + suspend fun getWithdrawalDetailsForUri(talerWithdrawUri: String): WithdrawalDetailsForUri { val bankInfo = withdrawManager.getBankInfo(talerWithdrawUri) return WithdrawalDetailsForUri( amount = bankInfo.amount, @@ -57,7 +61,7 @@ public class WalletApi { ) } - public suspend fun getWithdrawalDetailsForAmount( + suspend fun getWithdrawalDetailsForAmount( exchangeBaseUrl: String, amount: Amount ): WithdrawalDetails { @@ -69,19 +73,19 @@ public class WalletApi { ) } - public suspend fun listExchanges(): List<ExchangeListItem> { + suspend fun listExchanges(): List<ExchangeListItem> { return db.listExchanges().mapNotNull { exchange -> ExchangeListItem.fromExchangeRecord(exchange) } } - public suspend fun addExchange(exchangeBaseUrl: String): ExchangeListItem { + suspend fun addExchange(exchangeBaseUrl: String): ExchangeListItem { val exchange = exchangeManager.updateFromUrl(exchangeBaseUrl) db.put(exchange) return ExchangeListItem.fromExchangeRecord(exchange) ?: TODO("error handling") } - public suspend fun getExchangeTos(exchangeBaseUrl: String): GetExchangeTosResult { + suspend fun getExchangeTos(exchangeBaseUrl: String): GetExchangeTosResult { val record = db.getExchangeByBaseUrl(exchangeBaseUrl) ?: TODO("error handling") return GetExchangeTosResult( tos = record.termsOfServiceText ?: TODO("error handling"), @@ -90,7 +94,7 @@ public class WalletApi { ) } - public suspend fun setExchangeTosAccepted(exchangeBaseUrl: String, acceptedEtag: String) { + suspend fun setExchangeTosAccepted(exchangeBaseUrl: String, acceptedEtag: String) { db.transaction { val record = getExchangeByBaseUrl(exchangeBaseUrl) ?: TODO("error handling") val updatedRecord = record.copy( diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Withdrawal.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Withdrawal.kt new file mode 100644 index 0000000..88c96a4 --- /dev/null +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/api/Withdrawal.kt @@ -0,0 +1,53 @@ +/* + * 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.lib.wallet.api + +import net.taler.lib.common.Amount + +public data class WithdrawalDetailsForUri( + /** + * The amount that the user wants to withdraw + */ + val amount: Amount, + + /** + * Exchange suggested by the wallet + */ + val defaultExchangeBaseUrl: String?, + + /** + * A list of exchanges that can be used for this withdrawal + */ + val possibleExchanges: List<ExchangeListItem> +) + +public data class WithdrawalDetails( + /** + * Did the user accept the current version of the exchange's terms of service? + */ + val tosAccepted: Boolean, + + /** + * Amount that will be transferred to the exchange. + */ + val amountRaw: Amount, + + /** + * Amount that will be added to the user's wallet balance. + */ + val amountEffective: Amount +) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Crypto.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Crypto.kt deleted file mode 100644 index cbb486a..0000000 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Crypto.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.lib.wallet.crypto - -internal interface Crypto { - fun sha256(input: ByteArray): ByteArray - fun sha512(input: ByteArray): ByteArray - fun getHashSha512State(): HashSha512State - fun getRandomBytes(num: Int): ByteArray - fun eddsaGetPublic(eddsaPrivateKey: ByteArray): ByteArray - fun ecdheGetPublic(ecdhePrivateKey: ByteArray): ByteArray - fun createEddsaKeyPair(): EddsaKeyPair - fun createEcdheKeyPair(): EcdheKeyPair - fun eddsaSign(msg: ByteArray, eddsaPrivateKey: ByteArray): ByteArray - fun eddsaVerify(msg: ByteArray, sig: ByteArray, eddsaPub: ByteArray): Boolean - fun keyExchangeEddsaEcdhe(eddsaPrivateKey: ByteArray, ecdhePublicKey: ByteArray): ByteArray - fun keyExchangeEcdheEddsa(ecdhePrivateKey: ByteArray, eddsaPublicKey: ByteArray): ByteArray - fun kdf(outputLength: Int, ikm: ByteArray, salt: ByteArray, info: ByteArray): ByteArray - fun rsaBlind(hm: ByteArray, bks: ByteArray, rsaPubEnc: ByteArray): ByteArray - fun rsaUnblind(sig: ByteArray, rsaPubEnc: ByteArray, bks: ByteArray): ByteArray - fun rsaVerify(hm: ByteArray, rsaSig: ByteArray, rsaPubEnc: ByteArray): Boolean - fun setupRefreshPlanchet(secretSeed: ByteArray, coinNumber: Int): FreshCoin -} - -interface HashSha512State { - fun update(data: ByteArray): HashSha512State - fun final(): ByteArray -} -class EddsaKeyPair(val privateKey: ByteArray, val publicKey: ByteArray) -class EcdheKeyPair(val privateKey: ByteArray, val publicKey: ByteArray) -data class FreshCoin(val coinPublicKey: ByteArray, val coinPrivateKey: ByteArray, val bks: ByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - other as FreshCoin - if (!coinPublicKey.contentEquals(other.coinPublicKey)) return false - if (!coinPrivateKey.contentEquals(other.coinPrivateKey)) return false - if (!bks.contentEquals(other.bks)) return false - return true - } - - override fun hashCode(): Int { - var result = coinPublicKey.contentHashCode() - result = 31 * result + coinPrivateKey.contentHashCode() - result = 31 * result + bks.contentHashCode() - return result - } -} - -internal expect object CryptoFactory { - internal fun getCrypto(): Crypto -} - -private val hexArray = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') - -fun ByteArray.toHexString(): String { - val hexChars = CharArray(this.size * 2) - for (j in this.indices) { - val v = (this[j].toInt() and 0xFF) - hexChars[j * 2] = hexArray[v ushr 4] - hexChars[j * 2 + 1] = hexArray[v and 0x0F] - } - return String(hexChars) -} diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/CryptoImpl.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/CryptoImpl.kt deleted file mode 100644 index 6b7cb8e..0000000 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/CryptoImpl.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.lib.wallet.crypto - -abstract class CryptoImpl : Crypto { - - companion object { - fun Int.toByteArray(): ByteArray { - val bytes = ByteArray(4) - bytes[3] = (this and 0xFFFF).toByte() - bytes[2] = ((this ushr 8) and 0xFFFF).toByte() - bytes[1] = ((this ushr 16) and 0xFFFF).toByte() - bytes[0] = ((this ushr 24) and 0xFFFF).toByte() - return bytes - } - - fun Long.toByteArray() = ByteArray(8).apply { - var l = this@toByteArray - for (i in 7 downTo 0) { - this[i] = (l and 0xFF).toByte() - l = l shr 8 - } - } - } - - override fun kdf(outputLength: Int, ikm: ByteArray, salt: ByteArray, info: ByteArray): ByteArray { - return Kdf.kdf(outputLength, ikm, salt, info, { sha256(it) }, { sha512(it) }) - } - - override fun setupRefreshPlanchet(secretSeed: ByteArray, coinNumber: Int): FreshCoin { - val info = "taler-coin-derivation".encodeToByteArray() - val salt = coinNumber.toByteArray() - val out = kdf(64, secretSeed, salt, info) - val coinPrivateKey = out.copyOfRange(0, 32) - val bks = out.copyOfRange(32, 64) - return FreshCoin(eddsaGetPublic(coinPrivateKey), coinPrivateKey, bks) - } - -} diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Deposit.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Deposit.kt deleted file mode 100644 index 66255d8..0000000 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Deposit.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.lib.wallet.crypto - -import net.taler.lib.common.Amount -import net.taler.lib.common.Timestamp -import net.taler.lib.wallet.Base32Crockford -import net.taler.lib.wallet.crypto.Signature.Companion.WALLET_COIN_DEPOSIT -import net.taler.lib.wallet.roundedToByteArray -import net.taler.lib.wallet.toByteArray - -/** - * Deposit operations are requested by a merchant during a transaction. - * For the deposit operation, the merchant has to obtain the deposit permission for a coin - * from their customer who owns the coin. - * - * When depositing a coin, the merchant is credited an amount specified in the deposit permission, - * possibly a fraction of the total coin’s value, - * minus the deposit fee as specified by the coin’s denomination. - */ -internal class Deposit(private val crypto: Crypto) { - - /** - * Private data required to make a deposit permission. - */ - data class DepositInfo( - val exchangeBaseUrl: String, - val contractTermsHash: String, - val coinPublicKey: String, - val coinPrivateKey: String, - val spendAmount: Amount, - val timestamp: Timestamp, - val refundDeadline: Timestamp, - val merchantPublicKey: String, - val feeDeposit: Amount, - val wireInfoHash: String, - val denomPublicKey: String, - val denomSignature: String - ) - - /** - * Deposit permission for a single coin. - */ - // TODO rename _ - data class CoinDepositPermission( - /** - * Signature by the coin. - */ - val coinSignature: String, - /** - * Public key of the coin being spend. - */ - val coinPublicKey: String, - /** - * Signature made by the denomination public key. - */ - val denomSignature: String, - /** - * The denomination public key associated with this coin. - */ - val denomPublicKey: String, - /** - * The amount that is subtracted from this coin with this payment. - */ - val contribution: String, - /** - * URL of the exchange this coin was withdrawn from. - */ - val exchangeBaseUrl: String - ) - - /** - * Generate updated coins (to store in the database) and deposit permissions for each given coin. - */ - fun signDepositPermission(depositInfo: DepositInfo): CoinDepositPermission { - val d = Signature.PurposeBuilder(WALLET_COIN_DEPOSIT) - .put(Base32Crockford.decode(depositInfo.contractTermsHash)) - .put(Base32Crockford.decode(depositInfo.wireInfoHash)) - .put(depositInfo.timestamp.roundedToByteArray()) - .put(depositInfo.refundDeadline.roundedToByteArray()) - .put(depositInfo.spendAmount.toByteArray()) - .put(depositInfo.feeDeposit.toByteArray()) - .put(Base32Crockford.decode(depositInfo.merchantPublicKey)) - .put(Base32Crockford.decode(depositInfo.coinPublicKey)) - .build() - val coinPriv = Base32Crockford.decode(depositInfo.coinPrivateKey); - val coinSig = crypto.eddsaSign(d, coinPriv) - return CoinDepositPermission( - coinPublicKey = depositInfo.coinPublicKey, - coinSignature = Base32Crockford.encode(coinSig), - contribution = depositInfo.spendAmount.toJSONString(), - denomPublicKey = depositInfo.denomPublicKey, - exchangeBaseUrl = depositInfo.exchangeBaseUrl, - denomSignature = depositInfo.denomSignature - ) - } - -} diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Kdf.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Kdf.kt deleted file mode 100644 index 2d714f8..0000000 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Kdf.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.lib.wallet.crypto - -import kotlin.experimental.xor -import kotlin.math.ceil - -internal object Kdf { - - const val HMAC_SHA256_BLOCK_SIZE = 64 - const val HMAC_SHA512_BLOCK_SIZE = 128 - - fun kdf( - outputLength: Int, - ikm: ByteArray, - salt: ByteArray, - info: ByteArray, - sha256: (ByteArray) -> ByteArray, - sha512: (ByteArray) -> ByteArray - ): ByteArray { - //extract - val prk = hmacSha512(salt, ikm, sha512) - - // expand - val n = ceil(outputLength.toDouble() / 32).toInt() - val output = ByteArray(n * 32) - for (i in 0 until n) { - val buf: ByteArray - if (i == 0) { - buf = ByteArray(info.size + 1) - info.copyInto(buf) - } else { - buf = ByteArray(info.size + 1 + 32) - for (j in 0 until 32) { - buf[j] = output[(i - 1) * 32 + j] - } - info.copyInto(buf, destinationOffset = 32) - } - buf[buf.size - 1] = (i + 1).toByte() - val chunk = hmacSha256(prk, buf, sha256) - chunk.copyInto(output, destinationOffset = i * 32) - } - return output.copyOfRange(0, outputLength) - } - - fun hmacSha256(key: ByteArray, message: ByteArray, sha256: (ByteArray) -> ByteArray): ByteArray { - return hmac(HMAC_SHA256_BLOCK_SIZE, key, message) { sha256(it) } - } - - fun hmacSha512(key: ByteArray, message: ByteArray, sha512: (ByteArray) -> ByteArray): ByteArray { - return hmac(HMAC_SHA512_BLOCK_SIZE, key, message) { sha512(it) } - } - - private fun hmac(blockSize: Int, key: ByteArray, message: ByteArray, hash: (ByteArray) -> ByteArray): ByteArray { - var newKey = key - if (newKey.size > blockSize) newKey = hash(newKey) - if (newKey.size < blockSize) newKey = ByteArray(blockSize).apply { - newKey.copyInto(this) - } - val okp = ByteArray(blockSize) - val ikp = ByteArray(blockSize) - for (i in 0 until blockSize) { - ikp[i] = newKey[i] xor 0x36 - okp[i] = newKey[i] xor 0x5c - } - val b1 = ByteArray(blockSize + message.size) - ikp.copyInto(b1) - message.copyInto(b1, destinationOffset = blockSize) - val h0 = hash(b1) - val b2 = ByteArray(blockSize + h0.size) - okp.copyInto(b2) - h0.copyInto(b2, destinationOffset = blockSize) - return hash(b2) - } - -} diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Planchet.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Planchet.kt index 22aa786..fa87348 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Planchet.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Planchet.kt @@ -17,7 +17,10 @@ package net.taler.lib.wallet.crypto import net.taler.lib.common.Amount -import net.taler.lib.wallet.Base32Crockford +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.EddsaKeyPair +import net.taler.lib.wallet.crypto.Signature.PurposeBuilder +import net.taler.lib.crypto.Base32Crockford import net.taler.lib.wallet.toByteArray internal class Planchet(private val crypto: Crypto) { @@ -53,7 +56,7 @@ internal class Planchet(private val crypto: Crypto) { val denomPubHash = crypto.sha512(denomPub) val evHash = crypto.sha512(ev) - val withdrawRequest = Signature.PurposeBuilder(Signature.RESERVE_WITHDRAW) + val withdrawRequest = PurposeBuilder(Signature.RESERVE_WITHDRAW) .put(reservePub) .put(amountWithFee.toByteArray()) .put(req.feeWithdraw.toByteArray()) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Recoup.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Recoup.kt index c7678e4..b87eff2 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Recoup.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Recoup.kt @@ -16,10 +16,12 @@ package net.taler.lib.wallet.crypto -import net.taler.lib.wallet.Base32Crockford +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.Base32Crockford import net.taler.lib.wallet.CoinRecord import net.taler.lib.wallet.CoinSourceType.REFRESH import net.taler.lib.wallet.crypto.Signature.Companion.WALLET_COIN_RECOUP +import net.taler.lib.wallet.crypto.Signature.PurposeBuilder internal class Recoup(private val crypto: Crypto) { @@ -64,7 +66,7 @@ internal class Recoup(private val crypto: Crypto) { * Create and sign a message to recoup a coin. */ fun createRequest(coin: CoinRecord): Request { - val p = Signature.PurposeBuilder(WALLET_COIN_RECOUP) + val p = PurposeBuilder(WALLET_COIN_RECOUP) .put(Base32Crockford.decode(coin.coinPub)) .put(Base32Crockford.decode(coin.denomPubHash)) .put(Base32Crockford.decode(coin.blindingKey)) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Refresh.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Refresh.kt index 90478ef..8098437 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Refresh.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Refresh.kt @@ -17,9 +17,11 @@ package net.taler.lib.wallet.crypto import net.taler.lib.common.Amount -import net.taler.lib.wallet.Base32Crockford +import net.taler.lib.crypto.Base32Crockford import net.taler.lib.wallet.CoinRecord import net.taler.lib.common.Timestamp +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.EcdheKeyPair import net.taler.lib.wallet.crypto.Signature.Companion.WALLET_COIN_LINK import net.taler.lib.wallet.crypto.Signature.Companion.WALLET_COIN_MELT import net.taler.lib.wallet.crypto.Signature.PurposeBuilder diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Signature.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Signature.kt index 006c004..8828509 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Signature.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/crypto/Signature.kt @@ -16,8 +16,9 @@ package net.taler.lib.wallet.crypto -import net.taler.lib.wallet.Base32Crockford -import net.taler.lib.wallet.crypto.CryptoImpl.Companion.toByteArray +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.Base32Crockford +import net.taler.lib.crypto.CryptoImpl.Companion.toByteArray import net.taler.lib.wallet.exchange.DenominationRecord import net.taler.lib.wallet.exchange.WireFee import net.taler.lib.wallet.roundedToByteArray diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Denomination.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Denomination.kt index fca9e3f..a515d96 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Denomination.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Denomination.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.Serializable import net.taler.lib.common.Amount import net.taler.lib.common.Duration import net.taler.lib.common.Timestamp -import net.taler.lib.wallet.Base32Crockford +import net.taler.lib.crypto.Base32Crockford import net.taler.lib.wallet.exchange.DenominationStatus.Unverified import net.taler.lib.wallet.exchange.DenominationStatus.VerifiedGood diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Exchange.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Exchange.kt index 4d89cd6..e685040 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Exchange.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/exchange/Exchange.kt @@ -27,11 +27,11 @@ import io.ktor.http.HttpStatusCode import net.taler.lib.common.Amount import net.taler.lib.common.Timestamp import net.taler.lib.common.Version -import net.taler.lib.wallet.Base32Crockford +import net.taler.lib.crypto.Base32Crockford import net.taler.lib.wallet.Db import net.taler.lib.wallet.DbFactory -import net.taler.lib.wallet.crypto.Crypto -import net.taler.lib.wallet.crypto.CryptoFactory +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.CryptoFactory import net.taler.lib.wallet.crypto.Signature import net.taler.lib.wallet.exchange.DenominationStatus.Unverified import net.taler.lib.wallet.exchange.ExchangeUpdateReason.Initial @@ -223,40 +223,3 @@ internal class Exchange( } } - - -data class ExchangeListItem( - val exchangeBaseUrl: String, - val currency: String, - val paytoUris: List<String> -) { - companion object { - fun fromExchangeRecord(exchange: ExchangeRecord): ExchangeListItem? { - return if (exchange.details == null || exchange.wireInfo == null) null - else ExchangeListItem( - exchangeBaseUrl = exchange.baseUrl, - currency = exchange.details.currency, - paytoUris = exchange.wireInfo.accounts.map { - it.paytoUri - } - ) - } - } -} - -data class GetExchangeTosResult( - /** - * Markdown version of the current ToS. - */ - val tos: String, - - /** - * Version tag of the current ToS. - */ - val currentEtag: String, - - /** - * Version tag of the last ToS that the user has accepted, if any. - */ - val acceptedEtag: String? = null -) diff --git a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/operations/Withdraw.kt b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/operations/Withdraw.kt index 69c68d1..84851ba 100644 --- a/wallet/src/commonMain/kotlin/net/taler/lib/wallet/operations/Withdraw.kt +++ b/wallet/src/commonMain/kotlin/net/taler/lib/wallet/operations/Withdraw.kt @@ -23,12 +23,11 @@ import kotlinx.serialization.Serializable import net.taler.lib.common.Amount import net.taler.lib.common.TalerUri.parseWithdrawUri import net.taler.lib.common.Timestamp -import net.taler.lib.common.Version import net.taler.lib.common.Version.VersionMatchResult +import net.taler.lib.crypto.Crypto +import net.taler.lib.crypto.CryptoFactory import net.taler.lib.wallet.Db import net.taler.lib.wallet.DbFactory -import net.taler.lib.wallet.crypto.Crypto -import net.taler.lib.wallet.crypto.CryptoFactory import net.taler.lib.wallet.crypto.Signature import net.taler.lib.wallet.exchange.DenominationRecord import net.taler.lib.wallet.exchange.DenominationSelectionInfo @@ -36,7 +35,6 @@ import net.taler.lib.wallet.exchange.DenominationStatus.Unverified import net.taler.lib.wallet.exchange.DenominationStatus.VerifiedBad import net.taler.lib.wallet.exchange.DenominationStatus.VerifiedGood import net.taler.lib.wallet.exchange.Exchange -import net.taler.lib.wallet.exchange.ExchangeListItem import net.taler.lib.wallet.exchange.ExchangeRecord import net.taler.lib.wallet.exchange.ExchangeWireInfo import net.taler.lib.wallet.exchange.SelectedDenomination @@ -46,7 +44,9 @@ internal class Withdraw( private val httpClient: HttpClient = getDefaultHttpClient(), private val db: Db = DbFactory().openDb(), private val crypto: Crypto = CryptoFactory.getCrypto(), - private val signature: Signature = Signature(crypto), + private val signature: Signature = Signature( + crypto + ), private val exchange: Exchange = Exchange(crypto, signature, httpClient, db = db) ) { @@ -270,37 +270,3 @@ internal class Withdraw( } } - -data class WithdrawalDetailsForUri( - /** - * The amount that the user wants to withdraw - */ - val amount: Amount, - - /** - * Exchange suggested by the wallet - */ - val defaultExchangeBaseUrl: String?, - - /** - * A list of exchanges that can be used for this withdrawal - */ - val possibleExchanges: List<ExchangeListItem> -) - -data class WithdrawalDetails( - /** - * Did the user accept the current version of the exchange's terms of service? - */ - val tosAccepted: Boolean, - - /** - * Amount that will be transferred to the exchange. - */ - val amountRaw: Amount, - - /** - * Amount that will be added to the user's wallet balance. - */ - val amountEffective: Amount -) |