Merge branch 'master' of git+ssh://git.taler.net/var/git/exchange

This commit is contained in:
Christian Grothoff 2016-06-20 10:19:58 +02:00
commit 029911c757
16 changed files with 1310 additions and 658 deletions

1
.gitignore vendored
View File

@ -44,6 +44,7 @@ src/exchange-tools/taler-exchange-reservemod
src/exchange-tools/taler-exchange-wire
src/exchangedb/perf-exchangedb
src/benchmark/taler-exchange-benchmark
src/benchmark/test_benchmark_home/.local/share/taler/exchange/live-keys/
src/json/test_json
src/wire/test_sepa_wireformat
src/wire/test_wire_plugin

View File

@ -583,6 +583,11 @@ protocol messages; denomination keys are used for blind-signing coins.
The exchange's long-term offline key is assumed to be known to both
customers and merchants and is certified by the auditors.
We avoid asking either customers or merchants to make trust desissions
about individual exchanges. Instead, they need only select the auditors.
Auditors must sign all the exchange's keys including, the individual
denomination keys.
As we are dealing with financial transactions, we explicitly describe
whenever entities need to safely commit data to persistent storage.
As long as those commitments persist, the protocol can be safely
@ -597,14 +602,20 @@ Merchants may discard information once payments from the exchange have
been received, assuming the records are also no longer needed for tax
purposes. The exchange's bank transfers dealing in traditional currency
are expected to be recorded for tax authorities to ensure taxability.
% FIXME: Auditor?
We use RSA for denomination keys and EdDSA over some eliptic curve
$\mathbb{E}$ for all other keys. Let $G$ denote the generator of
our elliptic curve $\mathbb{E}$.
\subsection{Withdrawal}
Let $G$ be the generator of an elliptic curve. To withdraw anonymous
digital coins, the customer first identifies a exchange with a
denomination public-private key pair $K := (K_s, K_p)$ corresponding
to a denomination the customer would like to withdraw, and then
performs the following interaction with the exchange:
To withdraw anonymous digital coins, the customer first selects an
exchange and one of its public denomination public keys $K_p$ whose
value $K_v$ corresponds to an amount the customer wishes to withdraw.
We let $K_s$ denote the exchange's private key corresponding to $K_p$.
Now the customer carries out the following interaction with the exchange:
% FIXME: We say withdrawal key in this document, but say reserve key in
% others, so probably withdrawal key should be renamed to reserve key.
@ -621,7 +632,7 @@ performs the following interaction with the exchange:
\item coin key $C := (c_s,C_p)$ with private key $c_s$ and public key $C_p := c_s G$,
\item blinding factor $b$, and commits $\langle W, C, b \rangle$ to disk.
\end{itemize}
\item The customer transfers an amount of money corresponding to at least $K_p$ to the exchange, with $W_p$ in the subject line of the transaction.
\item The customer transfers an amount of money corresponding to at least $K_v$ to the exchange, with $W_p$ in the subject line of the transaction.
\item The exchange receives the transaction and credits the $W_p$ reserve with the respective amount in its database.
\item The customer sends $S_W(B_b(C_p))$ to the exchange to request withdrawal of $C$; here, $B_b$ denotes Chaum-style blinding with blinding factor $b$.
\item The exchange checks if the same withdrawal request was issued before; in this case, it sends $S_{K}(B_b(C_p))$ to the customer.\footnote{$S_K$
@ -636,6 +647,7 @@ performs the following interaction with the exchange:
If the guards for the transaction fail, the exchange sends a descriptive error back to the customer,
with proof that it operated correctly.
Assuming the signature was valid, this would involve showing the transaction history for the reserve.
% FIXME: Is it really the whole history?
\item The customer computes and verifies the unblinded signature $S_K(C_p) = U_b(S_K(B_b(C_p)))$.
The customer saves the coin $\langle S_K(C_p), c_s \rangle$ to local wallet on disk.
\end{enumerate}
@ -644,9 +656,12 @@ performs the following interaction with the exchange:
\subsection{Exact and partial spending}
A customer can spend coins at a merchant, under the condition that the
merchant trusts the specific exchange that issued the coin. Merchants are
identified by their key $M := (m_s, M_p)$ where the public key $M_p$
must be known to the customer a priori.
merchant trusts the exchange that issued the coin.
% FIXME: Auditor here?
Merchants are identified by their public key $M_p = m_s G$ which the
customer's wallet learns through the merchant's webpage, which itself
must be authenticated with X.509c.
% FIXME: Is this correct?
We now describe the protocol between the customer, merchant, and exchange
for a transaction in which the customer spends a coin $C := (c_s, C_p)$
@ -676,8 +691,8 @@ with signature $\widetilde{C} := S_K(C_p)$
S_c(\widetilde{C}, m, f, H(a), H(p,r), M_p)$
and sends $\langle \mathcal{D}, D_j\rangle$ to the merchant,
where $D_j$ is the exchange which signed $K$.
\item The merchant gives $(\mathcal{D}, p, r)$ to the exchange, revealing $p$
only to the exchange.
\item The merchant gives $(\mathcal{D}, p, r)$ to the exchange, thereby
revealing $p$ only to the exchange.
\item The exchange validates $\mathcal{D}$ and checks for double spending.
If the coin has been involved in previous transactions and the new
one would exceed its remaining value, it sends an error

View File

@ -38,6 +38,7 @@ libtalerfakebank_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
-lmicrohttpd \
$(XLIB)

View File

@ -20,3 +20,11 @@ taler_exchange_benchmark_LDADD = \
-lgnunetcurl \
-lgnunetutil \
-ljansson
EXTRA_DIST = \
taler-exchange-benchmark.conf \
bank-details.json \
merchant-details.json \
test_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv \
test_benchmark_home/.config/taler/test.json \
test_benchmark_home/.config/taler/sepa.json

View File

@ -0,0 +1 @@
{"type":"test", "bank_uri":"http://localhost:8082/", "account_number":63}

View File

@ -1 +1 @@
{"type":"test", "bank_uri":"https://bank.test.taler.net/", "account_number":64}
{"type":"test", "bank_uri":"http://localhost:8082/", "account_number":64}

View File

@ -1 +0,0 @@
{"type":"test", "bank_uri":"https://bank.test.taler.net/", "account_number":63}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,91 @@
[benchmark]
SENDER_DETAILS = ~/exchange/src/benchmark/sender_details.json
MERCHANT_DETAILS = ~/exchange/src/benchmark/merchant_details.json
BANK_DETAILS = bank_details.json
MERCHANT_DETAILS = merchant_details.json
[PATHS]
# Persistant data storage for the testcase
TALER_TEST_HOME = test_benchmark_home/
[taler]
CURRENCY = KUDOS
[exchange]
# Wire format supported by the exchange
# We use 'test' for testing of the actual
# coin operations, and 'sepa' to test SEPA-specific routines.
WIREFORMAT = test
# HTTP port the exchange listens to
PORT = 8081
# How to access our database
DB = postgres
# Master public key used to sign the exchange's various keys
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
[exchangedb-postgres]
DB_CONN_STR = "postgres:///talercheck"
[exchange-wire-outgoing-test]
# What is the main website of the bank?
# (Not used unless the aggregator is run.)
BANK_URI = "http://localhost:8082/"
# From which account at the 'bank' should outgoing wire transfers be made?
BANK_ACCOUNT_NUMBER = 2
[exchange-wire-incoming-test]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
[coin_kudos_1]
value = KUDOS:1
duration_overlap = 5 minutes
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = KUDOS:0.00
fee_deposit = KUDOS:0.00
fee_refresh = KUDOS:0.00
fee_refund = KUDOS:0.00
rsa_keysize = 1024
[coin_kudos_2]
value = KUDOS:2
duration_overlap = 5 minutes
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = KUDOS:0.00
fee_deposit = KUDOS:0.00
fee_refresh = KUDOS:0.00
fee_refund = KUDOS:0.00
rsa_keysize = 1024
[coin_kudos_4]
value = KUDOS:4
duration_overlap = 5 minutes
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = KUDOS:0.00
fee_deposit = KUDOS:0.00
fee_refresh = KUDOS:0.00
fee_refund = KUDOS:0.00
rsa_keysize = 1024
[coin_kudos_8]
value = KUDOS:8
duration_overlap = 5 minutes
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = KUDOS:0.00
fee_deposit = KUDOS:0.00
fee_refresh = KUDOS:0.00
fee_refund = KUDOS:0.00
rsa_keysize = 1024

View File

@ -0,0 +1,9 @@
{
"name": "Max Musterman",
"bic": "COBADEFF370",
"type": "sepa",
"sig": "4EVRC2MCJPXQC8MC00831DNWEXMZAP4JQDDE1A7R6KR3MANG24RC1VQ55AX5A2E35S58VW1VSTENFTPHG5MWG9BSN8B8WXSV21KKW20",
"address": "Musterstadt",
"salt": "3KTM1ZRMWGEQPQ254S4R5R4Q8XM0ZYWTCTE01TZ76MVBSQ6RX7A5DR08WXVH1DCHR1R7ACRB7X0EVC2XDW1CBZM9WFSD9TRMZ90BR98",
"iban": "DE89370400440532013000"
}

View File

@ -0,0 +1,8 @@
{
"salt": "AZPRFVJ58NM6M7J5CZQPJAH3EW5DYM52AEZ9Y1C1ER3W94QV8D8TQKF6CK8MYQRA9QMSKDQTGZ306ZS9GQ0M6R01CJ20KPP49WFDZK8",
"name": "The exchange",
"account_number": 3,
"bank_uri": "http://localhost:8082/",
"type": "test",
"sig": "RPQXP9S4P8PQP7HEZQNRSZCT0ATNEP8GW0P5TPM34V5RX86FCD670V44R9NETSYDDKB8SZV7TKY9PAJYTY51D3VDWY9XXQ5BPFRXR28"
}

View File

@ -0,0 +1 @@
p<EFBFBD>^<5E>-<2D>33<33><33>XX<>!<04>\0q<30><71><EFBFBD><EFBFBD><18>mU<6D>_<EFBFBD><5F>

View File

@ -764,6 +764,7 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr
unsigned int i;
unsigned int j;
struct GNUNET_HashContext *hash_context;
struct TALER_Amount total;
/* build up melt data structure */
for (i=0;i<TALER_CNC_KAPPA;i++)
@ -799,10 +800,44 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr
md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
struct FreshCoinP);
for (j=0;j<fresh_pks_len;j++)
{
setup_fresh_coin (&md.fresh_coins[i][j],
&fresh_pks[j]);
}
}
/* verify that melt_amount is above total cost */
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (melt_amount->currency,
&total));
for (j=0;j<fresh_pks_len;j++)
{
if ( (GNUNET_OK !=
TALER_amount_add (&total,
&total,
&fresh_pks[j].value)) ||
(GNUNET_OK !=
TALER_amount_add (&total,
&total,
&fresh_pks[j].fee_withdraw)) )
{
GNUNET_break (0);
free_melt_data (&md);
return NULL;
}
}
if (1 ==
TALER_amount_cmp (&total,
melt_amount) )
{
/* Eh, this operation is more expensive than the
@a melt_amount. This is not OK. */
GNUNET_break (0);
free_melt_data (&md);
return NULL;
}
/* now compute melt session hash */
hash_context = GNUNET_CRYPTO_hash_context_start ();
for (i=0;i<fresh_pks_len;i++)

View File

@ -283,7 +283,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
"merchant_pub", GNUNET_JSON_from_data_auto (&rr.merchant),
"merchant_sig", GNUNET_JSON_from_data_auto (&merchant_sig)
);
GNUNET_assert (NULL != refund_obj);
rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle);
rh->exchange = exchange;
rh->cb = cb;

View File

@ -43,7 +43,7 @@ TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
[exchange-wire-outgoing-test]
# What is the main website of the bank?
BANK_URI = "http://localhost:8082/"
# Into which account at the 'bank' should (incoming) wire transfers be made?
# From which account at the 'bank' should outgoing wire transfers be made?
BANK_ACCOUNT_NUMBER = 2
[coin_eur_ct_1]

View File

@ -807,13 +807,14 @@ exchange_keys_update_cointype (void *cls,
&denomkey_issue);
if (GNUNET_OK !=
TALER_EXCHANGEDB_denomination_key_write (dkf,
&denomkey_issue))
&denomkey_issue))
{
fprintf (stderr,
"Failed to write denomination key information to file `%s'.\n",
dkf);
*ret = GNUNET_SYSERR;
GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key);
return;
}
if ( (NULL != auditor_output_file) &&
@ -828,9 +829,12 @@ exchange_keys_update_cointype (void *cls,
auditorrequestfile,
STRERROR (errno));
*ret = GNUNET_SYSERR;
GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key);
return;
}
GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key);
p.anchor = GNUNET_TIME_absolute_add (p.anchor,
p.duration_spend);
p.anchor = GNUNET_TIME_absolute_subtract (p.anchor,