This commit is contained in:
Christian Grothoff 2015-09-09 20:34:07 +02:00
commit 7237665713
2 changed files with 170 additions and 89 deletions

View File

@ -191,18 +191,20 @@ his message to the merchant.
\begin{figure}[h] \begin{figure}[h]
\centering \centering
\begin{tikzpicture} \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]; \draw [<-, C] (customer) -- (mint) node [midway, above, sloped] (TextNode) {withdraw coins};
\node (origin) at (0,0) {}; \draw [<-, C] (mint) -- (merchant) node [midway, above, sloped] (TextNode) {deposit coins};
\node (mint) [def,above=of origin,draw]{Mint}; \draw [<-, C] (merchant) -- (customer) node [midway, above, sloped] (TextNode) {spend coins};
\node (customer) [def, draw, below left=of origin] {Customer}; \draw [<-, C] (mint) -- (auditor) node [midway, above, sloped] (TextNode) {verify};
\node (merchant) [def, draw, below right=of origin] {Merchant};
\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} \end{tikzpicture}
\caption{Taler's system model for the payment system is based on Chaum~\cite{chaum1983blind}.} \caption{Taler's system model for the payment system is based on Chaum~\cite{chaum1983blind}.}
\label{fig:cmm} \label{fig:cmm}
@ -777,13 +779,78 @@ and $G$ is the generator of the elliptic curve.
\subsection{Linking} \subsection{Linking}
% FIXME: explain better... For a coin that was successfully refreshed, the mint responds to a
For a coin that was successfully refreshed, the mint responds to request $S_{C'}(\mathtt{link})$ with $(T^{(\gamma)}_p$, $E_{\gamma},
a request $S_{C'}(\mathtt{link})$ with $(T^{(\gamma)}_p$, $E_{\gamma}, \widetilde{C})$. \widetilde{C})$.
This allows the owner of the old coin to also obtain the private key 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 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} \subsection{Refunds}
@ -849,15 +916,15 @@ transfer.
%suitable for money laundering, we are optimistic that states will find %suitable for money laundering, we are optimistic that states will find
%the design desirable. %the design desirable.
We did not yet perform performance measurements for the various We performed some initial performance measurements for the various
operations. However, we are pretty sure that the computational and operations. The main conclusion was that the computational and
bandwidth cost for transactions described in this paper is likely bandwidth cost for transactions described in this paper is smaller
small compared to other business costs for the mint. We expect costs than $10^{-3}$ cent/transaction, and thus dwarfed by the other
within the system to be dominated by the (replicated, transactional) business costs for the mint. However, this figure excludes the cost
database. However, these expenses are again likely small in relation of currency transfers using traditional banking, which a mint operator
to the business cost of currency transfers using traditional banking. would ultimately have to interact with. Here, mint operators should
Here, mint operators should be able to reduce their expenses by be able to reduce their expenses by aggregating multiple transfers to
aggregating multiple transfers to the same merchant. the same merchant.
\section{Conclusion} \section{Conclusion}
@ -871,6 +938,15 @@ protocol may finally enable modern society to upgrade to proper
electronic wallets with efficient, secure and privacy-preserving electronic wallets with efficient, secure and privacy-preserving
transactions. 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} \bibliographystyle{alpha}
\bibliography{taler} \bibliography{taler}
@ -888,6 +964,7 @@ we expect that transactions with amounts below Taler's transaction
costs to be economically meaningless. Nevertheless, we document costs to be economically meaningless. Nevertheless, we document
various ways how this could be achieved. various ways how this could be achieved.
\subsection{Incremental spending} \subsection{Incremental spending}
For services that include pay-as-you-go billing, customers can over For services that include pay-as-you-go billing, customers can over

View File

@ -186,21 +186,74 @@ free_refresh_commit_coins_array(struct TALER_MINTDB_RefreshCommitCoin
GNUNET_free (commit_coins); GNUNET_free (commit_coins);
} }
#define MELT_NEW_COINS 5
static int static int
cmp_commit_coin_arrays (struct TALER_MINTDB_RefreshCommitCoin *a, test_refresh_commit_coins (struct TALER_MINTDB_Session *session,
struct TALER_MINTDB_RefreshCommitCoin *b, struct TALER_MINTDB_RefreshSession *refresh_session,
unsigned int size) 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_MINTDB_RefreshCommitCoin *a_ccoin;
struct TALER_RefreshLinkEncrypted *a_rlink; struct TALER_RefreshLinkEncrypted *a_rlink;
struct TALER_MINTDB_RefreshCommitCoin *b_ccoin; struct TALER_MINTDB_RefreshCommitCoin *b_ccoin;
struct TALER_RefreshLinkEncrypted *b_rlink; 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]; struct TALER_MINTDB_RefreshCommitCoin *ccoin;
b_ccoin = &b[cnt]; 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 (a_ccoin->coin_ev_size != b_ccoin->coin_ev_size);
FAILIF (0 != memcmp (a_ccoin->coin_ev, FAILIF (0 != memcmp (a_ccoin->coin_ev,
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, b_rlink->coin_priv_enc,
sizeof (a_rlink->coin_priv_enc))); sizeof (a_rlink->coin_priv_enc)));
} }
return GNUNET_OK; ret = GNUNET_OK;
drop: 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) test_melting (struct TALER_MINTDB_Session *session)
{ {
#define MELT_OLD_COINS 10 #define MELT_OLD_COINS 10
#define MELT_NEW_COINS 5
struct TALER_MINTDB_RefreshSession refresh_session; struct TALER_MINTDB_RefreshSession refresh_session;
struct TALER_MINTDB_RefreshSession ret_refresh_session; struct TALER_MINTDB_RefreshSession ret_refresh_session;
struct GNUNET_HashCode session_hash; struct GNUNET_HashCode session_hash;
@ -242,10 +298,6 @@ test_melting (struct TALER_MINTDB_Session *session)
struct TALER_MINTDB_RefreshMelt *melts; struct TALER_MINTDB_RefreshMelt *melts;
struct TALER_DenominationPublicKey *new_denom_pubs; struct TALER_DenominationPublicKey *new_denom_pubs;
struct TALER_DenominationPublicKey *ret_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; unsigned int cnt;
int ret; int ret;
@ -255,8 +307,6 @@ test_melting (struct TALER_MINTDB_Session *session)
melts = NULL; melts = NULL;
new_dkp = NULL; new_dkp = NULL;
new_denom_pubs = NULL; new_denom_pubs = NULL;
commit_coins = NULL;
ret_commit_coins = NULL;
/* create and test a refresh session */ /* create and test a refresh session */
refresh_session.num_oldcoins = MELT_OLD_COINS; refresh_session.num_oldcoins = MELT_OLD_COINS;
refresh_session.num_newcoins = 1; refresh_session.num_newcoins = 1;
@ -358,61 +408,15 @@ test_melting (struct TALER_MINTDB_Session *session)
(ret_denom_pubs[cnt].rsa_public_key, (ret_denom_pubs[cnt].rsa_public_key,
new_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 != FAILIF (GNUNET_OK !=
plugin->insert_refresh_commit_coins (plugin->cls, test_refresh_commit_coins (session,
session, &refresh_session,
&session_hash, &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));
ret = GNUNET_OK; ret = GNUNET_OK;
drop: drop:
destroy_denom_key_pair (dkp); 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) if (NULL != melts)
{ {
for (cnt = 0; cnt < MELT_OLD_COINS; cnt++) for (cnt = 0; cnt < MELT_OLD_COINS; cnt++)