diff --git a/doc/paper/taler.tex b/doc/paper/taler.tex index a728471c0..e3fb83592 100644 --- a/doc/paper/taler.tex +++ b/doc/paper/taler.tex @@ -191,18 +191,20 @@ his message to the merchant. \begin{figure}[h] \centering \begin{tikzpicture} + \tikzstyle{def} = [node distance= 5em and 7em, inner sep=1em, outer sep=.3em]; + \node (origin) at (0,0) {}; + \node (mint) [def,above=of origin,draw]{Mint}; + \node (customer) [def, draw, below left=of origin] {Customer}; + \node (merchant) [def, draw, below right=of origin] {Merchant}; + \node (auditor) [def, draw, above right=of origin]{Auditor}; + \tikzstyle{C} = [color=black, line width=1pt] -\tikzstyle{def} = [node distance= 7em and 10em, inner sep=1em, outer sep=.3em]; -\node (origin) at (0,0) {}; -\node (mint) [def,above=of origin,draw]{Mint}; -\node (customer) [def, draw, below left=of origin] {Customer}; -\node (merchant) [def, draw, below right=of origin] {Merchant}; + \draw [<-, C] (customer) -- (mint) node [midway, above, sloped] (TextNode) {withdraw coins}; + \draw [<-, C] (mint) -- (merchant) node [midway, above, sloped] (TextNode) {deposit coins}; + \draw [<-, C] (merchant) -- (customer) node [midway, above, sloped] (TextNode) {spend coins}; + \draw [<-, C] (mint) -- (auditor) node [midway, above, sloped] (TextNode) {verify}; -\tikzstyle{C} = [color=black, line width=1pt] -\draw [<-, C] (customer) -- (mint) node [midway, above, sloped] (TextNode) {withdraw coins}; -\draw [<-, C] (mint) -- (merchant) node [midway, above, sloped] (TextNode) {deposit coins}; -\draw [<-, C] (merchant) -- (customer) node [midway, above, sloped] (TextNode) {spend coins}; \end{tikzpicture} \caption{Taler's system model for the payment system is based on Chaum~\cite{chaum1983blind}.} \label{fig:cmm} @@ -777,13 +779,78 @@ and $G$ is the generator of the elliptic curve. \subsection{Linking} -% FIXME: explain better... -For a coin that was successfully refreshed, the mint responds to -a request $S_{C'}(\mathtt{link})$ with $(T^{(\gamma)}_p$, $E_{\gamma}, \widetilde{C})$. +For a coin that was successfully refreshed, the mint responds to a +request $S_{C'}(\mathtt{link})$ with $(T^{(\gamma)}_p$, $E_{\gamma}, +\widetilde{C})$. This allows the owner of the old coin to also obtain the private key of the new coin, even if the refreshing protocol was illicitly -executed by another party who learned $C'_s$ from the old owner. +executed by another party who learned $C'_s$ from the old owner. As a +result, linking ensures that access to the new coins minted by the +refresh protocol is always {\em shared} with the owner of the melted +coins. This makes it impossible to abuse the refresh protocol for +{\em transactions}. + +The linking request is not expected to be used at all during ordinary +operation of Taler. If the refresh protocol is used by Alice to +obtain change as designed, she already knows all of the information +and thus has little reason to request it via the linking protocol. +The fundamental reason why the mint must provide the link protocol is +simply to provide a threat: if Bob were to use the refresh protocol +for a transaction of funds from Alice to him, Alice may use a link +request to gain shared access to Bob's coins. Thus, this threat +prevents Bob from abusing the refresh protocol to evade taxation on +transactions. + +The auditor can anonymously check if the mint correctly implements the +link request, thus preventing the mint operator from legally disabling +this protocol component. Without the link operation, Taler would +devolve into a payment system where both sides can be anonymous, and +thus no longer provide taxability. + + +\subsection{Error handling} + +During operation, there are three main types of errors that are +expected. First, in the case of faulty clients, the responding server +will generate an error message with detailed cryptographic proofs +demonstrating that the client was faulty, for example by providing +proof of double-spending or providing the previous commit and the +location of the missmatch in the case of the reveal step in the +refresh protocol. It is also possible that the server may claim that +the client has been violating the protocol. In these cases, the +clients should verify any proofs provided and if they are acceptable, +notify the user that they are somehow ``faulty''. Similar, if the +server indicates that the client is violating the protocol, the +client should record the interaction and enable the user to file a +bug report with the developer. + +The second case is a faulty mint service provider. Such faults will +be detected because of protocol violations (for example, by providing +a faulty proof or no proof). In this case, the client is expected to +notify the auditor, providing a transcript of the interaction. The +auditor can then (anonymously) replay the transaction, and either +provide the (now) correct response to the client or take appropriate +legal action against the faulty provider. + +The third case are transient failures, such as network failures or +temporary hardware failures at the mint service provider. Here, the +client may receive an explicit protocol indication (such as an HTTP +response code 500 ``internal server error'') or simply no response. +The appropriate behavior for the client is to automatically retry +(after 1s, twice more at randomized times within 1 minute). If those +three attempts fail, the user should be informed about the delay. The +client should then retry another three times within the next 24h, and +after that time the auditor be informed about the outage. + +Using this process, short term failures should be effectively obscured +from the user, while malicious behavior is reported to the auditor who +can then presumably rectify the situation, for example by shutting +down the operator (while providing an opportunity for customers to +receive refunds for the coins in circulation). To ensure that such +refunds are possible, the operator is expected to always provide +adequate securities for the amount of coins in circulation as part of +the certification process. \subsection{Refunds} @@ -849,15 +916,15 @@ transfer. %suitable for money laundering, we are optimistic that states will find %the design desirable. -We did not yet perform performance measurements for the various -operations. However, we are pretty sure that the computational and -bandwidth cost for transactions described in this paper is likely -small compared to other business costs for the mint. We expect costs -within the system to be dominated by the (replicated, transactional) -database. However, these expenses are again likely small in relation -to the business cost of currency transfers using traditional banking. -Here, mint operators should be able to reduce their expenses by -aggregating multiple transfers to the same merchant. +We performed some initial performance measurements for the various +operations. The main conclusion was that the computational and +bandwidth cost for transactions described in this paper is smaller +than $10^{-3}$ cent/transaction, and thus dwarfed by the other +business costs for the mint. However, this figure excludes the cost +of currency transfers using traditional banking, which a mint operator +would ultimately have to interact with. Here, mint operators should +be able to reduce their expenses by aggregating multiple transfers to +the same merchant. \section{Conclusion} @@ -871,6 +938,15 @@ protocol may finally enable modern society to upgrade to proper electronic wallets with efficient, secure and privacy-preserving transactions. +\subsection*{Acknowledgements} + +This work was supported by a grant from the Renewable Freedom Foundation. +% FIXME: ARED? +We thank Tanja Lange and Dan Bernstein for feedback on an earlier +version of this paper, Nicolas Fournier for implementing and running +some performance benchmarks, and Richard Stallman, Hellekin Wolf, +Jacob Appelbaum for productive discussions and support. + \bibliographystyle{alpha} \bibliography{taler} @@ -888,6 +964,7 @@ we expect that transactions with amounts below Taler's transaction costs to be economically meaningless. Nevertheless, we document various ways how this could be achieved. + \subsection{Incremental spending} For services that include pay-as-you-go billing, customers can over diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c index c5c4a1f26..5b9d1c2ec 100644 --- a/src/mintdb/test_mintdb.c +++ b/src/mintdb/test_mintdb.c @@ -186,21 +186,74 @@ free_refresh_commit_coins_array(struct TALER_MINTDB_RefreshCommitCoin GNUNET_free (commit_coins); } +#define MELT_NEW_COINS 5 + static int -cmp_commit_coin_arrays (struct TALER_MINTDB_RefreshCommitCoin *a, - struct TALER_MINTDB_RefreshCommitCoin *b, - unsigned int size) +test_refresh_commit_coins (struct TALER_MINTDB_Session *session, + struct TALER_MINTDB_RefreshSession *refresh_session, + const struct GNUNET_HashCode *session_hash) { - unsigned int cnt; + struct TALER_MINTDB_RefreshCommitCoin *commit_coins; + struct TALER_MINTDB_RefreshCommitCoin *ret_commit_coins; struct TALER_MINTDB_RefreshCommitCoin *a_ccoin; struct TALER_RefreshLinkEncrypted *a_rlink; struct TALER_MINTDB_RefreshCommitCoin *b_ccoin; struct TALER_RefreshLinkEncrypted *b_rlink; + size_t size; + unsigned int cnt; + uint16_t cnc_index; + int ret; - for (cnt = 0; cnt < size; cnt++) + #define COIN_ENC_MAX_SIZE 512 + ret = GNUNET_SYSERR; + ret_commit_coins = NULL; + commit_coins = GNUNET_new_array (MELT_NEW_COINS, + struct TALER_MINTDB_RefreshCommitCoin); + cnc_index = (uint16_t) GNUNET_CRYPTO_random_u32 + (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_MIN (MELT_NEW_COINS, UINT16_MAX)); + for (cnt=0; cnt < MELT_NEW_COINS; cnt++) { - a_ccoin = &a[cnt]; - b_ccoin = &b[cnt]; + struct TALER_MINTDB_RefreshCommitCoin *ccoin; + struct TALER_RefreshLinkEncrypted *rlink; + ccoin = &commit_coins[cnt]; + size = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + COIN_ENC_MAX_SIZE); + rlink = GNUNET_malloc (sizeof (struct TALER_RefreshLinkEncrypted) + size); + ccoin->refresh_link = rlink; + ccoin->coin_ev_size = GNUNET_CRYPTO_random_u64 + (GNUNET_CRYPTO_QUALITY_WEAK, COIN_ENC_MAX_SIZE); + ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + ccoin->coin_ev, + ccoin->coin_ev_size); + rlink->blinding_key_enc_size = size; + RND_BLK (&rlink->coin_priv_enc); + rlink->blinding_key_enc = (const char *) &rlink[1]; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + (void *)rlink->blinding_key_enc, + rlink->blinding_key_enc_size); + } + FAILIF (GNUNET_OK != + plugin->insert_refresh_commit_coins (plugin->cls, + session, + session_hash, + cnc_index, + MELT_NEW_COINS, + commit_coins)); + ret_commit_coins = GNUNET_new_array (MELT_NEW_COINS, + struct TALER_MINTDB_RefreshCommitCoin); + FAILIF (GNUNET_OK != + plugin->get_refresh_commit_coins (plugin->cls, + session, + session_hash, + cnc_index, + MELT_NEW_COINS, + ret_commit_coins)); + /* compare the refresh commit coin arrays */ + for (cnt = 0; cnt < MELT_NEW_COINS; cnt++) + { + a_ccoin = &commit_coins[cnt]; + b_ccoin = &ret_commit_coins[cnt]; FAILIF (a_ccoin->coin_ev_size != b_ccoin->coin_ev_size); FAILIF (0 != memcmp (a_ccoin->coin_ev, a_ccoin->coin_ev, @@ -215,9 +268,14 @@ cmp_commit_coin_arrays (struct TALER_MINTDB_RefreshCommitCoin *a, b_rlink->coin_priv_enc, sizeof (a_rlink->coin_priv_enc))); } - return GNUNET_OK; + ret = GNUNET_OK; + drop: - return GNUNET_SYSERR; + if (NULL != ret_commit_coins) + free_refresh_commit_coins_array (ret_commit_coins, MELT_NEW_COINS); + if (NULL != commit_coins) + free_refresh_commit_coins_array (commit_coins, MELT_NEW_COINS); + return ret; } /** @@ -231,8 +289,6 @@ static int test_melting (struct TALER_MINTDB_Session *session) { #define MELT_OLD_COINS 10 -#define MELT_NEW_COINS 5 - struct TALER_MINTDB_RefreshSession refresh_session; struct TALER_MINTDB_RefreshSession ret_refresh_session; struct GNUNET_HashCode session_hash; @@ -242,10 +298,6 @@ test_melting (struct TALER_MINTDB_Session *session) struct TALER_MINTDB_RefreshMelt *melts; struct TALER_DenominationPublicKey *new_denom_pubs; struct TALER_DenominationPublicKey *ret_denom_pubs; - struct TALER_MINTDB_RefreshCommitCoin *commit_coins; - struct TALER_MINTDB_RefreshCommitCoin *ret_commit_coins; - size_t size; - uint16_t cnc_index; unsigned int cnt; int ret; @@ -255,8 +307,6 @@ test_melting (struct TALER_MINTDB_Session *session) melts = NULL; new_dkp = NULL; new_denom_pubs = NULL; - commit_coins = NULL; - ret_commit_coins = NULL; /* create and test a refresh session */ refresh_session.num_oldcoins = MELT_OLD_COINS; refresh_session.num_newcoins = 1; @@ -358,61 +408,15 @@ test_melting (struct TALER_MINTDB_Session *session) (ret_denom_pubs[cnt].rsa_public_key, new_denom_pubs[cnt].rsa_public_key)); } -#define COIN_ENC_MAX_SIZE 512 - commit_coins = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_MINTDB_RefreshCommitCoin); - cnc_index = (uint16_t) GNUNET_CRYPTO_random_u32 - (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_MIN (MELT_NEW_COINS, UINT16_MAX)); - for (cnt=0; cnt < MELT_NEW_COINS; cnt++) - { - struct TALER_MINTDB_RefreshCommitCoin *ccoin; - struct TALER_RefreshLinkEncrypted *rlink; - ccoin = &commit_coins[cnt]; - size = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - COIN_ENC_MAX_SIZE); - rlink = GNUNET_malloc (sizeof (struct TALER_RefreshLinkEncrypted) + size); - ccoin->refresh_link = rlink; - ccoin->coin_ev_size = GNUNET_CRYPTO_random_u64 - (GNUNET_CRYPTO_QUALITY_WEAK, COIN_ENC_MAX_SIZE); - ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - ccoin->coin_ev, - ccoin->coin_ev_size); - rlink->blinding_key_enc_size = size; - RND_BLK (&rlink->coin_priv_enc); - rlink->blinding_key_enc = (const char *) &rlink[1]; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - (void *)rlink->blinding_key_enc, - rlink->blinding_key_enc_size); - } FAILIF (GNUNET_OK != - plugin->insert_refresh_commit_coins (plugin->cls, - session, - &session_hash, - cnc_index, - MELT_NEW_COINS, - commit_coins)); - ret_commit_coins = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_MINTDB_RefreshCommitCoin); - FAILIF (GNUNET_OK != - plugin->get_refresh_commit_coins (plugin->cls, - session, - &session_hash, - cnc_index, - MELT_NEW_COINS, - ret_commit_coins)); - FAILIF (GNUNET_OK != - cmp_commit_coin_arrays (commit_coins, - ret_commit_coins, - MELT_NEW_COINS)); + test_refresh_commit_coins (session, + &refresh_session, + &session_hash)); + ret = GNUNET_OK; drop: destroy_denom_key_pair (dkp); - if (NULL != ret_commit_coins) - free_refresh_commit_coins_array (ret_commit_coins, MELT_NEW_COINS); - if (NULL != commit_coins) - free_refresh_commit_coins_array (commit_coins, MELT_NEW_COINS); if (NULL != melts) { for (cnt = 0; cnt < MELT_OLD_COINS; cnt++)