diff options
| -rw-r--r-- | doc/paper/taler.tex | 121 | ||||
| -rw-r--r-- | src/mintdb/test_mintdb.c | 138 | 
2 files changed, 170 insertions, 89 deletions
| diff --git a/doc/paper/taler.tex b/doc/paper/taler.tex index a728471c..e3fb8359 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 c5c4a1f2..5b9d1c2e 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++) | 
