Compare commits

...

56 Commits

Author SHA1 Message Date
Christian Grothoff
ef938e0f7a
-correctly implement CS idempotency check on withdraw 2022-02-15 17:07:13 +01:00
Christian Grothoff
8ecbdeb55b
-actually, commit phase does take care of this 2022-02-14 23:15:29 +01:00
Christian Grothoff
bd77bcb52d
-towards fixing the protocol 2022-02-14 23:02:25 +01:00
Christian Grothoff
f4f40a31ef
-fixmes 2022-02-14 13:03:26 +01:00
Gian Demarmels
9e69499468
CS thesis 2022-02-14 00:03:06 +01:00
Christian Grothoff
9b8c350d4d
-remove redundancies in the refresh-melt computation and fix uninitialized bks return value 2022-02-13 12:44:09 +01:00
Christian Grothoff
f6ecf7458a
-refactor melt API, add FIXME for discovered bug 2022-02-12 14:38:27 +01:00
Christian Grothoff
7cedf3f0bf
-clean up refresh reveal API 2022-02-12 14:00:58 +01:00
Christian Grothoff
4d26042b5a
-modify link API to return 'ps' so that linked coins can be refreshed 2022-02-12 13:39:58 +01:00
Christian Grothoff
730d8c893c
-more doxygen fixes 2022-02-12 12:35:03 +01:00
Christian Grothoff
d81b3f13d1
-remove legacy uncrustify workaround 2022-02-12 12:21:27 +01:00
Christian Grothoff
a0d9d59b73
-refactor to address FIXME 2022-02-12 12:20:12 +01:00
Christian Grothoff
88033aa15e
-removed confused TODOs 2022-02-12 12:15:02 +01:00
Christian Grothoff
d581729443
-removed confused TODOs 2022-02-12 12:14:24 +01:00
Christian Grothoff
db8cdc8c4c
-remove addessed documentation FIXMEs 2022-02-12 12:10:33 +01:00
Christian Grothoff
ea4be7ba6f
-swap argument/rval for nicer code 2022-02-12 11:42:25 +01:00
Christian Grothoff
8a3e88fbf1
-add missing comment 2022-02-12 11:27:57 +01:00
Christian Grothoff
c2549e8b1e
-address FIXMEs 2022-02-12 11:24:32 +01:00
Christian Grothoff
bc14c215b3
-doxygen fixes 2022-02-12 11:12:33 +01:00
Christian Grothoff
819b67426c
-doxygen fixes 2022-02-12 10:33:23 +01:00
Christian Grothoff
2cdbf58006
-remove redundant comments 2022-02-12 10:05:45 +01:00
Christian Grothoff
c93150b8cd
-work on more FIXMEs 2022-02-12 01:00:31 +01:00
Christian Grothoff
94a5359494
-address misc. fixmes 2022-02-12 00:52:19 +01:00
Christian Grothoff
3b1e742dde
-simplify: nonce no longer hashed 2022-02-11 18:00:20 +01:00
Christian Grothoff
9f77398fe2
-fix use of uninit memory in test 2022-02-11 17:44:18 +01:00
Christian Grothoff
0eb5b08d50
-minor API clean up 2022-02-11 17:05:57 +01:00
Christian Grothoff
2772050b95
-add missing cfg file 2022-02-11 12:23:57 +01:00
Christian Grothoff
4472cbaf9d
-simplify structures 2022-02-11 11:55:59 +01:00
Christian Grothoff
3b9d67a6f8
-get twisted tests to pass 2022-02-11 11:46:42 +01:00
Christian Grothoff
0995bdd1d0
-get tests to pass 2022-02-11 09:36:01 +01:00
Christian Grothoff
532d4ad0dc
-fixes to tests, and half-baked fixes for CS-/link (still fails) 2022-02-10 23:39:00 +01:00
Christian Grothoff
d58d89dcab
-get recoup/refresh to pass 2022-02-10 20:15:17 +01:00
Christian Grothoff
ed5ef2b5f7
also pass ewvs during recoup-refresh 2022-02-09 22:05:10 +01:00
Christian Grothoff
025922950d
pass exchange values to /recoup 2022-02-09 22:02:29 +01:00
Christian Grothoff
e6598cfa1a
-get refresh to work 2022-02-09 21:25:57 +01:00
Christian Grothoff
758f13b557
-fix error handling 2022-02-09 19:28:34 +01:00
Christian Grothoff
008ba5cf89
implement CS refresh-reveal signing, simplify TEH keys logic 2022-02-09 19:24:29 +01:00
Christian Grothoff
8e4eaabc96
-fix refresh commitment check for CS 2022-02-09 19:17:50 +01:00
Christian Grothoff
4ee82c1ed3
-fix init of nonces in reveal request 2022-02-09 18:42:16 +01:00
Christian Grothoff
730f9e8865
-more refresh CS fixes 2022-02-09 17:30:02 +01:00
Christian Grothoff
1777db292e
-fix withdraw logic 2022-02-09 16:43:36 +01:00
Christian Grothoff
d05c561e4f
-fix fTBFS 2022-02-09 15:33:20 +01:00
Christian Grothoff
12290af845
-clean up crypto 2022-02-09 10:49:10 +01:00
Christian Grothoff
d559610da7
-poison 2022-02-09 10:38:02 +01:00
Christian Grothoff
77eaa685b9
-indent 2022-02-09 10:11:40 +01:00
Christian Grothoff
88b84d01cb
-simpilify 2022-02-09 10:09:01 +01:00
Christian Grothoff
b461fc6fc4
-simpilify 2022-02-09 10:03:10 +01:00
Christian Grothoff
e82d18325a
-dce 2022-02-09 10:02:10 +01:00
Christian Grothoff
bc15478c3b
-fix leak 2022-02-09 09:25:32 +01:00
Christian Grothoff
c3e1aa36ee
-fix leak 2022-02-09 09:19:56 +01:00
Christian Grothoff
dfc5039d9a
-fix leak 2022-02-09 09:18:35 +01:00
Christian Grothoff
bd930549fb
initialize reserved field 2022-02-09 09:13:40 +01:00
Christian Grothoff
cb723a82fd
-initialize cipher type 2022-02-08 23:25:16 +01:00
Christian Grothoff
6cbf7218d8
patch from Lucien 2022-02-08 22:58:12 +01:00
ms
4076712210
cbdc-it: rewording 2022-02-08 14:34:05 +01:00
Christian Grothoff
b3cf788424
-more test fixes 2022-02-08 14:02:27 +01:00
140 changed files with 9296 additions and 2248 deletions

@ -1 +1 @@
Subproject commit c12314df0f82e192c6829a9c6cf3e9663b586da1
Subproject commit f0deccc31022f5aa0eecfe4c9c173625f4a6d848

View File

@ -172,6 +172,30 @@
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
<member kind="define">
<type>#define</type>
<name>MHD_HTTP_METHOD_GET</name>
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
<member kind="define">
<type>#define</type>
<name>MHD_HTTP_METHOD_POST</name>
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
<member kind="define">
<type>#define</type>
<name>MHD_HTTP_METHOD_PUT</name>
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
<member kind="define">
<type>#define</type>
<name>MHD_HTTP_METHOD_DELETE</name>
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
<member kind="typedef">
<type>int</type>
<name>MHD_AccessHandlerCallback</name>

View File

@ -8,7 +8,7 @@
\usepackage{graphicx}
\usepackage{natbib}
\usepackage[italian]{babel}
\title{Come emettere una moneta digitale di banca centrale}
\title{Come una banca centrale dovrebbe emettere una moneta digitale}
\author{David Chaum\footnote{david@chaum.com} \\
xx Network \and
Christian Grothoff\footnote{christian.grothoff@bfh.ch} \\
@ -44,7 +44,7 @@ requisiti normativi in modo efficace e offrire un livello di protezione
resistente ai computer quantistici contro il rischio sistemico per
la privacy. Né la politica monetaria né la stabilità del sistema
finanziario sarebbero realmente interessate da questo sistema dal
momento che una CBDC emessa in questo modo replicherebbe il contante
momento che una moneta emessa in questo modo replicherebbe il contante
fisico anziché i depositi bancari. \\
JEL: E42, E51, E52, E58, G2

View File

@ -0,0 +1,48 @@
%!TEX root = ../dokumentation.tex
\chapter*{Abbreviations}
\begin{acronym}[YTMMM]
\acro{AES}{Advanced Encryption Standard}
\acro{AML}{Anti Money Laundering}
\acro{API}{Application Programming Interface}
\acrodefplural{API}[APIs]{Application Programming Interfaces}
\acro{BIP}{Bitcoin Improvement Proposal}
\acro{CA}{Certificate Authority}
\acro{CDH}{Computational Diffie-Hellman}
\acro{CFT}{Combating Financing of Terrorism}
\acro{CMA}{Choosen-Message Attack}
\acro{CS}{Clause Blind Schnorr Signature Scheme}
\acro{CSRF}{Client-Side Request Forgery}
\acro{CWE}{Common Weakness Enumeration}
\acro{DDH}{Decisional Diffie-Hellman}
\acro{DHKE}{Diffie-Hellman key exchange}
\acro{DLP}{Discrete Logarithm Problem}
\acro{DSA}{Digital Signature Algorithm}
\acro{ECC}{Elliptic Curve Cryptography}
\acro{ECDH}{Elliptic Curve Diffie Hellman}
\acro{EdDSA}{Edwards-curve Digital Signature Algorithm}
\acro{EUF}{Existentially Unforgeability}
\acro{FDH}{Full-Domain Hash}
\acro{GNU AGPL}{GNU Affero General Public License}
\acro{GNU GPL}{GNU General Public License}
\acro{GNU LGPL}{GNU Lesser General Public License}
\acro{IPC}{Inter Process Communication}
\acro{JSON}{JavaScript Object Notation}
\acro{KDF}{Key Derivation Function}
\acro{KYC}{Know Your Customer}
\acro{MAC}{Message Authentication Code}
\acro{NIST}{National Institute of Standards and Technology}
\acro{MK}{Master Key}
\acro{PKI}{Public Key Infrastructure}
\acro{PRF}{Pseudo Random Function}
\acro{PoS}{Point-of-Sales}
\acro{PRNG}{Pseudo Random Number Generator}
\acro{RNG}{Random Number Generator}
\acro{ROS}{Random inhomogeneities in an Overdetermined, Solvable system of linear equations}
\acro{RT}{Round-Trip}
\acro{RTT}{Round-Trip Time}
\acro{SPOF}{Single Point of Failure}
\acro{SSRF}{Server-Side Request Forgery}
\acro{Taler}{GNU Taler}
\acro{TRNG}{True Random Number Generator}
\acro{URL}{uniform resource locator}
\end{acronym}

26
doc/cs/ads/abstract.tex Normal file
View File

@ -0,0 +1,26 @@
\chapter*{Abstract}
GNU Taler is an intuitive, fast and socially responsible digital payment system implemented as free software.
While preserving the customers privacy, GNU Taler is still compliant to regulations.
\\\\
The goal of this thesis is to improve Taler's performance and provide cipher agility by adding support for Schnorr's blind signatures.
To achieve this goal, the current state in research for Schnorr signatures needs to be analyzed.
After choosing a signature scheme, it has to be integrated into the Taler protocols.
Besides implementing the redesigned protocols in Taler, an implementation of the cryptographic routines is needed.
\\\\
The paper "Blind Schnorr
Signatures and Signed ElGamal Encryption in the Algebraic Group Model" \cite{cryptoeprint:2019:877} from 2019 (updated in 2021) introducing \gls{CSBS} is used as theoretical basis for our improvements.
The paper explains why simple Blind Schnorr Signatures are broken and how the Clause Schnorr Blind Signature scheme is secured against this attack.\\
Compared to the currently used \gls{RSABS}, the new scheme has an additional request, two blinding factors instead of one and many calculations are done twice to prevent attacks.
\\\\
The Taler protocols were redesigned to support the Clause Blind Schnorr Signature scheme, including slight alterations to ensure \textit{abort-idempotency}, and then further specified.
Before starting with the implementation of the redesigned protocols, the cryptographic routines for \gls{CSBS} were implemented as part of the thesis. \\
All of the implemented code is tested and benchmarks are added for the cryptographic routines.
\\\\
Multiple results were achieved during this thesis:
The redesigned protocols Taler protocols with support for \gls{CSBS}, the implementation of the cryptographic routines, the implementation of Talers core protocols and a detailed comparison between \gls{RSABS} and \gls{CSBS}.
Overall, the \gls{CSBS} are significantly faster, require less disk space, and bandwidth and provide \textit{cipher agility} for Taler.
\section*{Acknowledgement}
We would like to kindly thank Christian Grothoff (Bern University of Applied Sciences) for his extensive advice, support and very helpful feedback during our whole thesis.\\
We also kindly thank Jeffrey Burdges (Web 3, Switzerland) for reviewing the proposal containing the redesigned protocols and giving feedback.\\
Further, we kindly thank Jacob Appelbaum (Bern University of Applied Sciences, Eindhoven University of Technology) for further results for the performance measurements of our cryptographic routines and the insightful conversations.

53
doc/cs/ads/glossary.tex Normal file
View File

@ -0,0 +1,53 @@
%!TEX root = ../thesis.tex
%
% vorher in Konsole folgendes aufrufen:
% makeglossaries makeglossaries dokumentation.acn && makeglossaries dokumentation.glo
%
%
% Glossareintraege --> referenz, name, beschreibung
% Aufruf mit \gls{...}
%
% \newglossaryentry{non-repudiation}{name={non-repudiation},plural={non-repudiation},description={After a message is signed, one can not dispute that a message was signed}}
% \newglossaryentry{sender_authenticity}{name={sender authenticity},plural={sender authenticity},description={The origin/sender of a message can not be forged}}
% \newglossaryentry{message_integrity}{name={message integrity},plural={message integrity},description={No unauthorized change to the message can be made, the message is tamperproof}}
\newglossaryentry{hkdf}{
name = {HKDF},
description = {The HMAC-based Extract-and-Expand Key Derivation Function is a function that takes potentially weak keying material as input and outputs high entropy keying material. For more information see section \ref{sec:kdf}}
}
\newglossaryentry{25519}{
name = {Curve25519},
description = {A popular elliptic curve used in many cryptographic systems based on elliptic curve cryptography. See section \ref{par:curve25519}}
}
\newglossaryentry{fdh}{
name = {FDH},
description = {A Full-Domain Hash is a hash function with an image size equal to the original gorup. See section \ref{sec:rsa-fdh}}.
}
\newglossaryentry{idempotence}{
name = {idempotence},
description = {Idempotence in the context of computer science is a property to ensure that the state of system will not change, no matter how many times the same request was made. See section \ref{abort-idempotency}}
}
\newglossaryentry{abort-idempotency}{
name = {abort-idempotency},
description = {Abort-idempotency is a special case of \gls{idempotence}. On every step in a protocol it needs to be ensured that even on an abort, the same request always receives the same response. See section \ref{abort-idempotency}}
}
\newglossaryentry{RSABS}{
name = {RSA Blind Signatures},
description = {Chaums Blind Signature Scheme based on RSA. See section \ref{sec:blind-rsa-sign}}
}
\newglossaryentry{CSBS}{
name = {Clause Blind Schnorr Signatures},
description = {A secure variant of Blind Schnorr Signature Schemes introduced in section \ref{sec:clause-blind-schnorr-sig}}
}
% \newglossaryentry{25519}{
% name = {},
% description = {}
% }

71
doc/cs/ads/header.tex Normal file
View File

@ -0,0 +1,71 @@
% Hyperlinks
\usepackage[
hidelinks,
pdfusetitle,
]{hyperref}
% Grafiken
\usepackage{graphicx}
%Bildpfad
\graphicspath{{images/}}
% Micro sign
\usepackage{siunitx}
% Farben
\usepackage{color}
\definecolor{LinkColor}{rgb}{0,0,0.2}
% Glossar
\usepackage[
nonumberlist, %keine Seitenzahlen anzeigen
%acronym, %ein Abkürzungsverzeichnis erstellen
%section, %im Inhaltsverzeichnis auf section-Ebene erscheinen
toc, %Einträge im Inhaltsverzeichnis
]{glossaries}
\makeglossaries
\input{ads/glossary}
%Nomenklatur
\usepackage{nomencl}
\makenomenclature
%PDF pages
\usepackage{pdfpages}
%Adjustbox (tikz figures of Taler)
\usepackage{adjustbox}
%BFH Boxes
% see BFH example for usage, looks nice!<<
\LoadBFHModule{listings,terminal,boxes}
%Akronyme
\usepackage[printonlyused,footnote]{acronym}
% Literaturverweise
\usepackage[
backend=biber,
style=alphabetic,
%citestyle=authoryear
]{biblatex}
\addbibresource{bibliography.bib}
\addbibresource{bibliography_projekt2.bib}
% TODOs in text
% documentation: http://tug.ctan.org/macros/latex/contrib/todonotes/todonotes.pdf
\usepackage{todonotes}
%Crypto Grafiken
\usepackage{cryptocode}
%\usepackage{amsmath}
\usepackage{listings}
\usepackage{xcolor}
\definecolor{mGreen}{rgb}{0,0.6,0}
\definecolor{mGray}{rgb}{0.5,0.5,0.5}
\definecolor{mPurple}{rgb}{0.58,0,0.82}
\definecolor{backgroundColour}{rgb}{0.95,0.95,0.92}
\definecolor{ApiColor}{HTML}{307FCB}
\definecolor{whyite}{HTML}{A1C66C} % Needs to be here due to some typo in BFH-CI stuff. Thanks BFH.

12
doc/cs/ads/history.tex Normal file
View File

@ -0,0 +1,12 @@
\chapter*{Document History}
\addcontentsline{toc}{chapter}{Document History}
%\begin{center}
\begin{tabular}{ ||l|l|l|l|| }
\hline
Version & Date & Comment & Author \\
\hline\hline
0.0.1 & 30.09.2021 & Document created & Gian Demarmels \& Lucien Heuzeveldt \\
\hline
\end{tabular}
%\end{center}

362
doc/cs/bibliography.bib Normal file
View File

@ -0,0 +1,362 @@
@misc{project-definition,
author = {Dr. Emmanuel Benoist},
title = {Adding Schnorr's blind signature in Taler},
howpublished = {\url{https://fbi.bfh.ch/fbi/2022/Studienbetrieb/BaThesisHS21/aufgabestellungen/BIE1-1-21-en.html}},
year = {2021}
}
@misc{swot-analysis,
author = {Will Kenton},
title = {Strength, Weakness, Opportunity, and Threat (SWOT) Analysis},
year = {2021},
howpublished = {\url{https://www.investopedia.com/terms/s/swot.asp}},
note = {[Online; accessed 01-October-2021]}
}
@misc{enwiki:1040250156,
author = {{Wikipedia contributors}},
title = {Project management triangle --- {Wikipedia}{,} The Free Encyclopedia},
year = {2021},
url = {https://en.wikipedia.org/w/index.php?title=Project_management_triangle&oldid=1040250156},
note = {[Online; accessed 1-October-2021]}
}
@misc{ionos:waterfall_model,
author = {ionos.com},
title = {Waterfall methodology},
year = {2019},
url = {https://www.ionos.com/digitalguide/websites/web-development/waterfall-methodology/},
note = {[Online; accessed 1-October-2021]}
}
@misc{schwab:anforderungen,
author = {Gerhard Schwab},
title = {Lerneinheit 4 - Anforderungen ermitteln},
howpublished = {BFH Moodle},
year = {2017}
}
@techreport{rfc2104,
shorthand = {RFC2104},
author = {H. Krawczyk, M.Bellare, R. Canetti},
title = {HMAC: Keyed-Hashing for Message Authentication},
howpublished = {Internet Requests for Comments},
type = {RFC},
number = 2104,
year = {1997},
issn = {2070-1721},
month = {02},
publisher = {IETF},
institution = {IETF},
url = {https://tools.ietf.org/html/rfc2104}
}
@techreport{rfc5869,
shorthand = {RFC5869},
author = {H. Krawczyk, P.Eronen},
title = {HMAC-based Extract-and-Expand Key Derivation Function (HKDF)},
howpublished = {Internet Requests for Comments},
type = {RFC},
number = 5869,
year = {2010},
issn = {2070-1721},
month = {05},
publisher = {IETF},
institution = {IETF},
url = {https://tools.ietf.org/html/rfc5869}
}
@misc{cryptoeprint:2019:877,
author = {Georg Fuchsbauer and
Antoine Plouviez and
Yannick Seurin},
title = {Blind Schnorr Signatures and Signed ElGamal Encryption in the Algebraic Group Model},
howpublished = {Cryptology ePrint Archive, Report 2019/877},
year = {2019},
note = {\url{https://ia.cr/2019/877} and \url{https://www.youtube.com/watch?v=W-uwVdGeUUs}}
}
@misc{bip:schnorr-bitc,
author = {Pieter Wuille, Jonas Nick, Tim Ruffing},
title = {Schnorr Signatures for secp256k1},
howpublished = {Bitcoin Improvement Proposal, bip-0340},
year = {2020},
note = {\url{https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki}}
}
@misc{git:secp256k1-schnorr,
author = {Bitcoin Repository},
title = {BIP-340 - Module for Schnorr signatures in libsecp256k1},
howpublished = {\url{https://github.com/bitcoin/bitcoin/tree/master/src/secp256k1}}
}
@misc{btc:releasnotes-0.21,
author = {Bitcoin.org },
title = {0.21.1 Release Notes},
howpublished = {\url{https://bitcoin.org/en/releases/0.21.1/}}
}
@inproceedings{spring:wallet-db-with-observers,
author = {Chaum, David
and Pedersen, Torben Pryds},
editor = {Brickell, Ernest F.},
title = {Wallet Databases with Observers},
booktitle = {Advances in Cryptology --- CRYPTO' 92},
year = {1993},
publisher = {Springer Berlin Heidelberg},
address = {Berlin, Heidelberg},
pages = {89--105},
abstract = {Previously there have been essentially only two models for computers that people can use to handle ordinary consumer transactions: (1) the tamper-proof module, such as a smart card, that the person cannot modify or probe; and (2) the personal workstation whose inner working is totally under control of the individual. The first part of this article argues that a particular combination of these two kinds of mechanism can overcome the limitations of each alone, providing both security and correctness for organizations as well as privacy and even anonymity for individuals.},
isbn = {978-3-540-48071-6}
}
@misc{schnorr:perfect-dl-signatures,
author = {Claus Peter Schnorr},
title = {Enhancing the Security of Perfect Blind DL-Signatures.},
howpublished = {Universität Frankfurt},
year = {2004},
note = {\url{https://www.math.uni-frankfurt.de/~dmst/teaching/SS2012/Vorlesung/EBS5.pdf}}
}
@misc{wagner:generalized-bday-prob,
author = {David Wagner},
title = {A Generalized Birthday Problem},
howpublished = {University of California Berkeley},
year = {2002},
note = {\url{https://www.iacr.org/archive/crypto2002/24420288/24420288.pdf}}
}
@inproceedings{Schnorr01securityof,
author = {Claus Peter Schnorr},
title = {Security of Blind Discrete Log Signatures against Interactive Attacks},
booktitle = {ICICS 2001, LNCS 2229},
year = {2001},
pages = {1--12},
publisher = {Springer-Verlag}
}
@misc{pic:simple-diagram,
author = {GNU Taler},
title = {Simple Taler Diagram},
year = {[Online; accessed 2-November-2021]},
note = {\url{https://taler.net/images/diagram-simple.png}}
}
@misc{pic:refresh-prot,
author = {GNU Taler},
title = {Taler Refresh protocol},
year = {[Online; accessed 2-November-2021]},
note = {\url{https://git.taler.net/marketing.git/plain/presentations/comprehensive/main.pdf}}
}
@misc{pic:taler-overview,
author = {GNU Taler},
title = {Operations},
howpublished = {\url{https://git.taler.net/marketing.git/plain/presentations/comprehensive/operations.png}},
year = {[Online; accessed 2-November-2021]},
}
@misc{pic:coin-state-machine,
author = {GNU Taler},
howpublished = {\url{https://git.taler.net/exchange.git/tree/doc/system/taler/coin.pdf}},
title = {Coin State Machine},
year = {[Online; accessed 13 January 2022]}
}
@misc{pic:deposit-state-machine,
author = {GNU Taler},
howpublished = {\url{https://git.taler.net/exchange.git/tree/doc/system/taler/deposit.pdf}},
title = {Deposit State Machine},
year = {[Online; accessed 13 January 2022]}
}
@misc{gnunet-git,
author = {GNUnet Git Repositories},
title = {gnunet.git},
howpublished = {\url{https://git.gnunet.org/gnunet.git/}}
}
@misc{libsodium:finite-field-arithmetic,
author = {libsodium documentation},
howpublished = {\url{https://doc.libsodium.org/advanced/point-arithmetic}},
title = {Finite field arithmetic}
}
@misc{bernlange:safecurves,
author = {Daniel J. Bernstein and Tanja Lange},
title = {SafeCurves: choosing safe curves for elliptic-curve cryptography.},
howpublished = {\url{https://safecurves.cr.yp.to}},
year = {accessed 17 October 2021. }
}
@misc{matt:unix-domain-sockets,
author = {Matt Lim},
title = {Getting Started With Unix Domain Sockets},
howpublished = {\url{https://medium.com/swlh/getting-started-with-unix-domain-sockets-4472c0db4eb1}},
year = {accessed 08 January 2022. }
}
@misc{rfc7748,
shorthand = {RFC7748},
series = {Request for Comments},
number = 7748,
howpublished = {RFC 7748},
publisher = {RFC Editor},
doi = {10.17487/RFC7748},
url = {https://rfc-editor.org/rfc/rfc7748.txt},
author = {Adam Langley and Mike Hamburg and Sean Turner},
title = {{Elliptic Curves for Security}},
pagetotal = 22,
year = 2016,
month = jan,
abstract = {This memo specifies two elliptic curves over prime fields that offer a high level of practical security in cryptographic applications, including Transport Layer Security (TLS). These curves are intended to operate at the \textasciitilde{}128-bit and \textasciitilde{}224-bit security level, respectively, and are generated deterministically based on a list of required properties.}
}
@misc{ganapati:rsactftool,
author = {Ganapati},
title = {RsaCtfTool},
howpublished = {\url{https://github.com/Ganapati/RsaCtfTool}},
year = {accessed 13 January 2022. }
}
@misc{perez:stoprsa,
author = {Ben Perez},
title = {Seriously, stop using RSA},
howpublished = {\url{https://blog.trailofbits.com/2019/07/08/fuck-rsa/}},
year = {accessed 13 January 2022. }
}
@misc{geeks:rtt,
author = {preetikagupta8171},
title = {What is RTT(Round Trip Time)?},
howpublished = {\url{https://www.geeksforgeeks.org/what-is-rttround-trip-time/}},
year = {accessed 13 January 2022. }
}
@misc{madden:curve25519-clamping,
author = {Neil Madden},
howpublished = {\url{https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/}},
title = {Whats the Curve25519 clamping all about?},
year = {2020}
}
@misc{bern:tweetnacl,
author = {Daniel J. Bernstein, Bernard van Gastel, Wesley Janssen},
title = {TweetNaCl: a crypto library in 100 tweets.},
howpublished = {\url{https://tweetnacl.cr.yp.to/papers.html}},
year = {17.09.2014}
}
@misc{taler-presentation,
author = {GNU Taler},
howpublished = {\url{https://git.taler.net/marketing.git/tree/presentations/comprehensive/main.pdf}},
title = {GNU Taler},
year = {2021}
}
@misc{cryptoeprint:2020:945,
author = {Fabrice Benhamouda and
Tancrède Lepoint and
Julian Loss and
Michele Orrù and
Mariana Raykova},
title = {On the (in)security of ROS},
howpublished = {Cryptology ePrint Archive, Report 2020/945},
year = {2020},
note = {\url{https://ia.cr/2020/945}}
}
@misc{rfc5246,
series = {Request for Comments},
number = 5246,
howpublished = {RFC 5246},
publisher = {RFC Editor},
doi = {10.17487/RFC5246},
url = {https://rfc-editor.org/rfc/rfc5246.txt},
author = {Eric Rescorla and Tim Dierks},
title = {{The Transport Layer Security (TLS) Protocol Version 1.2}},
pagetotal = 104,
year = 2008,
month = aug,
abstract = {This document specifies Version 1.2 of the Transport Layer Security (TLS) protocol. The TLS protocol provides communications security over the Internet. The protocol allows client/server applications to communicate in a way that is designed to prevent eavesdropping, tampering, or message forgery. {[}STANDARDS-TRACK{]}}
}
@misc{rfc6071,
series = {Request for Comments},
number = 6071,
howpublished = {RFC 6071},
publisher = {RFC Editor},
doi = {10.17487/RFC6071},
url = {https://rfc-editor.org/rfc/rfc6071.txt},
author = {Sheila Frankel and Suresh Krishnan},
title = {{IP Security (IPsec) and Internet Key Exchange (IKE) Document Roadmap}},
pagetotal = 63,
year = 2011,
month = feb,
abstract = {Over the past few years, the number of RFCs that define and use IPsec and Internet Key Exchange (IKE) has greatly proliferated. This is complicated by the fact that these RFCs originate from numerous IETF working groups: the original IPsec WG, its various spin-offs, and other WGs that use IPsec and/or IKE to protect their protocols' traffic. This document is a snapshot of IPsec- and IKE-related RFCs. It includes a brief description of each RFC, along with background information explaining the motivation and context of IPsec's outgrowths and extensions. It obsoletes RFC 2411, the previous "IP Security Document Roadmap." The obsoleted IPsec roadmap (RFC 2411) briefly described the interrelationship of the various classes of base IPsec documents. The major focus of RFC 2411 was to specify the recommended contents of documents specifying additional encryption and authentication algorithms. This document is not an Internet Standards Track specification; it is published for informational purposes.}
}
@misc{enwiki:1055393696,
author = {{Wikipedia contributors}},
title = {RSA Factoring Challenge --- {Wikipedia}{,} The Free Encyclopedia},
year = {2021},
howpublished = {\url{https://en.wikipedia.org/w/index.php?title=RSA_Factoring_Challenge&oldid=1055393696}},
note = {[Online; accessed 16-January-2022]}
}
@misc{cryptoeprint:2015:625,
author = {Mike Hamburg},
title = {Ed448-Goldilocks, a new elliptic curve},
howpublished = {Cryptology ePrint Archive, Report 2015/625},
year = {2015},
note = {\url{https://ia.cr/2015/625}},
}
@misc{bern:curve25519,
author = {Daniel J. Bernstein},
title = {Curve25519: new Diffie-Hellman speed records},
howpublished = {\url{https://cr.yp.to/ecdh/curve25519-20060209.pdf}},
year = {02.09.2006}
}
@misc{yuchen:idempotence,
author = {Yuchen Z.},
title = {A Deep Dive Into Idempotence},
year = {2021},
howpublished = {\url{https://betterprogramming.pub/a-deep-dive-into-idempotence-1a39393df7e6}},
note = {[Online; accessed 16-January-2022]}
}
@misc{tibouchi:attacks-schnorr-nonce,
author = {Mehdi Tibouchi},
title = {Attacks on Schnorr signatures with biased nonces},
howpublished = {\url{https://ecc2017.cs.ru.nl/slides/ecc2017-tibouchi.pdf}},
year = {13.11.2017}
}
@article{wang:bitcoin-ecdsa-vuln,
author = {Wang, Ziyu and Yu, Hui and Zhang, Zongyang and Piao, Jiaming and Liu, Jianwei},
year = {2019},
month = {09},
pages = {},
title = {ECDSA weak randomness in Bitcoin},
volume = {102},
journal = {Future Generation Computer Systems},
doi = {10.1016/j.future.2019.08.034}
}
@misc{buchanan:ps3-ecdsa-vuln,
author = {Prof Bill Buchanan OBE},
title = {Not Playing Randomly: The Sony PS3 and Bitcoin Crypto Hacks},
howpublished = {\url{https://medium.com/asecuritysite-when-bob-met-alice/not-playing-randomly-the-sony-ps3-and-bitcoin-crypto-hacks-c1fe92bea9bc}},
year = {12.11.2018}
}
@misc{gian:nonce-sense,
author = {Gian Demarmels},
title = {Nonce-Sense - Romhack CTF Crypto Challenge},
howpublished = {\url{https://blog.c4pr1c0rn.ch/writeups/romhack_21/nonce_sence.html}},
year = {2021},
note = {[Online; accessed 19-January-2022]}
}

View File

@ -0,0 +1,442 @@
% see here for standard templates: https://en.wikibooks.org/wiki/LaTeX/Bibliography_Management#Standard_templates
@misc{chaum-grothoff-moser:issue-cdbc,
author = {Chaum David, Grothoff Christian, Moser Thomas},
title = {How to issue a central bank digital currency},
howpublished = {\url{https://www.snb.ch/en/mmr/papers/id/working_paper_2021_03}},
year = {2021}
}
@phdthesis{dold:the-gnu-taler-system,
author = {Florian Dold},
title = {The GNU Taler System},
howpublished ={\url{https://taler.net/papers/thesis-dold-phd-2019.pdf}},
school = {Université de Rennes},
year = {2019}
}
@misc{schneier:value-privacy,
author = {Bruce Schneier},
title = {The Value of Privacy},
howpublished = {\url{https://www.schneier.com/blog/archives/2006/05/the_value_of_pr.html}},
year = {2006}
}
@misc{qualcomm:mobile-rng,
author = {Liang Kai},
title = {Guard your data with the Qualcomm Snapdragon mobile platform},
howpublished = {\url{https://www.qualcomm.com/media/documents/files/guard-your-data-with-the-qualcomm-snapdragon-mobile-platform.pdf}},
year = {2019}
}
@misc{chaum:blind-sign,
author = {Chaum David},
title = {Blind Signatures for Untraceable Payments},
howpublished = {\url{https://www.chaum.com/publications/Chaum-blind-signatures.PDF}},
year = {1983}
}
@misc{grothoff-dold:euro-bearer-online,
author = {Christian Grothoff, Florian Dold},
title = {Why a Digital Euro should be Online-first and Bearer-based},
howpublished = {\url{https://taler.net/papers/euro-bearer-online-2021.pdf}},
year = {2021}
}
@misc{website:bigcommerce-payment-fraud,
author = {BigCommerce},
title = {Payment fraud: What is it and how it can be avoided?},
howpublished = {\url{https://www.bigcommerce.com/ecommerce-answers/payment-fraud-what-it-and-how-it-can-be-avoided/}}
}
@misc{nist:recommendation-for-key-management,
author = {Elaine Barker},
title = {Recommendation for Key Management},
howpublished = {\url{https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf}},
year = {2020}
}
@misc{taler:snackautomat,
author = {Berner Fachhochschule},
title = {GNU Taler Snackautomat},
howpublished = {\url{https://www.bfh.ch/de/forschung/referenzprojekte/gnu-taler-snackautomat/}}
}
@book{modernCrypto,
author = {Nigel P. Smart},
editor = {David Basin, Kenny Paterson},
title = {Cryptography Made Simple},
publisher = {Springer International Publishing Switzerland AG},
year = {2016}
}
@inbook{Crépeau2005,
author = {Cr{\'e}peau, Claude},
title = {Cut-and-choose protocols},
publisher = {School of Computr Science, McGill University, Montréal (QC), Canada},
url = {http://crypto.cs.mcgill.ca/~crepeau/EoC/Cut&Choose.pdf}
}
% author from github: https://github.com/chaos-eng/chaos-eng.github.io
@misc{chaos-engineering,
author = {chaos-eng},
title = {PRINCIPLES OF CHAOS ENGINEERING},
howpublished = {\url{https://principlesofchaos.org/}},
year = {2019}
}
@misc{businger:public-key-crytpo,
author = {Walter Businger},
title = {Skript Public-Key Kryptographie},
year = {2021}
}
@misc{rutishauser:fuzzing,
author = {Dobin Rutishauser},
title = {Fuzzing},
howpublished = {Course material of BFH module Forensics and Code Security},
year = {2021}
}
@misc{codeblau:taler-audit,
author = {Code Blau GmbH},
title = {Report for the GNU Taler security audit in Q2/Q3 2020},
howpublished = {\url{https://taler.net/papers/codeblau-report-2020-q2.pdf}},
year = {2020}
}
@misc{pentest-execution-standard,
author = {The Penetration Testing Execution Standard},
title = {Main Page},
howpublished = {\url{http://www.pentest-standard.org/index.php/Main_Page}}
}
@misc{owasp:top-ten,
author = {OWASP Foundation},
title = {OWASP Top Ten},
howpublished = {\url{https://owasp.org/www-project-top-ten/}}
}
@misc{owasp:mobile-top-ten,
author = {OWASP Foundation},
title = {OWASP Mobile Top 10},
howpublished = {\url{https://owasp.org/www-project-mobile-top-10/}}
}
@misc{owasp:api-security-project,
author = {OWASP Foundation},
title = {OWASP API Security Project},
howpublished = {\url{https://owasp.org/www-project-api-security/}}
}
@misc{owasp:web-security-testing-guide,
author = {OWASP Foundation},
title = {OWASP Web Security Testing Guide},
howpublished = {\url{https://owasp.org/www-project-web-security-testing-guide/}}
}
@misc{owasp:mobile-security-testing-guide,
author = {OWASP Foundation},
title = {OWASP Mobile Security Testing Guide},
howpublished = {\url{https://owasp.org/www-project-mobile-security-testing-guide/}}
}
@misc{owasp:application-security-verification-standard,
author = {OWASP Foundation},
title = {OWASP Application Security Verification Standard},
howpublished = {\url{https://owasp.org/www-project-application-security-verification-standard/}}
}
@misc{owasp:mobile-application-security-verification-standard,
author = {OWASP Foundation},
title = {OWASP Mobile Application Security Verification Standard},
howpublished = {\url{https://github.com/OWASP/owasp-masvs}}
}
@misc{osstmm,
author = {ISECOM},
title = {OSSTMM 3},
howpublished = {\url{https://www.isecom.org/OSSTMM.3.pdf}}
}
@misc{emscripten,
author = {Emscripten Contributors},
title = {Emscripten documentation},
howpublished = {\url{https://emscripten.org/}}
}
@misc{emscripten:paper,
author = {Alon Zakai},
title = {Emscripten: an LLVM-to-JavaScript compiler},
howpublished = {\url{https://www.researchgate.net/publication/221320724_Emscripten_an_LLVM-to-JavaScript_compiler}},
year = {2011}
}
@misc{cwe,
author = {Common Weakness Enumeration},
title = {CWE - Common Weakness Enumeration},
howpublished = {\url{https://cwe.mitre.org/index.html}}
}
@misc{cwe:toctou,
author = {Common Weakness Enumeration},
title = {CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition},
howpublished = {\url{https://cwe.mitre.org/data/definitions/367.html}},
year = {2021}
}
@misc{cwe:c-weaknesses,
author = {Common Weakness Enumeration},
title = {CWE VIEW: Weaknesses in Software Written in C},
howpublished = {\url{https://cwe.mitre.org/data/definitions/658.html}}
}
% ---------- Taler documentation and repos
@misc{taler-documentation,
author = {Taler Systems SA},
title = {GNU Taler Documentation},
howpublished = {\url{https://docs.taler.net/}}
}
@misc{taler-documentation:backup-synchronization,
author = {Taler Systems SA},
title = {Backup and Synchronization Service API},
howpublished = {\url{https://docs.taler.net/core/api-sync.html}}
}
@misc{taler-documentation:auditor-operator-manual,
author = {Taler Systems SA},
title = {GNU Taler Auditor Operator Manual},
howpublished = {\url{https://docs.taler.net/taler-auditor-manual.html}}
}
@misc{taler-documentation:exchange-operator-manual,
author = {Taler Systems SA},
title = {GNU Taler Exchange Operator Manual},
howpublished = {\url{https://docs.taler.net/taler-exchange-manual.html}}
}
@misc{taler-documentation:merchant-backend-operator-manual,
author = {Taler Systems SA},
title = {GNU Taler Merchant Backend Operator Manual},
howpublished = {\url{https://docs.taler.net/taler-merchant-manual.html}}
}
@misc{taler-documentation:merchant-api,
author = {Taler Systems SA},
title = {GNU Taler Merchant API Tutorial},
howpublished = {\url{https://docs.taler.net/taler-merchant-api-tutorial.html}}
}
@misc{taler-documentation:back-office,
author = {Taler Systems SA},
title = {Back-office Web service manual},
howpublished = {\url{https://docs.taler.net/taler-backoffice-manual.html}}
}
@misc{taler-documentation:pos-manual,
author = {Taler Systems SA},
title = {GNU Taler Merchant POS Manual},
howpublished = {\url{https://docs.taler.net/taler-merchant-pos-terminal.html}}
}
@misc{taler-documentation:wallet-developer-manual,
author = {Taler Systems SA},
title = {GNU Taler Wallet Developer Manual},
howpublished = {\url{https://docs.taler.net/taler-wallet.html}}
}
@misc{taler-documentation:wallet-cli-manual,
author = {Taler Systems SA},
title = {GNU Taler Wallet CLI Manual},
howpublished = {\url{https://docs.taler.net/taler-wallet-cli-manual.html}}
}
@misc{taler-documentation:,
author = {Taler Systems SA},
title = {},
howpublished = {\url{}}
}
@misc{taler-documentation:,
author = {Taler Systems SA},
title = {},
howpublished = {\url{}}
}
@misc{taler-documentation:,
author = {Taler Systems SA},
title = {},
howpublished = {\url{}}
}
% see https://git.taler.net/
@misc{taler-git,
author = {GNU Taler Git Repositories},
title = {GNU Taler Git Repositories},
howpublished = {\url{https://git.taler.net/}}
}
@misc{taler-git:exchange,
author = {GNU Taler Git Repositories},
title = {exchange.git},
howpublished = {\url{https://git.taler.net/exchange.git/}}
}
@misc{taler-git:merchant,
author = {GNU Taler Git Repositories},
title = {merchant.git},
howpublished = {\url{https://git.taler.net/merchant.git/}}
}
@misc{taler-git:wallet-core,
author = {GNU Taler Git Repositories},
title = {wallet-core.git},
howpublished = {\url{https://git.taler.net/wallet-core.git/}}
}
@misc{taler-git:auditor,
author = {GNU Taler Git Repositories},
title = {auditor.git},
howpublished = {\url{https://git.taler.net/auditor.git/}}
}
@misc{taler-git:backoffice,
author = {GNU Taler Git Repositories},
title = {backoffice.git},
howpublished = {\url{https://git.taler.net/backoffice.git/}}
}
@misc{taler-git:android,
author = {GNU Taler Git Repositories},
title = {taler-android.git},
howpublished = {\url{https://git.taler.net/taler-android.git}}
}
@misc{taler-git:ios,
author = {GNU Taler Git Repositories},
title = {taler-ios.git},
howpublished = {\url{https://git.taler.net/taler-ios.git/}}
}
@misc{taler-git:django-payments,
author = {GNU Taler Git Repositories},
title = {django-payments-taler.git},
howpublished = {\url{https://git.taler.net/django-payments-taler.git/}}
}
@misc{taler-git:woocommerce,
author = {GNU Taler Git Repositories},
title = {woocommerce-taler.git},
howpublished = {\url{https://git.taler.net/woocommerce-taler.git/}}
}
@misc{taler-git:saleor,
author = {GNU Taler Git Repositories},
title = {saleor-frontend.git},
howpublished = {\url{https://git.taler.net/saleor-frontend.git/}}
}
@misc{taler-git:merchant-demos,
author = {GNU Taler Git Repositories},
title = {taler-merchant-demos.git},
howpublished = {\url{https://git.taler.net/taler-merchant-demos.git/}}
}
% ---------- Wikipedia
@misc{dewiki:205456999,
author = {Wikipedia},
title = {Know your customer --- Wikipedia{,} Die freie Enzyklopädie},
year = {2020},
url = {\url{https://de.wikipedia.org/w/index.php?title=Know_your_customer&oldid=205456999}},
note = {[Online; Stand 3. April 2021]}
}
@misc{enwiki:1013094030,
author = {{Wikipedia contributors}},
title = {EdDSA --- {Wikipedia}{,} The Free Encyclopedia},
year = {2021},
howpublished = {\url{https://en.wikipedia.org/w/index.php?title=EdDSA&oldid=1013094030}},
note = {[Online; accessed 22-April-2021]}
}
@misc{enwiki:1020240018,
author = {{Wikipedia contributors}},
title = {Birthday problem --- {Wikipedia}{,} The Free Encyclopedia},
year = {2021},
howpublished = {\url{https://en.wikipedia.org/w/index.php?title=Birthday_problem&oldid=1020240018}},
note = {[Online; accessed 28-April-2021]}
}
@misc{enwiki:1019272750,
author = {{Wikipedia contributors}},
title = {Birthday attack --- {Wikipedia}{,} The Free Encyclopedia},
year = {2021},
howpublished = {\url{https://en.wikipedia.org/w/index.php?title=Birthday_attack&oldid=1019272750}},
note = {[Online; accessed 24-April-2021]}
}
@misc{enwiki:blind-sign,
author = {{Wikipedia contributors}},
title = {Blind signature --- {Wikipedia}{,} The Free Encyclopedia},
year = {2021},
howpublished = {\url{https://en.wikipedia.org/w/index.php?title=Blind_signature&oldid=1001105629}},
note = {[Online; accessed 12-April-2021]}
}
@misc{enwiki:1024158358,
author = "{Wikipedia contributors}",
title = "Scalability --- {Wikipedia}{,} The Free Encyclopedia",
year = "2021",
howpublished = "\url{https://en.wikipedia.org/w/index.php?title=Scalability&oldid=1024158358}",
note = "[Online; accessed 17-June-2021]"
}
@misc{enwiki:1024197377,
author = "{Wikipedia contributors}",
title = "Chaos engineering --- {Wikipedia}{,} The Free Encyclopedia",
year = "2021",
howpublished = "\url{https://en.wikipedia.org/w/index.php?title=Chaos_engineering&oldid=1024197377}",
note = "[Online; accessed 17-June-2021]"
}
@misc{enwiki:1026754635,
author = "{Wikipedia contributors}",
title = "Replay attack --- {Wikipedia}{,} The Free Encyclopedia",
year = "2021",
howpublished = "\url{https://en.wikipedia.org/w/index.php?title=Replay_attack&oldid=1026754635}",
note = "[Online; accessed 17-June-2021]"
}
% ---------- RFCs
@misc{rfc8032,
series = {Request for Comments},
number = 8032,
howpublished = {RFC 8032},
publisher = {RFC Editor},
doi = {10.17487/RFC8032},
url = {https://rfc-editor.org/rfc/rfc8032.txt},
author = {Simon Josefsson and Ilari Liusvaara},
title = {{Edwards-Curve Digital Signature Algorithm (EdDSA)}},
pagetotal = 60,
year = 2017,
month = jan,
abstract = {This document describes elliptic curve signature scheme Edwards-curve Digital Signature Algorithm (EdDSA). The algorithm is instantiated with recommended parameters for the edwards25519 and edwards448 curves. An example implementation and test vectors are provided.},
}
@misc{rfc6265,
series = {Request for Comments},
number = 6265,
howpublished = {RFC 6265},
publisher = {RFC Editor},
doi = {10.17487/RFC6265},
url = {https://rfc-editor.org/rfc/rfc6265.txt},
author = {Adam Barth},
title = {{HTTP State Management Mechanism}},
pagetotal = 37,
year = 2011,
month = apr,
abstract = {This document defines the HTTP Cookie and Set-Cookie header fields. These header fields can be used by HTTP servers to store state (called cookies) at HTTP user agents, letting the servers maintain a stateful session over the mostly stateless HTTP protocol. Although cookies have many historical infelicities that degrade their security and privacy, the Cookie and Set-Cookie header fields are widely used on the Internet. This document obsoletes RFC 2965. {[}STANDARDS-TRACK{]}},
}

View File

@ -0,0 +1,72 @@
\chapter{Introduction}
\section{Motivation}
Public key cryptography based on elliptic curves allows smaller key sizes compared to other cryptographic systems.
While still providing equivalent security, the smaller key size leads to huge performance benefits.
\\
Blind Signatures are one of the key components upon which Taler's privacy is built upon.
Our thesis adds support for a modern cryptographic scheme called the Clause Blind Schnorr Signature scheme \cite{cryptoeprint:2019:877}.\\
Additionally to the benefits of ellicptic curve cryptography, adding a second blind signature scheme makes Taler independent of a single cryptographic scheme and thus provides \textit{cipher agility}.
\section{Goals}
\label{sec:goals}
The project definition is as follows \cite{project-definition}:
The students will implement the blind Schnorr signature inside Taler.
Taler is a system for the management of virtual money.
Taler is based on coins that need to be signed by an exchange (for instance a bank).
In the actual version of the system, coins are signed by the exchange using Schaum's bind-signature protocol.
This allows users to have signed coins, without the exchange knowing what it signed.
This step is fundamental for the privacy protection of the system.
\\The students have to insert the Schnorr blind signature algorithm inside the protocol for the creation of coins.
But they also need to change the Taler subsystems where the verification of the signature is done.
\\The actual Taler system allows people to let an exchange sign a coin for which they do not have the private key.
This is a security issue (for misuse of coins on the dark-net for instance).
An optional task for the project is to prevent a user to let an exchange sign a public key when the client does not have access to the corresponding private key.
\\Here is a list of the tasks that the students must do:
\begin{itemize}
\item Design a protocol integrating Schnorr blind signature in the creation of Taler coins.
\item Implement the protocol inside the exchange application and the wallet app.
\item Analyze the different Taler subsystems to find where the blind signature is verified.
\item Replace verification of the blind signature everywhere it occurs.
\item Compare both blind signature systems (Schaum's and Schnorr's), from the point of view of security, privacy protection, speed, \dots
\item Write tests for the written software.
\item Conduct tests for the written software.
\item Transfer the new software the Taler developers team
\end{itemize}
Here is a list of optional features:
\begin{itemize}
\item Design a protocol, such that the exchange can verify that the user knows the private key corresponding to the coin that is to be signed.
\item Implement that protocol.
\end{itemize}
\section{Scope}
\label{sec:scope}
In scope are all necessary changes on the protocol(s) and components for the following tasks:
\begin{itemize}
\item Research the current state of Blind Schnorr Signature schemes
\item Redesign the Taler protocols to support Blind Schnorr signatures
\item Add support for a Blind Schnorr Signature Scheme in the exchange, merchant, wallet-core, wallet web-extension and optionally on the android mobile wallet
\item design and implement a protocol where the user proves to the exchange the knowledge of the coin that is to be signed (optional)
\end{itemize}
Out of scope is production readyness of the implementation.
This is because changes in the protocos and code need to be thoroughly vetted to ensure that no weaknesses or security vulnerabilities were introduced.
Such an audit is out of scope for the thesis and is recommended to be performed in the future.
The iOS wallet will not be considered in this work.
\\
It is not unusual that a scope changes when a project develops.
Due to different reasons, the scope needed to be shifted.
Since there are no libraries supporting \gls{CSBS}, the signature scheme has to be implemented and tested before integrating it into Taler.
While this is still reasonable to do in this project, it will affect the scope quite a bit.
The analysis of the optional goal showed, that a good solution that aligns with Taler's goals and properties needs more research and is a whole project by itself.
Scope changes during the project:
\begin{itemize}
\item \textbf{Added:} Implement the cryptographic routines in GNUnet
\item \textbf{Removed: } design and implement a protocol where the user proves to the exchange the knowledge of the coin that is to be signed (optional)
\item \textbf{Adjusted: } Focus is on the implementation of the exchange protocols (Withdraw, Spend, Refresh and cryptographic utilities)
\item \textbf{Adjusted: } Implementation of the refresh protocol and wallet-core are nice-to-have goals
\item \textbf{Removed: } The Merchant and the android wallet implementations are out of scope
\end{itemize}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,458 @@
\chapter{Protocol Design}
\label{chap:design}
This chapter describes the necessary changes on the protocol level to implement a Blind Schnorr Signature Scheme to Taler.
\section{Analysis of Current Protocols}
The blind RSA signature scheme is only used for coin signatures.
Note that we omitted protocols (or parts of them) where the coin signature is transmitted, but no other actions using it is performed.
\\\\
\label{abort-idempotency}
An important property to mention here is \textit{\gls{abort-idempotency}}.
\Gls{idempotence} in the context of computer science is a property to ensure that the state of a system will not change, no matter how many times the same request was made.
A more in-depth explanation is given within the cited source \cite{yuchen:idempotence}.\\
\textit{\gls{abort-idempotency}} goes a bit further.
When the protocol is aborted at any stage, for example due to power cuts or network issues, the protocol still needs to ensure that the same response is sent for the same request.
This is especially challenging when dealing with random values as we will see in the redesigned protocols in the following sections.
For \gls{RSABS} it is inherently easier to provide \textit{\gls{abort-idempotency}} since signature creation only needs one round-trip and requires less random values.
The following protocols currently use \gls{RSABS}:
\begin{itemize}
\item \textbf{Withdraw Protocol:}
The customer uses the blind signature scheme to blind the coins before transmitting them to the exchange, which blindly signs it (standard RSA signature) and the returns the signatures.
After the customer receives the signatures, he unblinds and stores them together with the coins.
\\ Components:
\begin{itemize}
\item Customer
\item Exchange
\end{itemize}
\item \textbf{Deposit Protocol:}
During the Deposit, the exchange verifies the coin signature derived using the blind RSA signature scheme.
\\ Components:
\begin{itemize}
\item Exchange
\end{itemize}
\item \textbf{Refresh Protocol:}
The refresh protocol is used to derive a new coin from an old one which was partially spent.
Parts of the protocol are similar to the withdraw protocol, but it is more complex due to the added DH lock and cut-and-choose.
\\ Components:
\begin{itemize}
\item Customer
\item Exchange
\end{itemize}
\item \textbf{Tipping:}
Tipping is a variation of the withdraw protocol where the message containing the blinded planchets is transmitted to the merchant, who signs them using his reserve private, key and returns the signatures back to the customer.
Here, the details from the withdraw protocol apply.
\\ Components:
\begin{itemize}
\item Customer
\item Exchange
\end{itemize}
\item \textbf{Payback Protocol:}
The payback protocol distinguishes three different cases, which either use the refresh protocol or disclose either the withdraw transcript or refresh protocol transcript to the exchange.
\\ Components:
\begin{itemize}
\item Customer
\item Exchange
\end{itemize}
\end{itemize}
\section{Protocol Changes}
The goal of the thesis is to add support for the Clause Blind Schnorr Signature scheme to Taler, besides the existing \gls{RSABS} implementation (see section \ref{sec:blind-rsa-sign}).
For the design of the \gls{CSBS} the existing protocols with \gls{RSABS} were redesigned.
The goal of the blind signature is to keep the exchange from knowing which coin a user withdraws and thus preventing the exchange linking a coin to a user.
The biggest impact is on the withdrawal and refresh protocols, but all protocols that include some operation around denomination signatures are affected.
During the thesis the protocols will be redesigned, implemented and the differences to the current version will be outlined.
These results will be delivered to the Taler team.
Feedback is very important when (re)designing protocols.
For that reason the redesigned protocols were discussed and reviewed with Christian Grothoff multiple times.
As signature scheme the Clause Blind Schnorr Signature Scheme described in section \ref{sec:clause-blind-schnorr-sig} was chosen for multiple reasons.
First of all it is currently considered to be secure (see \cite{cryptoeprint:2019:877}).
Schnorr Signatures on \gls{25519} are much shorter than RSA signatures.
This should provide notable performance improvements in speed and storage, and therefore scales better.
The paper describes a security analysis of the Blind Schnorr Signature scheme and introduces a modification (the "clause" part in the name) that is resistant to Wagner's algorithm (which solves ROS problem).
\Gls{25519} \cite{bern:curve25519} will be used for the implementation because it is a widely accepted curve (see \cite{bernlange:safecurves}, \cite{rfc7748}) and is already used by Taler (Taler uses Ed25519 which is built upon \gls{25519}).
\subsection{Withdraw Protocol}
\label{sec:withdraw-protocol-schnorr}
The modified protocol using the Clause Blind Schnorr Signature Scheme is described in figures \ref{fig:withdrawal-process-schnorr-1} and \ref{fig:withdrawal-process-schnorr-2}.
The proposed change introduces an additional round trip.
It must be prevented that the exchange has to track sessions or persist values during the first stage \ref{fig:withdrawal-process-schnorr-1}, while still ensuring \gls{abort-idempotency}.
In order to ensure \textit{\gls{abort-idempotency}}, the exchange has to generate the same $R_0,R_1$ for the same withdrawal request, while $r_0,r_1$ still needs to be unpredictable for the customer.
For this reason a withdrawal-nonce combined with a \gls{hkdf} comes into play.
The redesigned protocol makes extensive use of \gls{hkdf}'s functionality as \ac{PRNG} and one-way function, thus random becomes \textit{unpredictable}.
In the beginning of the protocol, the customer generates a coin key pair.
Its private key is used to generate the withdraw-nonce $n_w$ and the blinding factors $\alpha_0, \alpha_1, \beta_0, \beta_1$.
The exchange uses the withdraw nonce together with the reserve key and a long-term secret to generate $r_0, r_1$.
The coin and denomination private keys can be used as long-term secrets due to the one-way property of the \gls{hkdf}.
Another question evolved around which key to use for the derivation of $ r_0, r_1 $.
Obvious options are the denomination key or the exchange's online signing key.
The denomination key was chosen because it has the payback protocol in place that would handle coin recovery in case of a key compromise and subsequent revocation.
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{reserve keys } w_s, W_p & & \text{reserve public key } W_p
\\ \text{denomination public key } D_p & & \text{denomination keys } d_s, D_p
\\ & &
\\\text{generate withdraw secret:}
\\ \omega := randombytes(32)
\\ \text{persist } \langle \omega, D_p \rangle
\\ n_w := \text{HKDF}(256, \omega,\text{"n"})
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{n_w, D_p} &
% generate R
\\ & & \text{verify if } D_p \text{ is valid}
\\ & & r_0 := \text{HKDF}(256,n_w || d_s, \text{"r0"})
\\ & & r_1 := \text{HKDF}(256,n_w || d_s, \text{"r1"})
\\ & & R_0 := r_0G
\\ & & R_1 := r_1G
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{R_0, R_1} &
\\ \text{derive coin key pair}:
\\ c_s := \text{HKDF}(256, \omega || R_0 || R_1,\text{"cs"})
\\ C_p := \text{Ed25519.GetPub}(c_s)
% blinding
\\ \text{blind:} & &
\\ b_s := \text{HKDF}(256, \omega || R_0 || R_1,\text{"b-seed"})
\\ \alpha_0 := \text{HKDF}(256, b_s, \text{"a0"})
\\ \alpha_1 := \text{HKDF}(256, b_s, \text{"a1"})
\\ \beta_0 := \text{HKDF}(256, b_s, \text{"b0"})
\\ \beta_1 := \text{HKDF}(256, b_s, \text{"b1"})
\\ R'_0 := R_0 + \alpha_0 G + \beta_0 D_p & &
\\ R'_1 := R_1 + \alpha_1 G + \beta_1 D_p & &
\\ c'_0 := H(R'_0, C_p) & &
\\ c'_1 := H(R'_1, C_p) & &
\\ c_0 := c'_0 + \beta_0 \mod p & &
\\ c_1 := c'_1 + \beta_1 \mod p & &
\\
\\ & \textit{Continued in figure \ref{fig:withdrawal-process-schnorr-2}} &
\end{array}$
}
\end{equation*}
\caption{Withdrawal process using Clause Blind Schnorr Signatures part 1}
\label{fig:withdrawal-process-schnorr-1}
\end{figure}
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{reserve keys } w_s, W_p & & \text{reserve public key } W_p
\\ \text{denomination public key } D_p & & \text{denomination keys } d_s, D_p
\\
\\ & \textit{Continuation of figure \ref{fig:withdrawal-process-schnorr-1}} &
\\
% sign with reserve sk
\\ \text{sign with reserve private key:} & &
\\ \rho_W := \langle n_w, D_p, c_0, c_1 \rangle & &
\\ \sigma_W := \text{Ed25519.Sign}(w_s, \rho_W) & &
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{W_p, \sigma_W, \rho_W} &
\\ & & \langle n_w, D_p, c_0, c_1 \rangle := \rho_W
% checks done by the exchange
\\ & & \text{verify if } D_p \text{ is valid}
\\ & & \text{check } \text{Ed25519.Verify}(W_p, \rho_W, \sigma_W)
\\ & & b := \text{HKDF}(1,n_w || d_s, \text{"b"})
\\ & & s \leftarrow \text{GetWithdraw}(n_w, D_p)
\\ & & \textbf{if } s = \bot
\\ & & r_b := \text{HKDF}(256,n_w || d_s, \text{"r}b\text{"})
% sign coin
\\ & & s := r_b + c_b d_s \mod p
% the following db operations are atomic
\\ & & \text{decrease balance if sufficient and}
\\ & & \text{persist } \langle n_w, D_p, s \rangle
\\ & & \textbf{endif}
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{b,s} &
% verify signature
\\ \text{verify signature:}& &
\\ \textbf{check if } sG = R_b + c_b D_p & &
% unblind signature
\\ \text{unblind:}& &
\\ s' := s + \alpha_b \mod p & &
\\ \text{verify signature:}& &
\\ \textbf{check if } s'G = R'_b + c'_b D_p & &
\\ \sigma_C := \langle R'_b, s' \rangle & &
\\ \text{resulting coin: } c_s, C_p, \sigma_C, D_p & &
\end{array}$
}
\end{equation*}
\caption{Withdrawal process using Clause Blind Schnorr Signatures part 2}
\label{fig:withdrawal-process-schnorr-2}
\end{figure}
\subsection{Deposit Protocol}
The deposit protocol remains unchanged, except for the verification of the coin signature.
To verify the signature, the exchange has to check if the following equation holds:
\begin{align*}
s'G & = R' + c' D_p
\\ &= R' + H(R', C_p) D_p
\end{align*}
$ s', R' $ together form the signature, $ D_p $ is the denomination public key and $ C_p $ is the coin public key.
Further details regarding the verification process can be found in section \ref{sec:blind-schnorr-sig}.
\subsection{Refresh Protocol}
The refresh protocol blindly signs the new derived coins.
The replacement of \gls{RSABS} with the Clause Blind Schnorr Signature Scheme (see \ref{sec:clause-blind-schnorr-sig}) makes the refresh protocol a bit more complex.
\subsubsection{RefreshDerive Schnorr}
The RefreshDerive protocol is described in figure \ref{fig:refresh-derive-schnorr}.
For this protocol, the main change is that more values need to be derived somehow.
These blinding factors are also derived from $x$.
Then the challenges $\overline{c_0}$ and $\overline{c_1}$ are generated as in the Clause Blind Schnorr Signature Scheme.
\begin{figure}[htp]
\centering
\fbox{%
\procedure[codesize=\small]{$\text{RefreshDerive}(t, D_{p(t)}, C_p, R_0, R_1)$}{%
T := \text{Curve25519.GetPub}(t) \\
x := \textrm{ECDH-EC}(t, C_p) \\
c'_s := \text{HKDF}(256, x, \text{"c"}) \\
C_p' := \text{Ed25519.GetPub}(c'_s) \\
b_s := \text{HKDF}(256, x || R_0 || R_1,\text{"b-seed"}) \\
\alpha_0 := \text{HKDF}(256, b_s, \text{"a0"}) \\
\alpha_1 := \text{HKDF}(256, b_s, \text{"a1"}) \\
\beta_0 := \text{HKDF}(256, b_s, \text{"b0"}) \\
\beta_1 := \text{HKDF}(256, b_s, \text{"b1"}) \\
R'_0 = R_0 + \alpha_0 G + \beta_0 D_p \\
R'_1 = R_1 + \alpha_1 G + \beta_1 D_p \\
c'_0 = H(R'_0, C_p') \\
c'_1 = H(R'_1, C_p') \\
\overline{c_0} = c'_0 + \beta_0 \mod p \\
\overline{c_1} = c'_1 + \beta_1 \mod p \\
\pcreturn \langle T, c'_s, C_p', \overline{c_0}, \overline{c_1} \rangle
}
}
\caption[RefreshDerive algorithm]{The RefreshDerive replaced with Schnorr blind signature details. As before the uses the seed $s$ on the dirty coin for generating the new coin.
The new coin needs to be signed later on with the denomination key.}
\label{fig:refresh-derive-schnorr}
\end{figure}
\subsubsection{Refresh Protocol}
\label{sec:refresh-protocol}
In the commit phase (see figure \ref{fig:refresh-commit-part1}) there needs to be requested an $R_0$ and $R_1$ before deriving the new coins.
There now needs to be calculated two different commit hashes, one for $\overline{c_0}$ and one for $\overline{c_1}$.
The exchange needs to additionally generate a random $b \leftarrow \{0,1\}$ to choose a $\overline{c_b}$.
The reveal phase (see figure \ref{fig:refresh-commit-part2}) now is continued only with the chosen $\overline{c_b}$.
In the reveal phase, the RSA signing and unblinding is exchanged with Schnorr's blind signature counterparts.
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{denomination public key } D_p & & \text{old denomination keys } d_{s(0)} D_{p(0)}
\\ \text{coin}_0 = \langle D_{p(0)}, c_s^{(0)}, C_p^{(0)}, \sigma_c^{(0)} \rangle && \text{new denomination keys } d_s, D_P
% request r
\\ & &
\\ \omega := randombytes(32)
\\ \text{persist } \langle \omega, D_p \rangle
%\\ s_w := \text{HKDF}(256, c_s^{(0)},\text{"n"})
\\ n_r := \text{HKDF}(256, \omega,\text{"n"})
% sign with reserve sk
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{n_r, D_p} &
% generate R
\\ & & \text{verify if } D_p \text{ is valid}
\\ & & r_0 := \text{HKDF}(256,n_r || d_s, \text{"r0"})
\\ & & r_1 := \text{HKDF}(256,n_r || d_s, \text{"r1"})
\\ & & R_0 := r_0G
\\ & & R_1 := r_1G
\\ & \xleftarrow[\rule{2cm}{0pt}]{R_0, R_1} &
% refresh request
\\ \textbf{for } i = 1, \dots, \kappa: % generate k derives
%\\ s_i \leftarrow \{0,1\}^{256} % seed generation
\\ t_i := \text{HKDF}(256, \omega || R_0 || R_1,\text{"t} i \text{"} ) % seed generation
\\ X_i := \text{RefreshDerive}(t_i, D_p, C_p^{(0)}, R_0, R_1)
\\ (T_i, c_s^{(i)}, C_p^{(i)}, \overline{c_0}, \overline{c_1}):= X_i
\\ \textbf{endfor}
\\ h_T := H(T_1, \dots, T_k)
\\ h_{\overline{c_0}} := H(\overline{c_{0_1}},\dots, \overline{c}_{0_k})
\\ h_{\overline{c_1}} := H(\overline{c_{1_1}},\dots, \overline{c}_{1_k})
\\ h_{\overline{c}} := H(h_{\overline{c_0}}, h_{\overline{c_1}})
\\ h_C := H(h_T, h_{\overline{c}})
\\ \rho_{RC} := \langle h_C, D_p, \text{ } D_{p(0)}, C_p^{(0)}, \sigma_C^{(0)} \rangle
\\ \sigma_{RC} := \text{Ed25519.Sign}(c_s^{(0)}, \rho_{RC})
\\ \text{Persist refresh-request}
\\ \langle \omega, R_0, R_1, \rho_{RC}, \sigma_{RC} \rangle
\\
\\ & \textit{Continued in figure \ref{fig:refresh-commit-part2}} &
\end{array}$
}
\end{equation*}
\caption{Refresh protocol (commit phase part 1) using Clause Blind Schnorr Signatures}
\label{fig:refresh-commit-part1}
\end{figure}
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
\text{Customer} & & \text{Exchange}
\\ & \textit{Continuation of}
\\ & \textit{figure \ref{fig:refresh-commit-part1}}
\\
\\ & \xrightarrow[\rule{2cm}{0pt}]{\rho_{RC}, \sigma_{RC}} &
% Exchange checks refresh request
\\ & & \langle h_C, D_p, D_{p(0)}, C_p^{(0)}, \sigma_C^{(0)} \rangle := \rho_{RC}
\\ & & \textbf{check} \text{ Ed25519.Verify}(C_p^{(0)}, \sigma_{RC}, \rho_{RC})
\\
\\ & & \gamma \leftarrow \text{GetOldRefresh}(\rho_{RC})
\\ & & \textbf{Comment: }\text{GetOldRefresh}(\rho_{RC} \mapsto
\\ & & \{\bot, \gamma \})
\\ & & \pcif \gamma = \bot
\\ & & v := \text{Denomination}(D_p)
\\ & & \textbf{check } \text{IsOverspending}(C_p^{(0)}, D_ {p(0)}, v)
\\ & & \text{verify if } D_p \text{ is valid}
\\ & & \textbf{check } \text{Schnorr.Verify}(D_{p(0)}, C_p^{(0)}, \sigma_C^{(0)})
\\ & & \text{MarkFractionalSpend}(C_p^{(0)}, v)
\\ & & \gamma \leftarrow \{1, \dots, \kappa\}
\\ & & \text{Persist refresh-record } \langle \rho_{RC},\gamma \rangle
\\ & \xleftarrow[\rule{2cm}{0pt}]{\gamma} &
% Check challenge and send challenge response (reveal not selected msgs)
\\ \textbf{check } \text{IsConsistentChallenge}(\rho_{RC}, \gamma)
\\ \textbf{Comment: } \text{IsConsistentChallenge}\\(\rho_{RC}, \gamma) \mapsto \{ \bot,\top \}
\\
\\ \text{Persist refresh-challenge} \langle \rho_{RC}, \gamma \rangle
\\ S := \langle t_1, \dots, t_{\gamma-1}, t_{\gamma+1}, \dots,t_\kappa \rangle % all seeds without the gamma seed
\\ \rho_L := \langle C_p^{(0)}, D_p, T_{\gamma}, \overline{c_0}_\gamma, \overline{c_1}_\gamma, n_r \rangle
\\ \rho_{RR} := \langle \rho_L, S \rangle
\\ \sigma_{L} := \text{Ed25519.Sign}(c_s^{(0)}, \rho_{L})
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{\rho_{RR},\rho_L, \sigma_{L}} &
\\
\\ & \textit{Continued in} &
\\ & \textit{figure \ref{fig:refresh-reveal-part1}} &
\end{array}$
}
\end{equation*}
\caption{Refresh protocol (commit phase part 2) using Clause Blind Schnorr Signatures}
\label{fig:refresh-commit-part2}
\end{figure}
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ & \textit{Continuation of}
\\ & \textit{figure \ref{fig:refresh-commit-part2}}
\\
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{\rho_{RR},\rho_L, \sigma_{L}} &
% check revealed msgs and sign coin
\\ & & \langle C_p^{(0)}, D_p, T_{\gamma}, \overline{c_0}_\gamma, \overline{c_1}_\gamma, n_r \rangle := \rho_L
\\ & & \langle T'_\gamma, \overline{c_0}_\gamma, \overline{c_1}_\gamma, S \rangle := \rho_{RR}
\\ & & \langle t_1,\dots,t_{\gamma-1},t_{\gamma+1},\dots,t_\kappa \rangle := S
\\ & & \textbf{check } \text{Ed25519.Verify}(C_p^{(0)}, \sigma_L, \rho_L)
\\ & & b := \text{HKDF}(1,n_r || d_{s(i)}, \text{"b"})
\\ & & \textbf{for } i = 1,\dots, \gamma-1, \gamma+1,\dots, \kappa
\\ & & X_i := \text{RefreshDerive}(t_i, D_p, C_p^{(0)} \\ &&, R_0, R_1)
\\ & & \langle T_i, c_s^{(i)}, C_p^{(i)}, \overline{c_1}_i, \overline{c_2}_i \rangle := X_i
\\ & & \textbf{endfor}
\\ & & h_T' = H(T_1,\dots,T_{\gamma-1},T'_{\gamma},T_{\gamma+1},\dots,T_\kappa)
\\ & & h_{\overline{c_0}}' := H(\overline{c_{0_1}},\dots, \overline{c}_{0_k})
\\ & & h_{\overline{c_1}}' := H(\overline{c_{1_1}},\dots, \overline{c}_{1_k})
\\ & & h_{\overline{c}}' := H(h_{\overline{c_0}}, h_{\overline{c_1}})
\\ & & h_C' = H(h_T', h_{\overline{c}}')
\\ & & \textbf{check } h_C = h_C'
\\ & & r_b := \text{HKDF}(256,n_r || d_s, \text{"r}b\text{"})
\\ & & \overline{s}_{C_p}^{(\gamma)} = r_b + \overline{c_{b_\gamma}} d_s \mod p
\\ & & \text{persist } \langle \rho_L, \sigma_L \rangle
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{b, \overline{s}_C^{(\gamma)}} &
% Check coin signature and persist coin
% unblind signature
\\ \text{unblind:}& &
\\ s_C'^{(\gamma)} := \overline{s}_C^{(\gamma)} + \alpha_b \mod p & &
\\ \text{verify signature:}& &
\\ \textbf{check if } \overline{s'}_C^{(\gamma)}G \equiv R'_b + \overline{c'_0}_\gamma D_p & &
\\ \sigma_C^{(\gamma)} := \langle s_{C}'^{(\gamma)},R_b' \rangle
\\ \text{Persist coin} \langle D_p, c_s^{(\gamma)}, C_p^{(\gamma)}, \sigma_C^{(\gamma)} \rangle
\end{array}$
}
\end{equation*}
\caption{Refresh protocol (reveal phase) using Clause Blind Schnorr Signatures}
\label{fig:refresh-reveal-part1}
\end{figure}
\newpage
\subsubsection{Linking Protocol}
\label{sec:refresh-link}
The beginning of the linking protocol (see figure \ref{fig:refresh-link}) is the same as in the current protocol.
After the customer received the answer $L$ the only difference is in obtaining the coin.
To re-obtain the derived coin, the same calculations as in \ref{fig:refresh-derive-schnorr} are made.
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{coin}_0 = \langle D_{p(0)}, c_s^{(0)}, C_p^{(0)}, \sigma_{C}^{(0)} \rangle
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{C_{p(0)}} &
\\ & & L := \text{LookupLink}(C_{p(0)})
\\ & & \textbf{Comment: } \text{LookupLink}(C_p^{(0)}) \mapsto
\\ & & \{\langle \rho_L^{(i)}, \sigma_L^{(i)}, \overline{\sigma}_C^{(i)}, b \rangle\}
%\\ & & \{\langle C_{p(0)}, D_{p(t)},\overline{\sigma}_C^{(i)}, b^{(i)}, R_b^{(i)}\rangle\}
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{L} &
\\ \textbf{for } \langle \rho_L^{(i)}, \overline{\sigma}_L^{(i)}, \overline{\sigma}_C^{(i)}, b \rangle\ \in L
%\\ & & \langle C_p^{(0)}, D_{p(t)}, T_{\gamma}, \overline{c_0}_\gamma, \overline{c_1}_\gamma, n_r \rangle := \rho_L
\\ \langle \hat{C}_p^{(i)}, D_p^{(i)}, T_\gamma^{(i)}, \overline{c_0}_\gamma^{(i)}, \overline{c_1}_\gamma^{(i)}, n_r \rangle := \rho_L^{(i)}
\\ \langle \overline{s}_C^{(i)}, R_b^{(i)} \rangle := \overline{\sigma}_C^{(i)}
\\ \textbf{check } \hat{C}_p^{(i)} \equiv C_p^{(0)}
\\ \textbf{check } \text{Ed25519.Verify}(C_p^{(0)}, \rho_{L}^{(i)}, \sigma_L^{(i)})
\\ \langle \overline{s}_C^{(i)}, R_b^{(i)} \rangle := \sigma_C^{(i)}
\\ x_i := \text{ECDH}(c_s^{(0)}, T_{\gamma}^{(i)})
\\ c_s^{(i)} := \text{HKDF}(256, x, \text{"c"})
\\ C_p^{(i)} := \text{Ed25519.GetPub}(c_s^{(i)})
\\ b_s^{(i)} := \text{HKDF}(256, x_i || R_0^{(i)} || R_1^{(i)},\text{"b-seed"})
\\ \alpha_b := \text{HKDF}(256, b_s^{(i)}, \text{"a}b\text{"})
\\ \beta_b := \text{HKDF}(256, b_s^{(i)}, \text{"b}b\text{"})
\\ {R'}_b^{(i)} = R_b^{(i)} + \alpha_b G + \beta_b D_p^{(i)}
\\ c'_b = H(R'_b, C_p^{(i)})
\\ c_b = c'_b + \beta_b \mod p
\\ s_C'^{(i)} := \overline{s}_C^{(i)} + \alpha_b \mod p
\\ \sigma_C^{(i)} := \langle s_C'^{(i)}, R_b' \rangle
\\ \textbf{check } s'{_C^{(i)}}G \equiv {R'}_b^{(i)} + c'_b D_p^{(i)}
\\ \text{(Re-)obtain coin} \langle D_p^{(i)},c_s^{(i)}, C_p^{(i)}, \sigma_C^{(i)} \rangle
\end{array}$
}
\end{equation*}
\caption{Linking protocol using Clause Blind Schnorr Signatures}
\label{fig:refresh-link}
\end{figure}
\subsection{Tipping}
Tipping remains unchanged, except for the content of the message $ \rho_W = D_p, c_0, c_1 $ signed by the merchant using its reserve private key.
\subsection{Payback Protocol}
The payback protocol distinguishes three different cases, which all depend on the state of a coin whose denomination key has been revoked.
The following listing outlines the necessary changes on the protocol, please refer to Dold's documentation section 2.2.1 \cite{dold:the-gnu-taler-system} for details regarding the different cases.
\begin{itemize}
\item \textbf{The revoked coin has never been seen by the exchange}:
\\The withdraw transcript (and verification) must be adjusted in order for the exchange to be able to retrace the blinding.
\item \textbf{The coin has been partially spent}:
\\In this case the refresh protocol will be invoked on the coin.
The necessary changes are outlined in \ref{sec:refresh-protocol}.
\item \textbf{The revoked coin has never been seen by the exchange and resulted from a refresh operation}:
\\The refresh protocol transcript and its blinding factors must be adjusted to consider the changes in the blind signature scheme.
\end{itemize}

View File

@ -0,0 +1,790 @@
\chapter{Protocol Specification}
\label{chap:spec}
The proposed Taler protocols using the Clause Blind Schnorr Signature Scheme will be implemented as an additional option besides the existing \gls{RSABS} variant of the protocol as suggested by Christian Grothoff.
A Taler Exchange operator should be able to configure whether he wants to use \gls{RSABS} or \gls{CSBS}.
\\This variant allows to choose the signature scheme globally or per denomination.
Furthermore, it allows a change of signature scheme in a non-breaking way by revoking (or letting expire) a denomination and offering new denominations with the other scheme.
\\
The following key points are specified in this chapter:
\begin{itemize}
\item Architecture of the different components
\item Explain and specify needed changes
\item Data strucutures
\item Public \acp{API}
\item Persistence
\item Used libraries
\end{itemize}
\section{Architecture}
Before specifying the implementation of the different protocols, a deeper understanding of the technical architecture of Talers components is needed.
this section introduces the architecture of the exchange and wallet components and explains where the needed changes need to be implemented on a high-level.
\subsection{Exchange}
An introduction to the exchange can be found in section \ref{sec:exchange}.
An exchange operator needs to run and maintain some additional services besides Taler's exchange.
Although this is not directly relevant for the implementation, it helps to better understand the environment in which the exchange runs.
The perspective of an exchange operator can be seen in figure \ref{fig:taler:exchange-operator-architecture}.
\begin{figure}[h!]
\begin{adjustbox}{max totalsize={.9\textwidth}{.7\textheight},center}
\begin{tikzpicture}
\tikzstyle{def} = [node distance= 5em and 6.5em, inner sep=1em, outer sep=.3em];
\node (origin) at (0,0) {};
\node (exchange) [def,above=of origin,draw]{Exchange};
\node (nexus) [def, draw, below right=of exchange] {Nexus};
\node (corebanking) [def, draw, below left=of nexus] {Core Banking};
\node (nginx) [def, draw, above=of exchange]{Nginx};
\node (postgres) [def, draw, below left=of exchange]{Postgres};
\node (postgres-nexus) [def, draw, below right=of nexus]{Postgres};
\tikzstyle{C} = [color=black, line width=1pt]
\draw [<-, C] (exchange) -- (nginx) node [midway, above, sloped] (TextNode) {REST API};
\draw [<-, C] (postgres) -- (exchange) node [midway, above, sloped] (TextNode) {SQL};
\draw [<-, C] (postgres-nexus) -- (nexus) node [midway, above, sloped] (TextNode) {SQL};
\draw [<-, C] (nexus) -- (exchange) node [midway, above, sloped] (TextNode) {Internal REST API};
\draw [<-, C] (corebanking) -- (nexus) node [midway, above, sloped] (TextNode) {EBICS/FinTS};
\end{tikzpicture}
\end{adjustbox}
\caption{Taler exchange operator architecture (source: \cite{taler-presentation})}
\label{fig:taler:exchange-operator-architecture}
\end{figure}
The software architecture of the exchange can be seen in figure \ref{fig:taler:exchange-architecture}.
The API runs under the httpd service, where the API endpoints need to be adjusted/added to incorporate the changes of this thesis.
The httpd server has no access to the private keys of the denomination and online signing keys.
Only the corresponding security module can perform operations requiring the private key.
Further the keys are also managed by these security modules.
To support \gls{CSBS} a new security module, which performs signature operations, is added.
To persist the new data structures, the postgres helpers need to be adjusted to serialize/deserialize the new \gls{CSBS} data structures.
More details on what changes are needed in these places is discussed in the following sections.
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}
\tikzstyle{def} = [node distance=2em and 2.5em, inner sep=1em, outer sep=.3em];
\node (origin) at (0,0) {};
\node [blue] (httpd) [def,above=of origin,draw]{httpd};
\node (secmod-rsa) [def, draw, right=of httpd] {secmod-rsa};
\node (secmod-eddsa) [def, draw, left=of httpd] {secmod-eddsa};
\node [blue](postgres) [def, draw, below=of httpd]{Postgres};
\node [mGreen] (secmod-cs) [def, draw, left=of postgres]{secmod-cs};
\node (aggregator) [def, draw, right=of postgres]{aggregator};
\node (transfer) [def, draw, below left=of postgres]{transfer};
\node (wirewatch) [def, draw, below right=of postgres]{wirewatch};
\node (nexus) [def, draw, below=of postgres]{Nexus};
\tikzstyle{C} = [color=black, line width=1pt]
\draw [<->, C] (httpd) -- (postgres) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (httpd) -- (secmod-rsa) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (httpd) -- (secmod-eddsa) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (httpd) -- (secmod-cs) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (aggregator) -- (postgres) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (wirewatch) -- (postgres) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (transfer) -- (postgres) node [midway, above, sloped] (TextNode) {};
\draw [->, C] (transfer) -- (nexus) node [midway, above, sloped] (TextNode) {};
\draw [<-, C] (wirewatch) -- (nexus) node [midway, above, sloped] (TextNode) {};
\end{tikzpicture}
\end{center}
\caption{Taler exchange architecture (source: \cite{taler-presentation})}
\label{fig:taler:exchange-architecture}
\end{figure}
\subsection{Wallet}
The architecture of the wallet implementation (as seen in figure \ref{fig:taler:wallet-architecture}) is quite straightforward.
To add support for \gls{CSBS} in the wallet, the cryptographic routines need to be reimplemented in Typescript.
Taler uses tweetnacl \cite{bern:tweetnacl} which provides functionality for the group operations.
There are existing \gls{hkdf} and \gls{fdh} implementations, that can be reused.\\
Furthermore, the Taler protocols need to be adjusted to support \gls{CSBS} in the wallet-core.
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}
\tikzstyle{def} = [node distance= 5em and 4.5em, inner sep=1em, outer sep=.3em];
\node (origin) at (0,0) {};
\node (gui) [def,above=of origin,draw]{wallet-gui};
\node [blue](core) [def,below=of gui,draw]{wallet-core};
\node (sync) [def, draw, below left=of core] {Sync};
\node (taler) [def, draw, below right=of core] {Taler};
\node (anastasis) [def, draw, below=of core] {Anastasis};
\tikzstyle{C} = [color=black, line width=1pt]
\draw [<->, C] (gui) -- (core) node [midway, above, sloped] (TextNode) {};
\draw [<->, C] (core) -- (sync) node [midway, above, sloped] (TextNode) {Backup};
\draw [<->, C] (core) -- (taler) node [midway, above, sloped] (TextNode) {Payment};
\draw [<->, C] (core) -- (anastasis) node [midway, above, sloped] (TextNode) {Key Escrow};
\end{tikzpicture}
\end{center}
\caption{Taler wallet architecture (source: \cite{taler-presentation})}
\label{fig:taler:wallet-architecture}
\end{figure}
\section{Persistence}
The Clause Blind Schnorr Signature scheme is quite different to \gls{RSABS}.
Despite the differences, the database model does not need to be changed.
The only change needed an additional type field, specifying whether RSA or CS is used as signature algorithm.
To persist the new structs introduced with the support for \gls{CSBS}, only the postgres helpers need to support serialization and deserialization of the new structs.
\section{Testing}
We will partially use test-driven development, meaning that we will write tests (at least for the known good case) before implementing functions, and extend them during and after development.
This allows us to check the functionality (code section, function(s)) during development, while being able to extend testing whenever we identify new test cases during development.
Test cases can be used to verify different aspects of a functionality.
These are the ones we will focus on.
\begin{itemize}
\item \textbf{Known good}:
Known good cases test whether a functionality works as expected.
They are the most useful during development, because they indicate whether the code is working as expected.
\item \textbf{Known Bad}:
Known bad cases test whether functionality that is known not to work behaves as expected.
\item \textbf{Determinism}:
This case type checks whether the same input leads to the same output.
It is important for code that must work deterministic (same output), non-deterministic (e.g. random output) or based on a state that impacts the functionality.
\item \textbf{Performance testing}:
Performance testing is used to gather timing information that can be used to identify functionality with long duration, or to compare performance between different implementations or major changes.
We will restrict performance testing to the comparison of the Blind RSA Signature Scheme and the Clause Blind Schnorr Signature Scheme.
\end{itemize}
\section{Signature Scheme Operations in GNUnet}
\label{sec:specification-signature-scheme}
The signature scheme operations implemented are needed in all other parts of the implementation.
Taler's cryptographic primitives (e.g. \gls{RSABS}, \gls{hkdf}, hash functions) are mostly implemented in GNUnet utils, therefore the Clause Blind Schnorr Signature routines will be implemented in GNUnet too.
It is important to provide a clear API for the cryptographic routines and to test them thoroughly.
Libsodium will be used for finite field arithmetic (\cite{libsodium:finite-field-arithmetic}) and for other functionality when available (e.g. for key generation).
Thus, a widely used and well tested cryptographic library is used for group operations.
For \acl{FDH} and \gls{hkdf} existing implementations provided by GNUnet are used.
The \gls{hkdf} is used with SHA-512 for the extraction phase and SHA-256 for the expansion phase.
\subsection{Data Structures}
Libsodium represents Ed25519 points and scalars as 32-byte char arrays.
To provide a more user-friendly \ac{API}, structs were created to represent each type.
For example \texttt{struct GNUNET\_CRYPTO\_CsPrivateKey} or \texttt{struct GNUNET\_CRYPTO\_RSecret}
The main reason is to increase readability and to prevent misusage of the \ac{API}.
Unlike RSA, our \gls{CSBS} on Ed25519 data structures have a fixed sizes.
The different data structures can be found in table \ref{tab:datastruct-crypto}.
\begin{table}[ht]
\centering
\resizebox{0.95\textwidth}{!}{\begin{minipage}{\textwidth}
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Values} & \textbf{Data Structure} & \textbf{Data Type} \\\hline
Curve25519 Scalar & {\small GNUNET\_CRYPTO\_Cs25519Scalar} & 32 byte char array\\\hline
Curve25519 Point & {\small GNUNET\_CRYPTO\_Cs25519Point} & 32 byte char array\\\hline
Private Key & {\small GNUNET\_CRYPTO\_CsPrivateKey} & {\small GNUNET\_CRYPTO\_Cs25519Scalar}\\\hline
Public Key & {\small GNUNET\_CRYPTO\_CsPublicKey} & {\small GNUNET\_CRYPTO\_Cs25519Point}\\\hline
$\alpha, \beta$ & {\small GNUNET\_CRYPTO\_CsBlindingSecret} & {\footnotesize 2x GNUNET\_CRYPTO\_Cs25519Scalar}\\\hline
$r$ & {\small GNUNET\_CRYPTO\_CsRSecret} & {\small GNUNET\_CRYPTO\_Cs25519Scalar}\\\hline
$R$ & {\small GNUNET\_CRYPTO\_CsRPublic} & {\small GNUNET\_CRYPTO\_Cs25519Point}\\\hline
$c$ & {\small GNUNET\_CRYPTO\_CsC} & {\small GNUNET\_CRYPTO\_Cs25519Scalar}\\\hline
$s$ & {\small GNUNET\_CRYPTO\_CsBlindS} & {\small GNUNET\_CRYPTO\_Cs25519Scalar}\\\hline
$s'$ & {\small GNUNET\_CRYPTO\_CsS} & {\small GNUNET\_CRYPTO\_Cs25519Scalar}\\\hline
$\sigma := \langle s',R' \rangle$ & {\small GNUNET\_CRYPTO\_CsSignature} & {\small GNUNET\_CRYPTO\_Cs25519Scalar}\\
& & {\small GNUNET\_CRYPTO\_Cs25519Point}\\\hline
Nonce & {\small GNUNET\_CRYPTO\_CsNonce} & 32 byte char array\\\hline
\end{tabular}
\caption{Data structures for cryptographic routines}
\label{tab:datastruct-crypto}
\end{minipage}}
\end{table}
\subsection{Library API}
The public \ac{API} and related data structures are specified in the C header file \url{src/include/gnunet_crypto_lib.h} in the GNUnet repository \cite{gnunet-git}.
It was developed in multiple iterations based on feedback from Christian Grothoff.
The complete C header \ac{API} can be found in the repository.
This section provides an overview of the implemented crypto API.
Some design decisions need to be explained further:
\begin{itemize}
\item In order to prevent misusage of our implementation and increase readability, the functions that represent different stages in the signature scheme takes different data types as in- and output.
Internally most variables are either scalars or curve points (except for nonces, secrets and messages).
\item Operations that are performed twice in the Clause Blind Schnorr Signature Scheme (e.g. derivation of $ r $) do not have to be called twice.
Instead, the API returns an array of two instead of a single value.\\
For these functions, we also optimized the \gls{hkdf} (as proposed by Christian Grothoff).
Instead of calling \gls{hkdf} twice (with different salts, e.g. "r0" and "r1"), we call it one time (e.g. with salt "r") and double the output length.
\item The cryptographic hash function used to derive $ c' $ (hash of $ R' $ and message) must map the results into the main subgroup for scalars, meaning that it has to be a \gls{fdh} (see \ref{sec:rsa-fdh}).
\end{itemize}
The following API examples should provide an overview on how the API works and how to use it.
First of all the API must provide functionality to create a \gls{25519} keypair as in listing \ref{lst:crypto-keypair-api}
\begin{lstlisting}[style=bfh-c,language=C, caption={GNUnet create keypair API}, label={lst:crypto-keypair-api}]
/**
* Create a new random private key.
*
* @param[out] priv where to write the fresh private key
*/
void
GNUNET_CRYPTO_cs_private_key_generate (
struct GNUNET_CRYPTO_CsPrivateKey *priv);
/**
* Extract the public key of the given private key.
*
* @param priv the private key
* @param[out] pub where to write the public key
*/
void
GNUNET_CRYPTO_cs_private_key_get_public (
const struct GNUNET_CRYPTO_CsPrivateKey *priv,
struct GNUNET_CRYPTO_CsPublicKey *pub);
\end{lstlisting}
The signer needs an API to generate his secret $r$ and calculate his public point $R$.
As specified in the redesign of the protocols, the r must not be chosen randomly because we need to provide \textit{\gls{abort-idempotency}}. However, the secret $r$ still needs to be \textit{unpredictable} and look random to the client.
The r\_derive API derives such a secret $r$ from a nonce and a long-term secret with \gls{hkdf}.
Further, the API ensures that a caller must generate two secret $r$ as in the Clause Blind Schnorr Signature scheme. This should discourage people from using the unsecure Blind Schnorr Signature scheme. See \ref{lst:crypto-rderive-api}.
\begin{lstlisting}[style=bfh-c,language=C, caption={GNUnet r derive API}, label={lst:crypto-rderive-api}]
/**
* Derive a new secret r pair r0 and r1.
* In original papers r is generated randomly
* To provide abort-idempotency, r needs to be derived but still needs to be UNPREDICTABLE
* To ensure unpredictability a new nonce should be used when a new r needs to be derived.
* Uses HKDF internally.
* Comment: Can be done in one HKDF shot and split output.
*
* @param nonce is a random nonce
* @param lts is a long-term-secret in form of a private key
* @param[out] r array containing derived secrets r0 and r1
*/
void
GNUNET_CRYPTO_cs_r_derive (const struct GNUNET_CRYPTO_CsNonce *nonce,
const struct GNUNET_CRYPTO_CsPrivateKey *lts,
struct GNUNET_CRYPTO_CsRSecret r[2]);
/**
* Extract the public R of the given secret r.
*
* @param r_priv the private key
* @param[out] r_pub where to write the public key
*/
void
GNUNET_CRYPTO_cs_r_get_public (const struct GNUNET_CRYPTO_CsRSecret *r_priv,
struct GNUNET_CRYPTO_CsRPublic *r_pub);
\end{lstlisting}
Same as the r\_derive, the blinding secrets are also derived and not generated randomly.
The blinding secrets are generated by a client who provides a secret as seed to derive the secrets from as in listing \ref{lst:crypto-blinding-secrets-api}.
\begin{lstlisting}[style=bfh-c,language=C, caption={GNUnet blinding secrets derive API}, label={lst:crypto-blinding-secrets-api}]
/**
* Derives new random blinding factors.
* In original papers blinding factors are generated randomly
* To provide abort-idempotency, blinding factors need to be derived but still need to be UNPREDICTABLE
* To ensure unpredictability a new nonce has to be used.
* Uses HKDF internally
*
* @param secret is secret to derive blinding factors
* @param secret_len secret length
* @param[out] bs array containing the two derivedGNUNET_CRYPTO_CsBlindingSecret
*/
void
GNUNET_CRYPTO_cs_blinding_secrets_derive (
const struct GNUNET_CRYPTO_CsNonce *blind_seed,
struct GNUNET_CRYPTO_CsBlindingSecret bs[2]);
\end{lstlisting}
Further the Clause Blind Schnorr API provides an API to calculate the two blinded c of the message with the two public $R$, the blinding factors and the public key as in listing \ref{lst:crypto-calc-c-api}.
\begin{lstlisting}[style=bfh-c,language=C, caption={GNUnet calculate blinded c API}, label={lst:crypto-calc-c-api}]
/**
* Calculate two blinded c's
* Comment: One would be insecure due to Wagner's algorithm solving ROS
*
* @param bs array of the two blinding factor structs each containing alpha and beta
* @param r_pub array of the two signer's nonce R
* @param pub the public key of the signer
* @param msg the message to blind in preparation for signing
* @param msg_len length of message msg
* @param[out] blinded_c array of the two blinded c's
*/
void
GNUNET_CRYPTO_cs_calc_blinded_c (
const struct GNUNET_CRYPTO_CsBlindingSecret bs[2],
const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
const struct GNUNET_CRYPTO_CsPublicKey *pub,
const void *msg,
size_t msg_len,
struct GNUNET_CRYPTO_CsC blinded_c[2]);
\end{lstlisting}
The sign function in our API is called sign\_derive, since we derive $b \in \{0,1\}$ from the long-term secret and then calculate the signature scalar of $c_b$.
See listing \ref{lst:crypto-sign-api}.
\begin{lstlisting}[style=bfh-c,language=C, caption={GNUnet sign API}, label={lst:crypto-sign-api}]
/**
* Sign a blinded c
* This function derives b from a nonce and a longterm secret
* In original papers b is generated randomly
* To provide abort-idempotency, b needs to be derived but still need to be UNPREDICTABLE.
* To ensure unpredictability a new nonce has to be used for every signature
* HKDF is used internally for derivation
* r0 and r1 can be derived prior by using GNUNET_CRYPTO_cs_r_derive
*
* @param priv private key to use for the signing and as LTS in HKDF
* @param r array of the two secret nonce from the signer
* @param c array of the two blinded c to sign c_b
* @param nonce is a random nonce
* @param[out] blinded_signature_scalar where to write the signature
* @return 0 or 1 for b (see Clause Blind Signature Scheme)
*/
int
GNUNET_CRYPTO_cs_sign_derive(
const struct GNUNET_CRYPTO_CsPrivateKey *priv,
const struct GNUNET_CRYPTO_CsRSecret r[2],
const struct GNUNET_CRYPTO_CsC c[2],
const struct GNUNET_CRYPTO_CsNonce *nonce,
struct GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar);
\end{lstlisting}
The API for the unblind operation can be called with the blinding secrets and the signature scalar received from the signer as in listing \ref{lst:crypto-unblind-api}.
\begin{lstlisting}[style=bfh-c,language=C, caption={GNUnet unblind API}, label={lst:crypto-unblind-api}]
/**
* Unblind a blind-signed signature using a c that was blinded
*
* @param blinded_signature_scalar the signature made on the blinded c
* @param bs the blinding factors used in the blinding
* @param[out] signature_scalar where to write the unblinded signature
*/
void
GNUNET_CRYPTO_cs_unblind (
const struct GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar,
const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
struct GNUNET_CRYPTO_CsS *signature_scalar);
\end{lstlisting}
The verify API takes the message and its signature with the public key and returns GNUNET\_OK for a valid signature and GNUNET\_SYSERR otherwhise.
See listing \ref{lst:crypto-verify-api}.
\begin{lstlisting}[style=bfh-c,language=C,, caption={GNUnet verify API}, label={lst:crypto-verify-api}]
/**
* Verify whether the given message corresponds to the given signature and the
* signature is valid with respect to the given public key.
*
* @param sig signature that is being validated
* @param pub public key of the signer
* @param msg is the message that should be signed by @a sig (message is used to calculate c)
* @param msg_len is the message length
* @returns #GNUNET_YES on success, #GNUNET_SYSERR if signature invalid
*/
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_cs_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
const struct GNUNET_CRYPTO_CsPublicKey *pub,
const void *msg,
size_t msg_len);
\end{lstlisting}
\subsection{Testing}
For digital signature schemes, the most important test case is the \textit{known good} case where a signature is created and successfully validated.
This test case already tests very much in a digital signature scheme.
When the signature creation or verification has a bug, a test will not succeed, because the mathematic operations need to be correct to be validated correctly.
The cryptographic operations are further tested for deterministicy (where it applies), meaning that multiple function calls with the same input must lead to the same output.
Since libsodium is used for the finite field arithmetic operations and is a well tested library, many cryptographic tests are already done in libsodium.
The performance is measured in a benchmark to see how performant \gls{CSBS} are in comparison to the RSA Blind Signature Scheme.
\section{Taler Cryptographic Utilities}
Taler provides utility functions to support cryptographic operations.\\
This chapter provides an overview of these utility functions and about the functionality they provide.
\subsection{Planchet Creation}
In crypto.c many utility functions are provided to create planchets (for planchet details see \ref{fig:coin:states}), blinding secrets and much more.
One difference between \gls{RSABS} and \gls{CSBS} is, that the coin private key and RSA blinding secret can be created at the same point in time, since the RSA blinding secret is created randomly.
However, for Clause Blind Schnorr secrets an additional step is needed, the public $R_0$ and $R_1$ are required to calculate the blinding seed to derive the secrets.
A planchet in the Clause Blind Schnorr Signature Scheme can be created as followed (implementation details ommited).
\begin{enumerate}
\item Create planchet with new \ac{EdDSA} private key
\item Derive withdraw nonce
\item Request public $R_0, R_1$ from signer
\item Derive blinding seed
\item Prepare (blind) the planchet
\end{enumerate}
After the planchet is created, it is sent to the exchange to be signed.
\subsection{Taler CS Security Module}
The exchange segregates access to the private keys with separate security module processes.
The security module has sole access to the private keys of the online signing keys and thus, only a security module can create signatures.
The different \textit{taler-exchange-secmod} processes (separated by signature scheme) are managing the exchanges online signing keys. The RSA denomination keys for example are managed with \textit{taler-exchange-secmod-rsa}.
Now a new \textit{taler-exchange-secmod-cs} needs to be created for managing the \gls{CSBS} denomination keys.
These security modules run on the same machine as the httpd process and they use UNIX Domain Sockets as method for \acl{IPC}.
A short introduction about UNIX Domain Sockets can be found in the blog post \cite{matt:unix-domain-sockets}.
Furthermore, the security modules are used to protect the online signing keys by performing the actual signing operations in the dedicated taler-secmod-cs process.
This abstraction makes it harder for an attacker who has already compromised the http daemon to gain access to the private keys.
However, such an attacker would still be able to sign arbitrary messages (see \cite{taler-documentation:exchange-operator-manual}).
A crypto helper exists for each security module, these functions can be called inside the exchange for operations requiring the private online signing keys.
The new Clause Schnorr security module and corresponding crypto helper provides the following functionality:
\begin{itemize}
\item Private Key Management and creation
\item Request public $R_0, R_1$
\item Request a signature of a $c_0,c_1$ pair
\item Revoke an online signing key
\end{itemize}
\subsection{Testing}
All of the operations have tests and are included in unit tests.
As a template for testing, the existing RSA tests were used and adjusted for \gls{CSBS}.
\section{Denomination Key Management}
Since we introduce a type of denomination keys, related operations like connection to the \gls{CSBS} security module, making the denominations available for customers, persisting them in the database and offline signing using the exchange's offline signature key have to be extended to incorporate the \acl{CS}.
The exchange offline signer requests the future, not yet signed keys by calling GET \url{/management/keys} as described in table \ref{tab:management-keys-get}. \\\\
\framebox[1.1\width]{\color{blue}\texttt{GET /management/keys}}
\begin{table}[ht]
\centering
\resizebox{0.9\textwidth}{!}{\begin{minipage}{\textwidth}
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{ll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Value} \\
future\_denoms & Information about denomination keys \\
future\_signkeys & Information about exchange online signing keys \\
master\_pub & Exchange's master public key \\
denom\_secmod\_public\_key & RSA security module public key \\
denom\_secmod\_cs\_public\_key & \gls{CSBS} security module public key \\
signkey\_secmod\_public\_key & Online singing security module public key \\
\end{tabular}
\caption{GET \url{/management/keys} response data}
\label{tab:management-keys-get}
\end{minipage}}
\end{table}
It then signs the keys and returns them using POST on the same \ac{URL} with the data described in table \ref{tab:management-keys-post}. \\\\
\framebox[1.1\width]{\color{blue}\texttt{POST /management/keys}}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{ll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Value} \\
denom\_sigs & Denomination key signatures \\
signkey\_sigs & Online signing key signatures \\
\end{tabular}
\caption{POST \url{/management/keys} response data}
\label{tab:management-keys-post}
\end{table}
Wallets can then call GET \url{/keys} to obtain the current denominations and other information, the response is described in table \ref{tab:keys-get}. \\\\
\framebox[1.1\width]{\color{blue}\texttt{GET /keys}}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{ll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Value} \\
version & Exchange's protocol version \\
currency & Currency \\
master\_public\_key & Exchange's master public key \\
reserve\_closing\_delay & Delay before reserves are closed \\
signkeys & Exchange's online signing public keys \\
recoup & Revoked keys \\
denoms & List of denominations \\
auditors & Auditors for this exchange \\
list\_issue\_date & Timestamp \\
eddsa\_pub & Exchange's online signing public key \\
eddsa\_sig & Signature (use "eddsa\_pub" for verification) \\
\end{tabular}
\caption{GET \url{/keys} response data}
\label{tab:keys-get}
\end{table}
\section{New Endpoint for $R$}
The withdraw and refresh protocols using the Claude Blind Schnorr Signature Scheme introduce an additional round trip.
In this round trip, the customer requests two $ R $ from the exchange.
The exchange uses a secret $ r $ to calculate $ R := rG $.
\\
In contrast to the plain Clause Blind Schnorr Signature Scheme (see \ref{sec:clause-blind-schnorr-sig}), $ r $ isn't generated randomly but instead derived using a \gls{hkdf} with a nonce from the customer and a denomination private key (secret only known by the exchange).
This still ensures that the private $ r $ can't be anticipated, but has multiple advantages regarding \gls{abort-idempotency}.
\Gls{abort-idempotency} means that a withdraw or refresh operation can be aborted in any step and later tried again (using the same values) without yielding a different result.
The challenge for $ r, R $ regarding \gls{abort-idempotency} is to ensure that the same $ r $ is used during the complete signature creation process.
The only drawback of this process is that we have to ensure that the same nonce and secret aren't used for different withdraw- or refresh-operations.
This is done during signature creation and will be described in the withdraw protocol section \ref{sec:specification-withdraw}.
\subsection{Public APIs and Data Structures}
This is a new functionality, meaning a new endpoint accessible to customers has to be introduced.
It will be made available in the exchange HTTP server under \framebox[1.1\width]{\color{blue}\texttt{POST /csr}} and will take the input parameters described in table \ref{tab:csr-request-data} (as \ac{JSON}).
\begin{table}[ht]
\centering
\resizebox{0.9\textwidth}{!}{\begin{minipage}{\textwidth}
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
nonce & String & 32 Bytes encoded in Crockford base32 Hex \\
denom\_pub\_hash & String & Denomination Public Key encoded in Crockford base32 Hex \\
\end{tabular}
\caption{POST \url{/csr} request data}
\label{tab:csr-request-data}
\end{minipage}}
\end{table}
The exchange will then check the denomination and return one of these HTTP status codes:
\begin{itemize}
\item \textbf{200 (HTTP\_OK)}: Request Successful
\item \textbf{400 (BAD\_REQUEST)}: Invalid input parameters
\item \textbf{404 (NOT\_FOUND)}: Denomination unknown or not Clause Schnorr
\item \textbf{410 (GONE)}: Denomination revoked/expired
\item \textbf{412 (PRECONDITION\_FAILED)}: Denomination not yet valid
\end{itemize}
When the request was successful, the exchange returns the data described in table \ref{tab:csr-response-data} (as \ac{JSON}).
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
r\_pub\_0 & String & 32 Bytes encoded in Crockford base32 Hex \\
r\_pub\_1 & String & 32 Bytes encoded in Crockford base32 Hex \\
\end{tabular}
\caption{POST \url{/csr} response data}
\label{tab:csr-response-data}
\end{table}
\subsection{Persistence}
This API does not persist anything.
This is because the resulting $R_0, R_1$ are derived and can be derived in a later step.
\section{Withdraw Protocol}
\label{sec:specification-withdraw}
The withdraw protocol has been introduced in section \ref{sec:withdrawal}.
For the \acl{CS} necessary adjustments are described in section \ref{sec:withdraw-protocol-schnorr}.
\subsection{Public APIs and Data Structures}
\label{sec:specification-withdraw-public-api}
The existing endpoint is available under \texttt{POST /reserves/[reserve]/withdraw} where "reserve" is the reserve public key encoded as Crockford base32.
It takes the following input parameters described in table \ref{tab:withdraw-request-data} as JSON.\\\\
\framebox[1.1\width]{\color{blue}\texttt{POST /reserves/[reserve]/withdraw}}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{ll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Value} \\
denom\_pub\_hash & Denomination Public Key \\
coin\_ev & RSA blinded coin public key \\
reserve\_sig & Signature over the request using the reserve's private key \\
\end{tabular}
\caption{Withdraw request data}
\label{tab:withdraw-request-data}
\end{table}
In order to facilitate parsing, Christian Grothoff suggested to include the cipher type in the "coin\_ev" field, thus creating a nested \ac{JSON} (as described in table \ref{tab:withdraw-coin-ev-rsa}).
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
cipher & Integer & Denomination cipher: 1 stands for RSA \\
rsa\_blinded\_planchet & String & RSA blinded coin public key \\
\end{tabular}
\caption{Withdraw "coin\_ev" field (RSA)}
\label{tab:withdraw-coin-ev-rsa}
\end{table}
For the Clause Schnorr implementation, the field "rsa\_blinded\_planchet" will be replaced with the necessary values as described in table \ref{tab:withdraw-coin-ev-cs}.
\begin{table}[ht]
\centering
\resizebox{0.85\textwidth}{!}{\begin{minipage}{\textwidth}
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
cipher & Integer & Denomination cipher: 2 stands for \gls{CSBS} \\
cs\_nonce & String & 32 Bytes encoded in Crockford base32 Hex \\
cs\_blinded\_c0 & String & 32 Bytes encoded in Crockford base32 Hex \\
cs\_blinded\_c1 & String & 32 Bytes encoded in Crockford base32 Hex \\
\end{tabular}
\caption{Withdraw "coin\_ev" field (\gls{CSBS})}
\label{tab:withdraw-coin-ev-cs}
\end{minipage}}
\end{table}
The exchange will then process the withdraw request and return one of these HTTP status codes:
\begin{itemize}
\item \textbf{200 (HTTP\_OK)}: Request Successful
\item \textbf{400 (BAD\_REQUEST)}: Invalid input parameters (can also happen if denomination cipher doesn't match with cipher in "coin\_ev")
\item \textbf{403 (FORBIDDEN)}: Signature contained in "reserve\_sig" invalid
\item \textbf{404 (NOT\_FOUND)}: Denomination unknown
\item \textbf{410 (GONE)}: Denomination revoked/expired
\item \textbf{412 (PRECONDITION\_FAILED)}: Denomination not yet valid
\end{itemize}
When the request was successful, the exchange returns the RSA signature as JSON (described in table \ref{tab:withdraw-response-rsa}).
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
cipher & Integer & Denomination cipher: 1 stands for RSA \\
blinded\_rsa\_signature & String & RSA signature \\
\end{tabular}
\caption{Withdraw response (RSA)}
\label{tab:withdraw-response-rsa}
\end{table}
Table \ref{tab:withdraw-response-cs} describes the response for \gls{CSBS}.
\begin{table}[ht]
\centering
\resizebox{0.85\textwidth}{!}{\begin{minipage}{\textwidth}
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
cipher & Integer & Denomination cipher: 2 stands for \gls{CSBS} \\
b & Integer & \gls{CSBS} signature session identifier (either 0 or 1) \\
s & String & signature scalar (32 Bytes encoded in Crockford base32 Hex) \\
\end{tabular}
\caption{Withdraw response (\gls{CSBS})}
\label{tab:withdraw-response-cs}
\end{minipage}}
\end{table}
\subsection{Persistence}
Persistence for withdrawing is implemented in the function \texttt{postgres\_do\_withdraw} in \texttt{src/exchangedb/plugin\_exchangedb\_postgres.c}
For \gls{CSBS}, persisting the blinded signature must be implemented.
\section{Deposit Protocol}
For the deposit protocol (described in section \ref{sec:deposit-protocol}) only the handling and verification of \gls{CSBS} signatures has to be added.
\subsection{Public APIs and Data Structures}
Deposit is an existing endpoint available under \texttt{POST /coins/[coin public key]/deposit} where "coin public key" is encoded as Crockford base32.
Additional parameters are passed as JSON (as described in table \ref{tab:spend-request}).\\\\
\framebox[1.1\width]{\color{blue}\texttt{POST /coins/[coin public key]/deposit}}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{ll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Value} \\
% cipher & Denomination cipher: 2 stands for \gls{CSBS} \\
% b & \gls{CSBS} signature session identifier (either 0 or 1) \\
% s & signature scalar (32 Bytes encoded in Crockford base32 Hex) \\
merchant\_payto\_uri & Account that is credited \\
wire\_salt & Salt used by the merchant \\
contribution & Amount to use for payment (for one specific coin) \\
denom\_pub\_hash & Denomination public key hash \\
ub\_sig & (unblinded) denomination signature of coin \\
merchant\_pub & Merchant public key \\
h\_contract\_terms & Contract terms hash \\
coin\_sig & Deposit permission signature \\
timestamp & Timestamp of generation \\
refund\_deadline (optional) & Refund deadline \\
wire\_transfer\_deadline (optional) & Wire transfer deadline \\
\end{tabular}
\caption{Spend request}
\label{tab:spend-request}
\end{table}
Relevant field for the \gls{CSBS} implementation is the field "ub\_sig" containing the unblinded denomination signature of the coin.
For RSA, the (nested) \ac{JSON} is described in table \ref{tab:spend-request-ubsig-rsa}.
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
cipher & Integer & Denomination cipher: 1 stands for RSA \\
rsa\_signature & String & Unblinded RSA signature \\
\end{tabular}
\caption{ub\_sig (RSA)}
\label{tab:spend-request-ubsig-rsa}
\end{table}
Table \ref{tab:spend-request-ubsig-cs} describes the values in "ub\_sig" required for \gls{CSBS}.
\begin{table}[ht]
\centering
\resizebox{0.85\textwidth}{!}{\begin{minipage}{\textwidth}
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Field} & \textbf{Type} & \textbf{Value} \\
cipher & Integer & Denomination cipher: 2 stands for \gls{CSBS} \\
cs\_signature\_r & String & Curve point $ R' $ (32 Bytes encoded in Crockford base32 Hex) \\
cs\_signature\_s & String & Signature scalar (32 Bytes encoded in Crockford base32 Hex) \\
\end{tabular}
\caption{ub\_sig (\gls{CSBS})}
\label{tab:spend-request-ubsig-cs}
\end{minipage}}
\end{table}
\subsection{Persistence}
Persistence is handled in the functions \texttt{postgres\_insert\_deposit} and\\ \texttt{postgres\_have\_deposit} located in \url{src/exchangedb/plugin_exchangedb_postgres.c}.
However, these functions are not containing \gls{CSBS}-specific persistence.
\\What needs to be adjusted however, is the function \texttt{postgres\_ensure\_coin\_known} called by the function \texttt{TEH\_make\_coin\_known} (located in \url{src/exchange/taler-exchange-httpd_db.c}).
% \section{Tipping}
% \subsection{Public APIs and Data Structures}
% \subsection{Code Location}
% \subsection{Persistence}
% \subsection{Used Libraries}
% \section{Payback Protocol}
% \subsection{Public APIs and Data Structures}
% \subsection{Code Location}
% \subsection{Persistence}
% \subsection{Used Libraries}
% sollte ein Product Backlog das Ziel dieser Phase sein?

View File

@ -0,0 +1,333 @@
\chapter{Implementation}
\label{chap:implement}
This chapter gives an overview on the implementation challenges and discusses special parts in the implementation.
\section{Signature Scheme Operations}
The signature scheme operations are implemented in the GNUnet core repository \cite{gnunet-git} (and have been merged into the master branch).
This would allow other GNUnet projects to use our implementation of the Clause Blind Schnorr Signature Scheme.
The implementation is done in multiple locations:
\begin{itemize}
\item \texttt{src/include/gnunet\_crypto\_lib.h}:
This header file is included when using GNUnet's cryptography implementation.
\item \texttt{src/util/crypto\_cs.c}:
The functions specified in \texttt{gnunet\_crypto\_lib.h} will be implemented here.
\item \texttt{src/util/test\_crypto\_cs.c}:
The test cases for the signature scheme will be implemented here.
\item \texttt{src/util/perf\_crypto\_cs.c}:
This file houses the implementation of a small program that will be used to compare the performance against the blind RSA Signature Scheme.
\end{itemize}
The specification explaining the \ac{API} can be found in section \ref{sec:specification-signature-scheme}. There are two internal functions that have to be explained further in this section.
The \texttt{map\_to\_scalar\_subgroup} function clamps scalars, which is necessary for values that are derived using a \gls{hkdf}.
It sets the three least significant bits to zero (making the scalar a multiple of 8), sets the most significant bit to zero and the second-most significant bit to one.
This process is further described in \cite{rfc7748} and \cite{madden:curve25519-clamping}.
\begin{lstlisting}[style=bfh-c, language=C, caption={Function map\_to\_scalar\_subgroup - Crypto API}, label={lst:map-to-scalar}]
static void
map_to_scalar_subgroup (struct GNUNET_CRYPTO_Cs25519Scalar *scalar)
{
scalar->d[0] &= 248;
scalar->d[31] &= 127;
scalar->d[31] |= 64;
}
\end{lstlisting}
Another important function is the \gls{fdh} (see \ref{sec:schnorr-sig}) used to map the message to a scalar.
GNUnet provides a \gls{fdh} function, which expects libgcrypt's multi precision format.
A conversion function is provided by GNUnet, which requires the data to be in big endian format.
Since libsodium uses a little endian representation, the conversion process must include endianness conversion.
The complete \gls{fdh} including the required conversions is implemented in the function described in listing \ref{lst:cs-fdh}.
\begin{lstlisting}[style=bfh-c, language=C, caption={Function cs\_full\_domain\_hash - Crypto API}, label={lst:cs-fdh}]
static void
cs_full_domain_hash (const struct GNUNET_CRYPTO_CsRPublic *r_dash,
const void *msg,
size_t msg_len,
const struct GNUNET_CRYPTO_CsPublicKey *pub,
struct GNUNET_CRYPTO_CsC *c)
{
...
\end{lstlisting}
Last but not least, the implementation has one notable performance improvement not mentioned in the redesigned protocols.
In various steps \gls{hkdf} is used multiple times in a row.
For example to derive the four blinding secrets $\alpha_0, \alpha_1, \beta_0, \beta_1$.
The derivation can be done in one \gls{hkdf} call with bigger output size, 128 bit in this case.
The output then can be split in four parts and then mapped to the ed25519 subgroup.
This can be done secure, because as explained in \autoref{sec:kdf} a \gls{hkdf} output is truly random.
\section{Taler Cryptographic Utilities}
\begin{bfhNoteBox}
Implementation is done in Taler's exchange.
From here on the implementation can be found in the exchange git repository \cite{taler-git:exchange}.
\end{bfhNoteBox}
The cryptographic utilities of Taler can be found in \texttt{src/util}.
The implementation is done in various locations:
\begin{itemize}
\item \texttt{src/include/taler\_crypto\_lib.h}: This header file is included when using Taler's cryptography implementation.
The different data structures and functionality are defined here.
\item \texttt{src/util/denom.c}: Implement denomination utility functions for \gls{CSBS} cases
\item \texttt{src/util/crypto.c}: Adjust all utility functions to support \gls{CSBS}.
crypto.c contains many cryptographic utility functions, for example to create planchets or blinding factors.
\item \texttt{src/util/test\_crypto.c}: Functionality tests for crypto.c and denom.c
\item \texttt{src/include/taler\_signatures.h}: In this header file message formats and signature constants are defined (not modified)
\item \texttt{src/util/secmod\_signatures.c}: Utility functions for Taler security module signatures
\end{itemize}
The security module \texttt{taler-secmod-cs} is implemented here:
\begin{itemize}
\item \texttt{src/util/taler-exchange-secmod-cs.c}: Standalone process to perform private key Clause Blind Schnorr signature operations.
\item \texttt{src/util/taler-exchange-secmod-cs.h}: Specification of \ac{IPC} messages for the CS secmod process
\item \texttt{src/util/taler-exchange-secmod-cs.conf}: Configuration file for the secmod process
\item \texttt{src/util/secmod\_common.c} and \texttt{src/util/secmod\_common.h}: Common functions for the exchange's security modules (not modified)
\end{itemize}
The corresponding crypto helper, that talks with the security module, and its tests \& benchmarks are implemented here:
\begin{itemize}
\item \texttt{src/util/crypto\_helper\_cs.c}: Utility functions to communicate with the security module
\item \texttt{src/util/crypto\_helper\_common.c}: and \texttt{crypto\_helper\_common.h}: Common functions for the exchange security modules (not modified)
\item \texttt{src/util/test\_helper\_cs.c}: Tests and benchmarks for the \gls{CSBS} crypto helper
\end{itemize}
% Crypto API offene Punkte:
%Input-Validierung von Punkten und Skalar
% Clamping beschreiben: https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/
% Testing: inverse operations, blinded signature test
\section{Denomination Key Management}
For the implementation, the \gls{CSBS} security module had to be connected to the key handling and the \gls{CSBS} denominations had to be integrated:
\begin{itemize}
\item \url{src/exchange/taler-exchange-httpd_keys.h} and \\
\url{src/exchange/taler-exchange-httpd_keys.c}: Integrate \gls{CSBS} secmod and denomination key management
\item \url{src/exchange-tools/taler-exchange-offline.c}: Implement \gls{CSBS} case for offline signing of denomination keys
\item \url{src/include/taler_exchange_service.h}: \\
Add \gls{CSBS} secmod public key to struct\\TALER\_EXCHANGE\_FutureKeys
\item \url{src/json/json_helper.c}: Implement CS case in function parse\_denom\_pub (used in taler-exchange-offline.c)
\item \url{src/json/json_pack.c}: Implement \gls{CSBS} case in function TALER\_JSON\_pack\_denom\_pub (used in taler-exchange-httpd\_keys.c)
\item \url{src/pq/pq_query_helper.c}: Implement \gls{CSBS} case in function qconv\_denom\_pub
\item \url{src/pq/pq_result_helper.c}: Implement \gls{CSBS} case in function extract\_denom\_pub
\end{itemize}
In order for the tests to pass, the following changes had to be implemented:
\begin{itemize}
\item \url{src/lib/exchange_api_management_get_keys.c}: Add denom\_secmod\_cs\_public\_key JSON parsing, implement \gls{CSBS} case \\in function TALER\_EXCHANGE\_ManagementGetKeysHandle
\item \url{src/testing/.gitignore}: Add paths where \gls{CSBS} keys are stored (secmod-helper)
\item \url{src/testing/test_auditor_api.conf}: Add section taler-exchange-secmod-cs
\item \url{src/testing/test_exchange_api_keys_cherry_picking.conf}: Add section taler-exchange-secmod-cs
\item \url{src/testing/testing_api_helpers_exchange.c}: Add \gls{CSBS} secmod start and stop logic
\end{itemize}
\section{New Endpoint for $R$}
The new endpoint is available in the exchange's HTTP server under \url{/csr}.
It parses and checks the input, passes the request for derivation of the two $ R $'s down to the \gls{CSBS} security module and returns them to the requestor.
The implementation can be found in:
\begin{itemize}
\item \url{src/exchange/taler-exchange-httpd.c}:
Definition for the new endpoint, calls the function that handles \url{/csr} requests
\item \url{src/exchange/taler-exchange-httpd_responses.h} and \\
\url{src/exchange/taler-exchange-httpd_responses.c}: \\
Added function TEH\_RESPONSE\_reply\_invalid\_denom\_cipher\_for\_operation that indicates a failure when the endpoint is called for a non-\gls{CSBS} denomination
\item \url{src/exchange/taler-exchange-httpd_csr.h} and \\
\url{src/exchange/taler-exchange-httpd_csr.c}: \\
Implementation of the request handler for the new endpoint
\item \url{src/exchange/taler-exchange-httpd_keys.h} and \\
\url{src/exchange/taler-exchange-httpd_keys.c}: \\
Additional function TEH\_keys\_denomination\_cs\_r\_pub that passes down the request to derive the $ R $ to the taler-exchange-secmod-cs helper
\end{itemize}
The tests that check the functionality of the procotols are defined in \url{src/testing/} and use code that calls the \ac{API} (located in \url{src/lib/}).
Since the new endpoint is used during withdrawing coins, testing for the \url{/csr} endpoint is integrated in these protocol tests.
Therefore, a call to the endpoint was implemented and later integrated into the calls to the withdraw-\ac{API}.
Code for calling the endpoint is located in these files:
\begin{itemize}
\item \url{src/include/taler_exchange_service.h}: \\
Header describing functions and data structures used in withdraw and refresh testing:
\begin{itemize}
\item struct TALER\_EXCHANGE\_CsRHandle: Handle containing request information
\item struct TALER\_EXCHANGE\_CsRResponse: Response details
\item function TALER\_EXCHANGE\_CsRCallback: Callback function to deliver the results (used in withdraw and refresh)
\item function TALER\_EXCHANGE\_csr: Used to call endpoint
\item function TALER\_EXCHANGE\_csr\_cancel: Used to free dynamically allocated resources
\end{itemize}
\item \url{src/lib/exchange_api_csr.c}: Implementation of \url{/csr} request
\end{itemize}
\section{Withdraw Protocol}
\label{sec:withdraw-protocol-impl}
Since this is an existing endpoint, it was adjusted to support \gls{CSBS}.
Mainly, the in- and output-handling had to be adjusted as described in section \ref{sec:specification-withdraw-public-api}, additional cipher checks for the denomination were added and the \gls{CSBS} for persisting the request in the database was implemented.
\\\\
An interesting part of the implementation is the check whether a nonce was already used for this denomination or not (step: $s \leftarrow \text{GetWithdraw}(n_w, D_p)$).
This step ensures that the same signature will always be returned for a certain nonce.
Using the same nonce for the same denomination twice without this check would lead to the same random value $r$.
This is due to derivation of $r := \text{HKDF}(256,n_w || d_s, \text{"r"})$.
An attacker could then immediately recover the secret key by the following equation: $(h' - h) * x \mod q = s -s' \mod q$ \cite{tibouchi:attacks-schnorr-nonce}.
There are popular examples of this vulnerability in Sony Playstation 3's or Bitcoins ECDSA implementation \cite{buchanan:ps3-ecdsa-vuln} \cite{wang:bitcoin-ecdsa-vuln}.
More details on how such a vulnerability can be exploited can be found in one of the author's blog posts \cite{gian:nonce-sense}.\\
The designed Taler protocols using \gls{CSBS} are preventing this attack by checking the nonce and return the previously generated signature.
Additionally the denomination's public key is included in this check to prevent another issue explained in section \ref{sec:taler-vuln}.\\
The check is implemented by persisting a hash value over $n_w$ and $D_p$.
On every withdrawal \texttt{check\_request\_idempotent()} is called, which checks whether the persisted hash matches with the current $n_w, D_p$ pair.
\begin{itemize}
\item \url{src/exchange/taler-exchange-httpd_withdraw.c}: Implementation of \gls{CSBS} case for withdraw endpoint
\item \url{src/exchange/taler-exchange-httpd_keys.c}: Implement \gls{CSBS} case in function \\
TEH\_keys\_denomination\_sign (passes the signature creation down to the crypto helpers)
\item \url{src/include/taler_json_lib.h} and \url{src/json/json_helper.c}: \\
Add function TALER\_JSON\_spec\_blinded\_planchet
\item \url{src/json/json_pack.c}: \\
Implement \gls{CSBS} case in function\\ TALER\_JSON\_pack\_blinded\_denom\_sig
\item \url{src/pq/pq_query_helper.c}: implement \gls{CSBS} case in functions qconv\_denom\_sig and qconv\_blinded\_denom\_sig
\item \url{src/pq/pq_result_helper.c}: Implement \gls{CSBS} case in function extract\_blinded\_denom\_sig
\end{itemize}
For testing, the \gls{CSBS}-related data structures and procedures as well as the request to the additional endpoint \url{/csr} (before performing the actual withdrawal) were integrated:
\begin{itemize}
\item \url{src/testing/test_exchange_api.c}: Add additional tests for \gls{CSBS} withdraw
\item \url{src/include/taler_testing_lib.h}: Specification for functions \\
TALER\_TESTING\_cmd\_withdraw\_cs\_amount and \\
TALER\_TESTING\_cmd\_withdraw\_cs\_amount\_reuse\_key, add denomination cipher parameter to function TALER\_TESTING\_find\_pk
\item \url{src/testing/testing_api_cmd_withdraw.c}: add functions \\
TALER\_TESTING\_cmd\_withdraw\_cs\_amount and \\
TALER\_TESTING\_cmd\_withdraw\_cs\_amount\_reuse\_key, implement \gls{CSBS}-specific logic for withdraw
\item \url{src/testing/testing_api_helpers_exchange.c}:
add cipher parameter to function TALER\_TESTING\_find\_pk
\item \url{src/lib/exchange_api_withdraw.c}: Implement \gls{CSBS}-specific withdraw logic, integrate \url{/csr} request
\item \url{src/lib/exchange_api_withdraw2.c}: implement \gls{CSBS} case
\item \url{src/include/taler_json_lib.h} and \url{src/json/json_pack.c}: \\
Add function TALER\_JSON\_pack\_blinded\_planchet
\item \url{src/json/json_helper.c} implement \gls{CSBS} case in function parse\_blinded\_denom\_sig
\end{itemize}
\section{Deposit Protocol}
For deposit, only few changes were necessary because some of the required functionality has already been added for the previously implemented protocols, and only the coin signature verification is \gls{CSBS}-specific in this protocol.
\begin{itemize}
\item \url{/src/exchange/taler-exchange-httpd_deposit.c}: Add check whether denomination cipher and denomination signature cipher are equal
\item \url{/src/json/json_helper.c}: Implement \gls{CSBS} case in function parse\_denom\_sig
\item \url{/src/pq/pq_result_helper.c}: Implement \gls{CSBS} case in function extract\_denom\_sig
\end{itemize}
Tests for deposit are implemented here:
\begin{itemize}
\item \url{/src/testing/test_exchange_api.c}: Add tests (see "struct TALER\_TESTING\_Command\ spend\_cs[]") that spend \gls{CSBS} coins withdrawn in tests added for withdrawal
\item \url{/src/json/json_pack.c}: Implement \gls{CSBS} case in function TALER\_JSON\_pack\_denom\_sig
\end{itemize}
\section{Fixing a Minor Security Issue in Taler's RSA Blind Signature Protocols}
\label{sec:taler-vuln}
While implementing the nonce check in the \gls{CSBS} protocol (see section \ref{sec:withdraw-protocol-impl}), a minor security issue in Taler's current RSA Blind Signature implementation was detected and fixed.
The issue was only in the implementation of the current RSA Blind Signature protocols, the fix for this scenario was already implemented in \gls{CSBS} since the beginning.
\subsection{Security Issue}
\label{sec:taler-vuln-desc}
The redesigned \gls{CSBS} protocols already include the denomination key in the nonce check, which fixes this issue (see \ref{sec:withdraw-protocol-schnorr}).
In the case of \gls{RSABS}, the current protocol includes an \gls{idempotence} check by persisting the hash value of the blinded coin $m'$.
On a withdrawal/refresh the \gls{idempotence} check compares if the hash value of $m'$ was seen in the past and returns the 'old' signature on a match.
This could lead to the following scenario:
\begin{enumerate}
\item A broken wallet withdraws a coin with denomination $D_{p_{(1)}}$.
\item The wallet sends a request to withdraw the same coin for denomination $D_{p_{(2)}}$.
\item The exchange returns the signature for the denomination $D_{p_{(1)}}$ due to the \gls{idempotence} check.
\item Since the exchange returned an invalid signature, the customer can file a complaint at the auditor.
\item The auditor then has to investigate why the exchange returned invalid signatures.
\item The auditor can disprove the complaint by querying the persisted hash used for the \gls{idempotence} check.
With the associated denomination public key that is also persisted, the auditor can successfully verify the signature and thus prove that the exchange operated honestly.
\end{enumerate}
Including the denomination public key into the persisted hash for the \gls{idempotence} check solves this issue.
If a broken wallet now sends the same coin for more than one denomination, the exchange returns valid signatures in both cases.\\
While this is still an issue, this case is already handled nicely in Taler since this situation could also occur if a broken value tries to withdraw the same coin with two different blinding factors.
\subsection{Impact}
The impact of this security vulnerability is considered as very low.
An auditor investigating such an issue can simply retrace what happened by checking the persisted hash and associated denomination.
The impact of the issue is, that an auditor needs to investigate an issue, which can be prevented inside the protocol.
\\
In the previous section the client was considered a broken wallet.
While this could be done on purpose by malicious a customer, there is no real motivation for abusing this issue due the easy detection of an auditor.
\subsection{Fix}
Listing \ref{lst:rsa-idempotence} shows the code of calculating the hash for the idempotency check in the RSA case before it was fixed.
By trying to implement the \gls{CSBS} case, the question came up why the RSA case has not included the denomination key into the check.
After discussing this issue with Christian Grothoff, the conclusion was to include the denomination public key to prevent the discussed issue.
\begin{lstlisting}[style=bfh-c,language=C, caption={Idempotency check on RSA}, label={lst:rsa-idempotence}]
enum GNUNET_GenericReturnValue
TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
struct TALER_BlindedCoinHash *bch)
{
switch (blinded_planchet->cipher)
{
case TALER_DENOMINATION_RSA:
GNUNET_CRYPTO_hash (
blinded_planchet->details.rsa_blinded_planchet.blinded_msg,
blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size,
&bch->hash);
return GNUNET_OK;
case TALER_DENOMINATION_CS:
...
\end{lstlisting}
The issue is fixed by adding a hash of the current denomination key into the calculation of the hash used in the \gls{idempotence} check.
The applied fix can be seen in listing \ref{lst:fixed-idempotence}.
\begin{lstlisting}[style=bfh-c,language=C, caption={Fixed idempotency check}, label={lst:fixed-idempotence}]
enum GNUNET_GenericReturnValue
TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
const struct TALER_DenominationHash *denom_hash,
struct TALER_BlindedCoinHash *bch)
{
switch (blinded_planchet->cipher)
{
case TALER_DENOMINATION_RSA:
{
struct GNUNET_HashContext *hash_context;
hash_context = GNUNET_CRYPTO_hash_context_start ();
GNUNET_CRYPTO_hash_context_read (hash_context,
&denom_hash->hash,
sizeof(denom_hash->hash));
GNUNET_CRYPTO_hash_context_read (hash_context,
blinded_planchet->details.
rsa_blinded_planchet.blinded_msg,
blinded_planchet->details.
rsa_blinded_planchet.blinded_msg_size);
GNUNET_CRYPTO_hash_context_finish (hash_context,
&bch->hash);
return GNUNET_OK;
}
case TALER_DENOMINATION_CS:
{
struct GNUNET_HashContext *hash_context;
hash_context = GNUNET_CRYPTO_hash_context_start ();
GNUNET_CRYPTO_hash_context_read (hash_context,
&denom_hash->hash,
sizeof(denom_hash->hash));
GNUNET_CRYPTO_hash_context_read (hash_context,
&blinded_planchet->details.
cs_blinded_planchet.nonce,
sizeof (blinded_planchet->details.
cs_blinded_planchet.nonce));
GNUNET_CRYPTO_hash_context_finish (hash_context,
&bch->hash);
return GNUNET_OK;
}
default:
GNUNET_break (0);
return GNUNET_SYSERR;
}
}
\end{lstlisting}

View File

@ -0,0 +1,5 @@
\input{content/4_1_design.tex}
\input{content/4_2_specification.tex}
\input{content/4_3_implementation.tex}

View File

@ -0,0 +1,317 @@
\chapter{Discussion}
\label{chap:disc}
This chapter analyses the \acl{CS} implementation and compares it to the existing implementation with \gls{RSABS}.
The comparison will include the schemes itself, performance comparisons and a discussion on the security assumptions.
For the performance comparison CPU usage, latency, bandwidth and storage space are compared.
\section{Cipher Agility}
One of the benefits of having another blind signature scheme in Taler is \textit{cipher agility}.
Cipher agility means that one scheme can substitute another, for example if one scheme gets compromised in the future.
Cipher agility is considered harmful in certain situations.
TLS 1.2 \cite{rfc5246} and IPSEC/IKEv2 \cite{rfc6071} are good examples on how dangerous cipher agility inside protocols can be.
There are many ways these protocols can be set up insecure.
\\\\
Taler's protocols are built around blind signature schemes.
Therefore it is crucial to have an additional secure blind signature scheme that works with all Taler protocols.
As described in section \ref{sec:blind-sign-schemes}, blind signature schemes can vary and may be complex to substitute.
The \gls{CSBS} implementation provides such an alternative and thus \textit{cipher agility}.
\section{Scheme Comparison}
\label{chap:disc-scheme-comp}
Both schemes are explained in the preliminaries chapter (\gls{RSABS} in section \ref{fig:rsa-fdh-blind-sign} and \gls{CSBS} in \ref{fig:clause-blind-schnorr-sign-scheme}).
\\
There are multiple differences worth mentioning.
The first difference is that Schnorr signatures are inherently randomized.
This is also where the additional step in Schnorr signatures comes from.
A random number is chosen by the signer for every signature. \\
In \gls{CSBS} two blinding secrets are used instead of one in \gls{RSABS}.
On top of that, \gls{CSBS} needs to do most computations for signature creation twice, due to the \ac{ROS} problem (see \ref{par:ros-problem}).
\\\\
\textit{\Gls{abort-idempotency}} is a very important property for Taler.
Ensuring \gls{abort-idempotency} with the \gls{CSBS} scheme is harder than it was with RSA, due to the many random elements in the scheme ($r_0, r_1, \alpha_0, \alpha_1, \beta_0, \beta_1, b$).
The reason that these values are chosen randomly is the need for \textit{unpredictability}.\\
In the protocols (see chapter \ref{chap:design}) \gls{hkdf} is extensively used to derive these values instead of randomly generating them.
That way, the values are still \textit{unpredictable} (due to \gls{hkdf} properties), but now the protocols also ensure \textit{\gls{abort-idempotency}}.
In comparison to the RSA Blind Signature scheme, this is a clever and elegant solution, but the protocol complexity is increased.
\\\\
One could now think that RSA would be much simpler to implement, since the scheme looks easier and more accessible for many.
This can go horribly wrong and many developers still underestimate implementing RSA.
There are a lot of attacks on RSA, some examples are listed on the famous tool RsaCtfTool \cite{ganapati:rsactftool}.
Ben Perez made a popular talk and blog post, about why one should stop using RSA and should preferably use libsodium and \ac{ECC} \cite{perez:stoprsa}.
Using \gls{RSABS} in Taler is still a reasonable and fine choice.
Taler uses libgcrypt, a well-known and tested library.
\\
To conclude, the \gls{CSBS} protocols might be more complex to understand than the RSA Blind Signature protocols.
One has to keep in mind that implementing RSA correctly is hard.
\\
Another difference worth mentioning is, that the \gls{CSBS} scheme does not need scheme specific configurations, whereas RSA needs a key size specified.
This is because the implemented \gls{CSBS} version only supports \gls{25519}.
\\\\
Furthermore, both schemes provide \textit{perfect blindness}, see paragraph \ref{par:prop-blindness-rsa} for RSA and paragraph \ref{par:prop-blindness-cs} for \gls{CSBS}.
\section{Performance Comparison}
\label{sec:disc-perf-comp}
This section compares how the two schemes perform regarding CPU usage, latency, bandwidth and space.
Clause Schnorr has fixed key sizes with 256 bits (32 bytes), which we compare against different RSA key sizes (1024, 2048, 3072 and 4096 bits).
In terms of security, \gls{CSBS} 256 bit keys could be compared to 3072 bit RSA keys (see \url{https://www.keylength.com/} for more information).
\subsection{CPU Usage}
Various benchmarks were made on different CPU architectures.
This section discusses the main results, detailed information about the performance comparison can be found in appendix \ref{chap:app-perf}.
We thank the Taler team for providing measurements from additional systems and architectures.
Table \ref{tab:comp-cs-vs-rsa-3072} shows how \gls{CSBS} compares to RSA 3072.
RSA 3072 was chosen for comparison, since they both provide a comparable level of security.
Both provide about 128 bits of security, which means that roughly $2^{128}$ attempts in average are needed for a successful brute-force attack.\\
The table shows that \gls{CSBS} has better performance compared to RSA 3072 in all operations.
The biggest difference can be seen in the key generation.
In RSA, two random primes are needed, whereas \ac{DLP} algorithms like \gls{CSBS} only need to generate a random value.
Since key generation is done rarely compared to the other operations, the time needed for key generation does not matter that much.\\
Furthermore, the blinding in \gls{CSBS} is still faster than blinding in RSA, although in the \gls{CSBS} case the calculation is done twice. Also the derivation of $r_0,r_1$, the generation of $R_0,R_1$ and the derivation of $\alpha_0, \beta_0, \alpha_1, \beta_1$ is included in the measurement for the blinding operation of \gls{CSBS}.
Signing and blinding operations are much faster in \gls{CSBS}, also \gls{CSBS} signature verification is faster than RSA 3072.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 8-core AMD Ryzen 7 PRO 5850U \\
OS: Ubuntu 21.10 Linux 5.13.0-25-generic \#26-Ubuntu SMP Fri Jan 7 15:48:31 UTC 2022 x86\_64 x86\_64 x86\_64 GNU/Linux \\
libsodium version: 1.0.18-1build1 \\
libgcrypt version: 1.8.7-5ubuntu2 \\\\
Benchmarks with other hardware setups can be found in appendix \ref{chap:app-perf}.
\end{bfhBox}
\begin{table}[h]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.204 ms \\\hline
RSA 3072 bit & 10x key generation & 2684 ms \\\hline
\hline
CS & 10x derive $R_0, R_1$ \& blinding & 3.870 ms \\\hline
RSA 3072 bit & 10x blinding & 5 ms \\\hline
\hline
CS & 10x signing & 0.077 ms \\\hline
RSA 3072 bit & 10x signing & 86 ms \\\hline
\hline
CS & 10x unblinding & 0.001 ms \\\hline
RSA 3072 bit & 10x unblinding & 24 ms \\\hline
\hline
CS & 10x verifying & 1.358 ms \\\hline
RSA 3072 bit & 10x verifying & 3.075 ms \\\hline
\end{tabular}
\caption{Comparison on CS vs. RSA 3072}
\label{tab:comp-cs-vs-rsa-3072}
\end{table}
Table \ref{tab:comp-cs-vs-rsa-1024} shows a comparison between \gls{CSBS} and RSA 1024 bit.
RSA 1024 is in some situations faster than the \gls{CSBS} implementation.
Note that 1024 bit keys are not recommended for many use cases, but the highest currently known RSA factorization done is 829 bits \cite{enwiki:1055393696}.
The following section \ref{sec:disc-risk} explains the risk running RSA 1024 or \gls{CSBS} denominations further.\\
The blind and unblind operations are running in a wallet implementation, therefore the comparison with RSA 1024 is very interesting for devices with less CPU power.
Comparison of such hardware can be found in appendix \ref{chap:app-perf}, these comparison results come to the same conlcusion.\\
Although RSA 1024 bit is much faster in the blinding operation, \gls{CSBS} still perform better when calculating the blinding and unblinding operations together.
\gls{CSBS} unblinding computes only an addition of two scalars $s + \alpha \mod p$, while RSA computes $s * r^{-1}$.
To conclude, \gls{CSBS} are faster than RSA 1024 bit and provide a better level of security.
This can be especially useful for wallets running on devices with less CPU power.
The verification on RSA 1024 is faster than \gls{CSBS}.
Therefore, it has to be further investigated which algorithm would overall perform better for the exchange or merchants.
While RSA 1024 bit can compete in certain operations, \gls{CSBS} provide a better level of security and are still faster in most operations.
\begin{table}[h]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.204 ms \\\hline
RSA 1024 bit & 10x key generation & 126 ms \\\hline
\hline
CS & 10x derive $R_0, R_1$ \& blinding & 3.870 ms \\\hline
RSA 1024 bit & 10x blinding & 1.282 ms \\\hline
\hline
CS & 10x signing & 0.077 ms \\\hline
RSA 1024 bit & 10x signing & 7 ms \\\hline
\hline
CS & 10x unblinding & 0.001 ms \\\hline
RSA 1024 bit & 10x unblinding & 2.991 ms \\\hline
\hline
CS & 10x verifying & 1.358 ms \\\hline
RSA 1024 bit & 10x verifying & 0.876 ms \\\hline
\end{tabular}
\caption{Comparison on CS vs RSA 1024}
\label{tab:comp-cs-vs-rsa-1024}
\end{table}
\subsection{Disk Space}
\begin{bfhWarnBox}
These are theoretical calculations, implementations may choose to persist additional values.
\end{bfhWarnBox}
\gls{CSBS} save disk space due to the much smaller key sizes.
Even more disk space is saved by deriving values with the \gls{hkdf}, these values do not have to be stored.
\\
Table \ref{tab:comp-sign-space} shows the disk space comparison of signatures, the private keys alone need even less space with 256 bits per key.
\\
The wallet saves a lot of disk space by deriving most of the values.
In the \gls{CSBS} case a wallet must at least persist the private key $c_s$, $R_0, R_1, s', D_p$, each being 256 bits (32 bytes).
A wallet needs to persist 150 bytes per coin in total.
In the RSA Blind Signature case the wallet persists $c_s$, $b$, $\sigma_c$, $D_p$.
\\Note: for refreshed coins an additional 32 byte value is persisted as seed.\\
$c_s$ is still a 32 byte value in the RSA case, the other values depend on the RSA key size. (32 byte + 3 * \textit{rsa\_keysize}).
The disk space comparison for a wallet can be found in \ref{tab:comp-wallet-space}.
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lccr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Disk Space} & \textbf{Factor} & \textbf{Disk Space 1M signatures}\\\hline
CS & 512 bits & 1x & 64 MB\\\hline
RSA 1024 bit & 1024 bits & 2x & 128 MB \\\hline
RSA 2048 bit & 2048 bits & 4x & 256 MB\\\hline
RSA 3072 bit & 3072 bits & 6x & 384 MB\\\hline
RSA 4096 bit & 4096 bits & 8x & 512 MB\\\hline
\end{tabular}
\caption{Comparison disk space signatures}
\label{tab:comp-sign-space}
\end{table}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lccr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Disk Space} & \textbf{Factor} & \textbf{Disk Space 1M coins}\\\hline
CS 256 bits & 150 bytes & 1x & 150 MB\\\hline
RSA 1024 bit & 416 bytes & 2.7x & 416 MB \\\hline
RSA 2048 bit & 800 bits & 5.3x & 800 MB\\\hline
RSA 3072 bit & 1184 bits & 7.9x & 1184 MB\\\hline
RSA 4096 bit & 1568 bits & 10.4x & 1568 MB\\\hline
\end{tabular}
\caption{Comparison disk space wallet}
\label{tab:comp-wallet-space}
\end{table}
\subsection{Bandwidth}
\begin{bfhWarnBox}
These are theoretical calculations, implementations may choose to persist additional values.
\end{bfhWarnBox}
The reasons that \gls{CSBS} use less bandwidth is mostly because the signature/key sizes are much smaller.
The bandwith improvements for the \texttt{/keys} API is the same as specified in the table with disk space comparison \ref{tab:comp-sign-space}.
For \gls{CSBS} many calculations are performed twice, therefore also two values are submitted.
Table \ref{tab:comp-band-withd} compares the bandwidth used in a withdrawal.
The 32 byte values $2 * n_w, 2 * D_p, R_0, R_1, s,W_p, c_0, c_1, \sigma_W$ as well as an integer $b$ are transmitted for \gls{CSBS}.\\
For RSA, the values $D_p, m', \sigma'_c$ have the same size as the key size.
Additionally, the 32 byte values $W_p, \sigma_W$ are transmitted.
\\\\
In the refresh protocol the only difference is an additional hash ($h_{C_0}, h_{C_1}$ instead of only $h_C$) sent in the commit phase.
Depending on the hash size another 32 byte (or 64 byte) value is transmitted.
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lccr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Bandwith used} & \textbf{Factor} & \textbf{1M coins}\\\hline
CS 256 bits & 356 bytes & 1x & 324 MB\\\hline
RSA 1024 bit & 448 bytes & 1.3x & 448 MB \\\hline
RSA 2048 bit & 832 bits & 2.5x & 832 MB\\\hline
RSA 3072 bit & 1216 bits & 3.75x & 1216 MB\\\hline
RSA 4096 bit & 1600 bits & 4.9x & 1600 MB\\\hline
\end{tabular}
\caption{Bandwith comparison withdrawal}
\label{tab:comp-band-withd}
\end{table}
\subsection{Latency}
This section the notion of \acl{RTT} (see \cite{geeks:rtt}) is used.
There are many factors that influence the measurement of a \acl{RTT}.
Following factors can bring huge changes in the value of \ac{RTT}s.
\begin{itemize}
\item Distance
\item Transmission medium
\item Network hops
\item Traffic levels
\item Server response time
\end{itemize}
All of these factors will vary in reality and are independent of the scheme.\\
The important comparison here is the number of \ac{RT}s as in table \ref{tab:comp-rtt}.
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lc}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Number of RTs} \\\hline
RSA Blind Signatures & 1\\\hline
Clause Blind Schnorr Signatures & 2\\\hline
\end{tabular}
\caption{Comparison of Round-Trips}
\label{tab:comp-rtt}
\end{table}
While creating \gls{RSABS} have one \ac{RT}, \gls{CSBS} need an additional \ac{RT} for requesting the public $R_0, R_1$.
This means that the time spend for withdrawing is almost \textbf{doubled} (the $ R $ request doesn't have any persistence and therefore requires less time) in comparison to RSA.
A coin should not be spent immediately after withdrawal or refresh.
Otherwise, an adversary could deanonymize a customer by correlating the timestamps.
The additional \ac{RT} is a drawback of \gls{CSBS} compared to RSA, but negligible due to the fact that a coin must not be spent immediately.
\section{Security Assumptions}
\label{sec:disc-sec-assumptions}
This section discusses the differences regarding the security assumptions of the schemes.
This section should not explain nor analyze the security assumptions, instead the section focuses on explaining what these assumptions mean and what should be kept in mind about them.
Read section \ref{sec:sign-schemes} and it's references for more information on the assumptions.
RSA's security assumptions are well known since quite a long time and a lot of research is done.
Despite being a lot of attacks \cite{ganapati:rsactftool} \cite{perez:stoprsa}, RSA is still considered a secure scheme after decades.\\
For Schnorr Signatures the \acl{DLP} (see \autoref{sec:dlp}) needs to be hard.
Also the \ac{DLP} is well-known and is being researched since decades.\\
However, with Blind Schorr Signatures an additional assumption needs to hold; the \ac{ROS} problem.
Compared to the other assumptions, \ac{ROS} is relatively new and still a recent research topic.
A recent paper from 2020 on the (in)security of ROS \cite{cryptoeprint:2020:945} broke many schemes relying on ROS being hard, including Schnorr Blind signatures.
The paper on which we rely on (updated in 2021) with the Clause Blind Schnorr Signature Scheme \cite{cryptoeprint:2019:877} is considered secure at the time of writing.\\
\section{Risk}
\label{sec:disc-risk}
As introduced in \autoref{sec:disc-sec-assumptions}, \gls{CSBS} rely on an additional assumption currently being researched.
Compared to other schemes, the chosen \gls{CSBS} are very new (published in 2019, updated in 2021).
While every scheme could potentially be broken, older ones already went through a lot of research and their assumptions are well-known.
Therefore, the risk that a vulnerability in \gls{CSBS} will be discovered is probably higher than a newly discovered vulnerability breaking RSA.
Unpredictability of $ r $ is a key aspect of the signature creation process of \gls{CSBS}.
The redesigned Taler protocols solve this by persisting the nonce and denomination key (described in \autoref{sec:withdraw-protocol-impl}) and checking for reuse of this combination before signature creation.
If this process is malfunctioning (broken implementation, faulty database) or can be circumvented in any way, recovery of a denomination private key is possible.
An exchange operator can still consider using \gls{CSBS} as denomination scheme, as there are multiple benefits (see \autoref{sec:disc-perf-comp}).
The financial loss in the worst case can be calculated and capped by the validity of a denomination key.
If a vulnerability in the \gls{CSBS} would be detected, an exchange operator could revoke the corresponding denomination keys and change the scheme to \gls{RSABS}.
The wallets can then follow the refund protocol to get the money back.
\section{Comparison Conclusion}
\label{sec:disc-comp-conclusion}
A detailed comparison of the two blind signature schemes was made.
This last section interprets the results and concludes the comparison.
\gls{CSBS} on \gls{25519} provide the same security level as \gls{RSABS} with 3072 bit key sizes.
The implementation of \gls{CSBS} is the clear winner in all performance comparisons with RSA 3072 bits.
1024 bit RSA is faster than the \gls{CSBS} implementation in certain operations.
The \gls{CSBS} implementation still offers better performance for wallets with less CPU power and provides a much higher level of security (comparable to RSA 3072).
As further comparisons show, RSA scales very bad the larger the keys get and \gls{CSBS} performs much better overall.
As discussed in the risk section \ref{sec:disc-risk}, \gls{CSBS} have an additional security assumption, which is still a recent research topic.
\gls{CSBS} provide various benefits and the risk can be calculated and capped.
An exchange operator who is aware of the discussed risk can use \gls{CSBS} safely.
\gls{CSBS} are best suited for denominations with low value, where many coins are being withdrawn/refreshed.

View File

@ -0,0 +1,70 @@
\chapter{Conclusion}
This section provides a summary of this work, presents the results and gives an outlook on future work.
\section{Summary}
In the beginning of the project good knowledge on the current state in research about Blind Schnorr signatures was needed.
Therefore, various papers were read and then the paper "Blind Schnorr Signatures and Signed ElGamal Encryption in the Algebraic Group Model" \cite{cryptoeprint:2019:877} was chosen as basis for the redesign of the Taler protocols.\\
The next step was to analyze the current Taler protocols and understand the required properties including \textit{\gls{abort-idempotency}}.\\
With the gathered knowledge (see chapter \ref{chap:preliminaries}) the Taler protocols were redesigned to support \gls{CSBS} (see chapter \ref{chap:design}).
These redesigned protocols were then further specified (chapter \ref{chap:spec}) and then implemented (chapter \ref{chap:implement}).
The implementation includes the main protocols, key management, cryptographic utilities in Taler and the \gls{CSBS} cryptographic routines.\\
The \gls{CSBS} scheme was analyzed and compared in detail to the RSA Blind Signature scheme (see \ref{chap:disc}).
\section{Results}
The thesis provides several results to add support for Schnorr's blind signature in Taler, including:
\begin{itemize}
\item Redesigned Taler protocols to support \gls{CSBS}
\item Implementation of cryptographic routines
\item Implementation of Taler protocols in Exchange
\begin{itemize}
\item Key Management and security module
\item Cryptographic utilities
\item Withdraw protocol
\item Deposit protocol
\end{itemize}
\item Comparison and Analysis
\begin{itemize}
\item Performance (speed, space, latency \& bandwith)
\item Security
\item Scheme Comparison
\end{itemize}
\item Fixing a minor security issue in Taler's current protocols
\end{itemize}
The code is tested, and those tests are integrated in the existing testing framework.
Benchmarks are added for the cryptographic routines and the security module.
\section{Future Work}
Like in any other project, there is always more that could be done.
This section provides an outlook on what can be done in future work.
\begin{itemize}
\item Implement wallet
\item Implementing remaining \gls{CSBS} protocols (refresh, tipping protocol, refund etc.)
\item Implementing merchant
\item Security audit of CS implementation
\item Find a solution for withdraw loophole
\item Evaluating \& implementing \gls{CSBS} on other curves
\end{itemize}
There are some remaining protocols to implement, which were out of scope for this thesis.
To run \gls{CSBS} in production, these protocols have to be implemented too.
Further, the merchant needs to support \gls{CSBS} too.
The merchant implementation can be done fast, as the merchant only verifies denomination signatures in most cases. \\
Currently, the exchange runs both security modules, the \gls{CSBS} and the RSA security modules.
To reduce unnecessary overhead, this should be changed so that only one security has to be running.
To run \gls{CSBS} in production a security audit from an external company is recommended (as done for other parts in the exchange, see \cite{codeblau:taler-audit}).
A security audit should always be made when implementing big changes like these.\\
As mentioned in the scope section, the optional goal to find and implement a good solution for the withdraw loophole was dropped.
This was due to the scope shift and because the analysis of the problem showed that finding a good solution needs more research and is a whole project in itself (see \ref{sec:scope} for more information).\\
Furthermore, \gls{CSBS} could be implemented on other curves.
For example Curve448 \cite{cryptoeprint:2015:625} could be used, as it provides 224 bits of security, wheras \gls{25519} \cite{bern:curve25519} provides about 128 bits of security.
Curve secp256k1 could further improve \gls{CSBS} performance.
While providing support for Curve448 should not be problematic, a potential implementation for secp256k1 needs further analysis (see \cite{bernlange:safecurves} and \cite{bip:schnorr-bitc} for more information).
\section{Personal Conclusion}
This thesis includes understanding, analyzing, integrating and implementing a recent academic paper \cite{cryptoeprint:2019:877} containing a modern cryptographic scheme.
Furthermore, the implementation is done in Taler, an intuitive and modern solution for a social responsible payment system with high ethical standards.
Although there was a lot of work, we enjoyed working on such a modern and very interesting topic.
Especially the first successful signature verification and the signature scheme performance benchmarks motivated us to push the implementation and integration into Taler forward.\\
We are happy to provide an implementation of a modern scheme and making it available as free software.

677
doc/cs/content/appendix.tex Normal file
View File

@ -0,0 +1,677 @@
\appendix
\chapter{Installation}
These installation instructions are meant to run the code developed within this thesis for development- and review-purposes.
For a comprehensive installation instruction follow the Taler documentation
\cite{taler-documentation}.
\begin{bfhNoteBox}
These instructions are used and tested on Ubuntu 21.10.
\end{bfhNoteBox}
\section{Dependencies and Setup}
The following dependencies need to be installed for GNUnet and Taler Exchange:
\setupLinuxPrompt{student}
\begin{ubuntu}
sudo apt update
sudo apt install git curl build-essential gcc automake make \ texinfo autoconf uncrustify libtool pkgconf gettext gnutls-bin \ libcurl4-gnutls-dev libgcrypt20-dev libidn2-dev libjansson-dev \ libnss3-dev sqlite pipenv libltdl-dev libsodium-dev libpq-dev \ autopoint libunistring-dev libextractor-dev libpng-dev \ libpulse-dev libsqlite3-dev recutils python3-jinja2 sqlite yapf3 \ postgresql libpq-dev wget libmicrohttpd-dev
export LD\_LIBRARY\_PATH=/usr/local/lib
\end{ubuntu}
\begin{bfhBox}[BFH-MediumBlue]{Install in a container}
The installation can also be done in a docker or podman container with the ubuntu:21.10 image:
\setupLinuxPrompt{student}
\begin{ubuntu}
podman run -it --name talertest ubuntu:21.10
\end{ubuntu}
\end{bfhBox}
\section{Install GNUnet Core}
GNUnet core is both a dependency of the Taler exchange and where we implemented the Clause Blind Schnorr Signature Scheme.
\setupLinuxPrompt{student}
\begin{ubuntu}
git clone https://git.gnunet.org/gnunet.git
cd gnunet
./bootstrap
./configure --enable-benchmarks --prefix=/usr/local
make
make install
make check # Run optionally to verify installation and run tests
\end{ubuntu}
To run benchmarks run:
\setupLinuxPrompt{student}
\begin{ubuntu}
./src/util/perf_crypto_cs
./src/util/perf_crypto_rsa
\end{ubuntu}
\section{Install Taler Exchange}
\begin{bfhWarnBox}
Ensure that the current user has privileges in postgresql.
One possible way to do this is:\\
(where [user] has to be replaced with the name of the system user running the tests)
\setupLinuxPrompt{student}
\begin{ubuntu}
service postgresql start
sudo su
su - postgres
psql
CREATE ROLE [user] LOGIN SUPERUSER;
CREATE DATABASE [user] OWNER [user];
exit
\end{ubuntu}
\end{bfhWarnBox}
The Taler exchange can be installed as followed:
\setupLinuxPrompt{student}
\begin{ubuntu}
service postgresql start
createdb talercheck
git clone https://git.taler.net/exchange.git
cd exchange
./bootstrap
./configure --with-gnunet=/usr/local --prefix=/usr/local
./make
./make install
./make check # Run optionally to verify installation and run tests
\end{ubuntu}
To execute the security module benchmarks run:
\setupLinuxPrompt{student}
\begin{ubuntu}
cd src/util
./test_helper_cs
./test_helper_rsa
\end{ubuntu}
\chapter{Performance Measurements}
\label{chap:app-perf}
\section{AMD Ryzen 7 PRO 5850U (Notebook)}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-amd-ryzen-7}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 8-core AMD Ryzen 7 PRO 5850U \\
Architecture: amd64 \\
OS: Ubuntu 21.10 Linux 5.13.0-25-generic \#26-Ubuntu SMP Fri Jan 7 15:48:31 UTC 2022 x86\_64 x86\_64 x86\_64 GNU/Linux \\
libsodium:amd64 version: 1.0.18-1build1 \\
libgcrypt:amd64 version: 1.8.7-5ubuntu2
\end{bfhBox}
\begin{table}[h]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.204 ms \\\hline
RSA 1024 bit & 10x key generation & 126 ms \\\hline
RSA 2048 bit & 10x key generation & 903 ms \\\hline
RSA 3072 bit & 10x key generation & 2684 ms \\\hline
RSA 4096 bit & 10x key generation & 10 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 0.444 ms \\\hline
CS & 10x derivation of blinding secrets & 0.094 ms \\\hline
CS & 10x blinding & 3.332 ms \\\hline
RSA 1024 bit & 10x blinding & 1.282 ms \\\hline
RSA 2048 bit & 10x blinding & 3.012 ms \\\hline
RSA 3072 bit & 10x blinding & 5 ms \\\hline
RSA 4096 bit & 10x blinding & 9 ms \\\hline
\hline
CS & 10x signing & 0.077 ms \\\hline
RSA 1024 bit & 10x signing & 7 ms \\\hline
RSA 2048 bit & 10x signing & 34 ms \\\hline
RSA 3072 bit & 10x signing & 86 ms \\\hline
RSA 4096 bit & 10x signing & 183 ms \\\hline
\hline
CS & 10x unblinding & 0.001 ms \\\hline
RSA 1024 bit & 10x unblinding & 2.991 ms \\\hline
RSA 2048 bit & 10x unblinding & 10 ms \\\hline
RSA 3072 bit & 10x unblinding & 24 ms \\\hline
RSA 4096 bit & 10x unblinding & 44 ms \\\hline
\hline
CS & 10x verifying & 1.358 ms \\\hline
RSA 1024 bit & 10x verifying & 0.876 ms \\\hline
RSA 2048 bit & 10x verifying & 1.836 ms \\\hline
RSA 3072 bit & 10x verifying & 3.075 ms \\\hline
RSA 4096 bit & 10x verifying & 5 ms \\\hline
\end{tabular}
\caption{Comparison on AMD Ryzen 7}
\label{tab:comp-sign-amd-ryzen-7}
\end{table}
\section{Intel(R) Core(TM) i7-8565U}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-intel-i7}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 8-core Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz \\
Architecture: amd64 \\
OS: Ubuntu 21.10 Linux 5.13.0-25-generic \#26-Ubuntu SMP Fri Jan 7 15:48:31 UTC 2022 x86\_64 x86\_64 x86\_64 GNU/Linux \\
libsodium:amd64 version: 1.0.18-1build1 \\
libgcrypt:amd64 version: 1.8.7-5ubuntu2
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 1.05 ms \\\hline
RSA 1024 bit & 10x key generation & 189 ms \\\hline
RSA 2048 bit & 10x key generation & 1555 ms \\\hline
RSA 3072 bit & 10x key generation & 5000 ms \\\hline
RSA 4096 bit & 10x key generation & 11 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 2.261 ms \\\hline
CS & 10x derivation of blinding secrets & 0.521 ms \\\hline
CS & 10x blinding & 13 ms \\\hline
RSA 1024 bit & 10x blinding & 2.6 ms \\\hline
RSA 2048 bit & 10x blinding & 4.12 ms \\\hline
RSA 3072 bit & 10x blinding & 7 ms \\\hline
RSA 4096 bit & 10x blinding & 11 ms \\\hline
\hline
CS & 10x signing & 0.405 ms \\\hline
RSA 1024 bit & 10x signing & 9 ms \\\hline
RSA 2048 bit & 10x signing & 44 ms \\\hline
RSA 3072 bit & 10x signing & 108 ms \\\hline
RSA 4096 bit & 10x signing & 216 ms \\\hline
\hline
CS & 10x unblinding & 0.005 ms \\\hline
RSA 1024 bit & 10x unblinding & 3.353 ms \\\hline
RSA 2048 bit & 10x unblinding & 12 ms \\\hline
RSA 3072 bit & 10x unblinding & 27 ms \\\hline
RSA 4096 bit & 10x unblinding & 47 ms \\\hline
\hline
CS & 10x verifying & 4.413 ms \\\hline
RSA 1024 bit & 10x verifying & 1.202 ms \\\hline
RSA 2048 bit & 10x verifying & 2.304 ms \\\hline
RSA 3072 bit & 10x verifying & 4.094 ms \\\hline
RSA 4096 bit & 10x verifying & 6 ms \\\hline
\end{tabular}
\caption{Comparison on Intel(R) Core(TM) i7-8565U}
\label{tab:comp-sign-intel-i7}
\end{table}
\section{AMD Ryzen Threadripper 1950X 16-Core Processor}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-amd-threadripper}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: AMD Ryzen Threadripper 1950X 16-Core Processor \\
Architecture: amd64 \\
OS: Linux 5.13.0-trunk-amd64 \#1 SMP Debian 5.13.12-1~exp1
(2021-08-20) x86\_64 GNU/Linux \\
libsodium:amd64 version: 1.9.4-5 \\
libgcrypt:amd64 version: 1.0.18-1
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.442 ms \\\hline
RSA 1024 bit & 10x key generation & 145 ms \\\hline
RSA 2048 bit & 10x key generation & 1167 ms \\\hline
RSA 3072 bit & 10x key generation & 6000 ms \\\hline
RSA 4096 bit & 10x key generation & 11 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 1.043 ms \\\hline
CS & 10x derivation of blinding secrets & 0.242 ms \\\hline
CS & 10x blinding & 7 ms \\\hline
RSA 1024 bit & 10x blinding & 2.258 ms \\\hline
RSA 2048 bit & 10x blinding & 4.744 ms \\\hline
RSA 3072 bit & 10x blinding & 9 ms \\\hline
RSA 4096 bit & 10x blinding & 14 ms \\\hline
\hline
CS & 10x signing & 0.270 ms \\\hline
RSA 1024 bit & 10x signing & 10 ms \\\hline
RSA 2048 bit & 10x signing & 47 ms \\\hline
RSA 3072 bit & 10x signing & 119 ms \\\hline
RSA 4096 bit & 10x signing & 248 ms \\\hline
\hline
CS & 10x unblinding & 0.003 ms \\\hline
RSA 1024 bit & 10x unblinding & 4.086 ms \\\hline
RSA 2048 bit & 10x unblinding & 14 ms \\\hline
RSA 3072 bit & 10x unblinding & 34 ms \\\hline
RSA 4096 bit & 10x unblinding & 60 ms \\\hline
\hline
CS & 10x verifying & 2.392 ms \\\hline
RSA 1024 bit & 10x verifying & 1.137 ms \\\hline
RSA 2048 bit & 10x verifying & 2.797 ms \\\hline
RSA 3072 bit & 10x verifying & 5 ms \\\hline
RSA 4096 bit & 10x verifying & 7 ms \\\hline
\end{tabular}
\caption{Comparison on AMD Ryzen Threadripper 1950X}
\label{tab:comp-sign-amd-threadripper}
\end{table}
\section{Intel(R) Xeon(R) CPU E5-2630}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-intel-xeon}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz \\
Architecture: amd64 \\
OS: Linux 5.10.0-8-amd64 \#1 SMP Debian 5.10.46-4 (2021-08-03) x86\_64\\
libsodium:amd64 version: 1.0.18-1\\
libgcrypt:amd64 version: 1.8.7-6
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.606 ms \\\hline
RSA 1024 bit & 10x key generation & 329 ms \\\hline
RSA 2048 bit & 10x key generation & 3210 ms \\\hline
RSA 3072 bit & 10x key generation & 12 000 ms \\\hline
RSA 4096 bit & 10x key generation & 40 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 1.527 ms \\\hline
CS & 10x derivation of blinding secrets & 0.329 ms \\\hline
CS & 10x blinding & 9 ms \\\hline
RSA 1024 bit & 10x blinding & 4.026 ms \\\hline
RSA 2048 bit & 10x blinding & 9 ms \\\hline
RSA 3072 bit & 10x blinding & 18 ms \\\hline
RSA 4096 bit & 10x blinding & 27 ms \\\hline
\hline
CS & 10x signing & 0.274 ms \\\hline
RSA 1024 bit & 10x signing & 21 ms \\\hline
RSA 2048 bit & 10x signing & 96 ms \\\hline
RSA 3072 bit & 10x signing & 237 ms \\\hline
RSA 4096 bit & 10x signing & 482 ms \\\hline
\hline
CS & 10x unblinding & 0.004 ms \\\hline
RSA 1024 bit & 10x unblinding & 7 ms \\\hline
RSA 2048 bit & 10x unblinding & 25 ms \\\hline
RSA 3072 bit & 10x unblinding & 58 ms \\\hline
RSA 4096 bit & 10x unblinding & 99 ms \\\hline
\hline
CS & 10x verifying & 4.334 ms \\\hline
RSA 1024 bit & 10x verifying & 2.190 ms \\\hline
RSA 2048 bit & 10x verifying & 5 ms \\\hline
RSA 3072 bit & 10x verifying & 11 ms \\\hline
RSA 4096 bit & 10x verifying & 14 ms \\\hline
\end{tabular}
\caption{Comparison on Intel(R) Xeon(R) CPU E5-2630}
\label{tab:comp-sign-intel-xeon}
\end{table}
\section{Intel(R) Pentium(R) 3558U}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-intel-pentium}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: Intel(R) Pentium(R) 3558U @ 1.70GHz \\
Architecture: amd64 \\
OS: Linux 5.10.0-8-amd64 \#1 SMP Debian 5.10.46-3 (2021-07-28) x86\_64\\
libsodium:amd64 version: 1.0.18-1\\
libgcrypt:amd64 version: 1.8.7-6
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.53 ms \\\hline
RSA 1024 bit & 10x key generation & 524 ms \\\hline
RSA 2048 bit & 10x key generation & 3357 ms \\\hline
RSA 3072 bit & 10x key generation & 15 000 ms \\\hline
RSA 4096 bit & 10x key generation & 37 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 1.375 ms \\\hline
CS & 10x derivation of blinding secrets & 0.349 ms \\\hline
CS & 10x blinding & 8 ms \\\hline
RSA 1024 bit & 10x blinding & 4.86 ms \\\hline
RSA 2048 bit & 10x blinding & 11 ms \\\hline
RSA 3072 bit & 10x blinding & 19 ms \\\hline
RSA 4096 bit & 10x blinding & 31 ms \\\hline
\hline
CS & 10x signing & 0.283 ms \\\hline
RSA 1024 bit & 10x signing & 26 ms \\\hline
RSA 2048 bit & 10x signing & 117 ms \\\hline
RSA 3072 bit & 10x signing & 292 ms \\\hline
RSA 4096 bit & 10x signing & 571 ms \\\hline
\hline
CS & 10x unblinding & 0.003 ms \\\hline
RSA 1024 bit & 10x unblinding & 8 ms \\\hline
RSA 2048 bit & 10x unblinding & 30 ms \\\hline
RSA 3072 bit & 10x unblinding & 67 ms \\\hline
RSA 4096 bit & 10x unblinding & 111 ms \\\hline
\hline
CS & 10x verifying & 3.769 ms \\\hline
RSA 1024 bit & 10x verifying & 2.616 ms \\\hline
RSA 2048 bit & 10x verifying & 6 ms \\\hline
RSA 3072 bit & 10x verifying & 11 ms \\\hline
RSA 4096 bit & 10x verifying & 17 ms \\\hline
\end{tabular}
\caption{Comparison on Intel(R) Pentium(R) 3558U}
\label{tab:comp-sign-intel-pentium}
\end{table}
\section{arm64}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-arm64}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 8-core arm64\\
Architecture: ARM64 \\
OS: Linux ten64 5.11.0-31-generic \#33+testsfp1 SMP Mon Aug 23 16:07:41 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux \\
libsodium:arm64 version: 1.8.7-2ubuntu2.1 \\
libgcrypt:arm64 version: 1.0.18-1
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 2.896 ms \\\hline
RSA 1024 bit & 10x key generation & 839 ms \\\hline
RSA 2048 bit & 10x key generation & 8000 ms \\\hline
RSA 3072 bit & 10x key generation & 17 000 ms \\\hline
RSA 4096 bit & 10x key generation & 82 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 6 ms \\\hline
CS & 10x derivation of blinding secrets & 0.713 ms \\\hline
CS & 10x blinding & 23 ms \\\hline
RSA 1024 bit & 10x blinding & 11 ms \\\hline
RSA 2048 bit & 10x blinding & 28 ms \\\hline
RSA 3072 bit & 10x blinding & 51 ms \\\hline
RSA 4096 bit & 10x blinding & 81 ms \\\hline
\hline
CS & 10x signing & 0.321 ms \\\hline
RSA 1024 bit & 10x signing & 57 ms \\\hline
RSA 2048 bit & 10x signing & 263 ms \\\hline
RSA 3072 bit & 10x signing & 685 ms \\\hline
RSA 4096 bit & 10x signing & 1385 ms \\\hline
\hline
CS & 10x unblinding & 0.006 ms \\\hline
RSA 1024 bit & 10x unblinding & 23 ms \\\hline
RSA 2048 bit & 10x unblinding & 79 ms \\\hline
RSA 3072 bit & 10x unblinding & 171 ms \\\hline
RSA 4096 bit & 10x unblinding & 296 ms \\\hline
\hline
CS & 10x verifying & 11ms \\\hline
RSA 1024 bit & 10x verifying & 5 ms \\\hline
RSA 2048 bit & 10x verifying & 15 ms \\\hline
RSA 3072 bit & 10x verifying & 27 ms \\\hline
RSA 4096 bit & 10x verifying & 45 ms \\\hline
\end{tabular}
\caption{Comparison on arm64}
\label{tab:comp-sign-arm64}
\end{table}
\section{AMD Ryzen Embedded R1606G}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-amd-embedded}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 4-core AMD Ryzen Embedded R1606G with Radeon Vega Gfx\\
Architecture: amd64 \\
OS: Linux computer 5.13.0-25-generic \#26-Ubuntu SMP Fri Jan 7 15:48:31 UTC 2022 x86\_64 x86\_64 x86\_64 GNU/Linux\\
libsodium:amd64 version: 1.8.7-5ubuntu2 \\
libgcrypt:amd64 version: 1.0.18-1build1
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 2.373 ms \\\hline
RSA 1024 bit & 10x key generation & 184 ms \\\hline
RSA 2048 bit & 10x key generation & 2132 ms \\\hline
RSA 3072 bit & 10x key generation & 8000 ms \\\hline
RSA 4096 bit & 10x key generation & 21 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 1.09 ms \\\hline
CS & 10x derivation of blinding secrets & 0.43 ms \\\hline
CS & 10x blinding & 6 ms \\\hline
RSA 1024 bit & 10x blinding & 3.886 ms \\\hline
RSA 2048 bit & 10x blinding & 7 ms \\\hline
RSA 3072 bit & 10x blinding & 14 ms \\\hline
RSA 4096 bit & 10x blinding & 23 ms \\\hline
\hline
CS & 10x signing & 0.379 ms \\\hline
RSA 1024 bit & 10x signing & 15 ms \\\hline
RSA 2048 bit & 10x signing & 71 ms \\\hline
RSA 3072 bit & 10x signing & 177 ms \\\hline
RSA 4096 bit & 10x signing & 357 ms \\\hline
\hline
CS & 10x unblinding & 0.001 ms \\\hline
RSA 1024 bit & 10x unblinding & 6 ms \\\hline
RSA 2048 bit & 10x unblinding & 24 ms \\\hline
RSA 3072 bit & 10x unblinding & 53 ms \\\hline
RSA 4096 bit & 10x unblinding & 93 ms \\\hline
\hline
CS & 10x verifying & 2.610 ms \\\hline
RSA 1024 bit & 10x verifying & 2.303 ms \\\hline
RSA 2048 bit & 10x verifying & 4.386 ms \\\hline
RSA 3072 bit & 10x verifying & 7 ms \\\hline
RSA 4096 bit & 10x verifying & 11 ms \\\hline
\end{tabular}
\caption{Comparison on AMD Ryzen Embedded R1606G}
\label{tab:comp-sign-amd-embedded}
\end{table}
\section{risc64}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-risc64}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 4-core risc64 processor\\
OS: Linux risc-v-unleashed-000 5.11.0-1022-generic \#23~20.04.1-Ubuntu SMP Thu Oct 21 10:16:27 UTC 2021 riscv64 riscv64 riscv64 GNU/Linux\\
libsodium:riscv64 version: 1.8.7-5ubuntu2 \\
libgcrypt:riscv64 version: 1.0.18-1build1
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 4.144 ms \\\hline
RSA 1024 bit & 10x key generation & 2923 ms \\\hline
RSA 2048 bit & 10x key generation & 28 000 ms \\\hline
RSA 3072 bit & 10x key generation & 174 000 ms \\\hline
RSA 4096 bit & 10x key generation & 600 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 10 ms \\\hline
CS & 10x derivation of blinding secrets & 2.514 ms \\\hline
CS & 10x blinding & 72 ms \\\hline
RSA 1024 bit & 10x blinding & 37 ms \\\hline
RSA 2048 bit & 10x blinding & 93 ms \\\hline
RSA 3072 bit & 10x blinding & 170 ms \\\hline
RSA 4096 bit & 10x blinding & 277 ms \\\hline
\hline
CS & 10x signing & 1.697 ms \\\hline
RSA 1024 bit & 10x signing & 215 ms \\\hline
RSA 2048 bit & 10x signing & 1040 ms \\\hline
RSA 3072 bit & 10x signing & 2883 ms \\\hline
RSA 4096 bit & 10x signing & 5000 ms \\\hline
\hline
CS & 10x unblinding & 0.022 ms \\\hline
RSA 1024 bit & 10x unblinding & 62 ms \\\hline
RSA 2048 bit & 10x unblinding & 150 ms \\\hline
RSA 3072 bit & 10x unblinding & 275 ms \\\hline
RSA 4096 bit & 10x unblinding & 431 ms \\\hline
\hline
CS & 10x verifying & 29 ms \\\hline
RSA 1024 bit & 10x verifying & 22 ms \\\hline
RSA 2048 bit & 10x verifying & 54 ms \\\hline
RSA 3072 bit & 10x verifying & 99 ms \\\hline
RSA 4096 bit & 10x verifying & 166 ms \\\hline
\end{tabular}
\caption{Comparison on risc64}
\label{tab:comp-sign-risc64}
\end{table}
\section{POWER9}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-POWER9}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 176-core power9\\
architecture: pp64le \\
OS: Linux power9 5.11.0-34-generic \#36-Ubuntu SMP Thu Aug 26 19:19:54 UTC 2021 ppc64le ppc64le ppc64le GNU/Linux \\
libsodium:a::ppc64el version: 1.8.7-2ubuntu2.1 \\
libgcrypt::ppc64el version: 1.0.18-1
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 0.275 ms \\\hline
RSA 1024 bit & 10x key generation & 290 ms \\\hline
RSA 2048 bit & 10x key generation & 3743 ms \\\hline
RSA 3072 bit & 10x key generation & 15 000 ms \\\hline
RSA 4096 bit & 10x key generation & 45 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 0.749 ms \\\hline
CS & 10x derivation of blinding secrets & 0.267 ms \\\hline
CS & 10x blinding & 4.996 ms \\\hline
RSA 1024 bit & 10x blinding & 3.952 ms \\\hline
RSA 2048 bit & 10x blinding & 10 ms \\\hline
RSA 3072 bit & 10x blinding & 17 ms \\\hline
RSA 4096 bit & 10x blinding & 27 ms \\\hline
\hline
CS & 10x signing & 0.221 ms \\\hline
RSA 1024 bit & 10x signing & 25 ms \\\hline
RSA 2048 bit & 10x signing & 135 ms \\\hline
RSA 3072 bit & 10x signing & 381 ms \\\hline
RSA 4096 bit & 10x signing & 762 ms \\\hline
\hline
CS & 10x unblinding & 0.002 ms \\\hline
RSA 1024 bit & 10x unblinding & 9 ms \\\hline
RSA 2048 bit & 10x unblinding & 34 ms \\\hline
RSA 3072 bit & 10x unblinding & 80 ms \\\hline
RSA 4096 bit & 10x unblinding & 141 ms \\\hline
\hline
CS & 10x verifying & 2.458 ms \\\hline
RSA 1024 bit & 10x verifying & 2.365 ms \\\hline
RSA 2048 bit & 10x verifying & 6 ms \\\hline
RSA 3072 bit & 10x verifying & 10 ms \\\hline
RSA 4096 bit & 10x verifying & 16 ms \\\hline
\end{tabular}
\caption{Comparison on POWER9}
\label{tab:comp-sign-POWER9}
\end{table}
\section{ARMv7 Processor}
Detailed comparison of each operation can be found in table \ref{tab:comp-sign-armv7}.
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 8-core ARMv7 Processor rev 3 (v7l)
Architecture: armv7 \\
OS: Linux odroidxu4 4.14.150-odroidxu4 \#2 SMP PREEMPT Mon Oct 28 08:07:45 CET 2019 armv7l GNU/Linux\\
libsodium:armhf version: 1.9.4-5 \\
libgcrypt:armhf version: 1.0.18-1
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{llr}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Operation} & \textbf{Speed} \\\hline
CS & 10x key generation & 1.719 ms \\\hline
RSA 1024 bit & 10x key generation & 1050 ms \\\hline
RSA 2048 bit & 10x key generation & 8000 ms \\\hline
RSA 3072 bit & 10x key generation & 53 000 ms \\\hline
RSA 4096 bit & 10x key generation & 159 000 ms \\\hline
\hline
CS & 10x r0, r1 derive and R1,R2 calculation & 3.621 ms \\\hline
CS & 10x derivation of blinding secrets & 0.514 ms \\\hline
CS & 10x blinding & 24 ms \\\hline
RSA 1024 bit & 10x blinding & 10 ms \\\hline
RSA 2048 bit & 10x blinding & 26 ms \\\hline
RSA 3072 bit & 10x blinding & 45 ms \\\hline
RSA 4096 bit & 10x blinding & 78 ms \\\hline
\hline
CS & 10x signing & 0.481 ms \\\hline
RSA 1024 bit & 10x signing & 87 ms \\\hline
RSA 2048 bit & 10x signing & 385 ms \\\hline
RSA 3072 bit & 10x signing & 1038 ms \\\hline
RSA 4096 bit & 10x signing & 2073 ms \\\hline
\hline
CS & 10x unblinding & 0.008 ms \\\hline
RSA 1024 bit & 10x unblinding & 26 ms \\\hline
RSA 2048 bit & 10x unblinding & 90 ms \\\hline
RSA 3072 bit & 10x unblinding & 195 ms \\\hline
RSA 4096 bit & 10x unblinding & 344 ms \\\hline
\hline
CS & 10x verifying & 11 ms \\\hline
RSA 1024 bit & 10x verifying & 5 ms \\\hline
RSA 2048 bit & 10x verifying & 15 ms \\\hline
RSA 3072 bit & 10x verifying & 28 ms \\\hline
RSA 4096 bit & 10x verifying & 42 ms \\\hline
\end{tabular}
\caption{Comparison on ARMv7}
\label{tab:comp-sign-armv7}
\end{table}
\section{Performance of the Security Module}
These performance measurements are only done on one hardware setup.
The performance tests of the cryptographic routines are more meaningful, the architecture of the Taler exchange could change a lot.
Furthermore, there could be made performance improvements at costs of security by doing the operations requiring the private keys directly in the httpd process.
Because of security reasons, the current design with the security module makes a lot of sense.
It has to be kept in mind that the following performance benchmarks are interesting to see, but could vary a lot with changes inside the codebase.
The performance of the signatures with the security module can be found in table \ref{tab:comp-sign-full}
\begin{bfhBox}[BFH-MediumBlue]{Setup}
CPU: 8-core AMD Ryzen 7 PRO 5850U \\
OS: Ubuntu 21.10 Linux 5.13.0-25-generic \#26-Ubuntu SMP Fri Jan 7 15:48:31 UTC 2022 x86\_64 x86\_64 x86\_64 GNU/Linux \\
libsodium version: 1.0.18-1build1 \\
libgcrypt version: 1.8.7-5ubuntu2
\end{bfhBox}
\begin{table}[ht]
\centering
\colorlet{BFH-table}{BFH-MediumBlue!10}
\colorlet{BFH-tablehead}{BFH-MediumBlue!50}
\setupBfhTabular
\begin{tabular}{lll}
\rowcolor{BFH-tablehead}
\textbf{Signature Scheme} & \textbf{Test} & \textbf{Speed} \\\hline
CS & 100 sequential signature operations & 2.591 ms \\\hline
RSA 1024 bit & 100 sequential signature operations & 79 ms \\\hline
RSA 2048 bit & 100 sequential signature operations & 350 ms \\\hline
RSA 3072 bit & 100 sequential signature operations & 893 ms \\\hline
RSA 4092 & 100 sequential signature operations & 1811 ms \\\hline
\hline
CS & 100 parallel signature operations & 14 ms \\\hline
RSA 1024 bit & 100 parallel signature operations & 125 ms \\\hline
RSA 2048 bit & 100 parallel signature operations & 573ms \\\hline
RSA 3072 bit & 100 parallel signature operations & 1420 ms \\\hline
RSA 4092 & 100 parallel signature operations & 3279 ms \\\hline
\hline
CS & 800 parallel signature operations & 19 ms \\\hline
RSA 1024 bit & 800 parallel signature operations & 137 ms \\\hline
RSA 2048 bit & 800 parallel signature operations & 653 ms \\\hline
RSA 3072 bit & 800 parallel signature operations & 1451 ms \\\hline
RSA 4092 & 800 parallel signature operations & 3388 ms \\\hline
\end{tabular}
\caption{Performance comparison of the security module}
\label{tab:comp-sign-full}
\end{table}
\input{content/appendix/rsa-redesign.tex}

View File

@ -0,0 +1,279 @@
\begin{lstlisting}[style=bfh-c,language=C,, caption={Crypto Implementation API}, label={lst:cryptoapi}]
#include <sodium.h>
/**
* IMPLEMENTATION NOTICE:
*
* This is an implementation of the Schnorr, Blind Schnorr and
* Clause Blind Schnorr Signature Scheme using Curve25519.
* We use libsodium wherever possible.
*
* Blind Schnorr: The Blind Schnorr Signature Scheme is BROKEN!
* Use the Clause Blind Schnorr Signature Scheme instead.
*
* Clause Blind Schnorr Signature Scheme:
* This is a variation of the Blind Schnorr Signature Scheme where all operations
* before the signature creation are performed twice.
* The signer randomly chooses one of the two sessions and only creates the signature for this session.
* Note that the Clause part needs to be implemented by whoever uses this API.
* Further details about the Clause Blind Schnorr Signature Scheme can be found here:
* https://eprint.iacr.org/2019/877.pdf
*/
/**
* Curve25519 Scalar
*/
struct GNUNET_CRYPTO_Cs25519Scalar
{
/**
* 32 byte scalar
*/
unsigned char d[crypto_core_ed25519_SCALARBYTES];
};
/**
* Curve25519 point
*/
struct GNUNET_CRYPTO_Cs25519Point
{
/**
* This is a point on the Curve25519.
* The x coordinate can be restored using the y coordinate
*/
unsigned char y[crypto_core_ed25519_BYTES];
};
/**
* The private information of an Schnorr key pair.
*/
struct GNUNET_CRYPTO_CsPrivateKey
{
struct GNUNET_CRYPTO_Cs25519Scalar scalar;
};
/**
* The public information of an Schnorr key pair.
*/
struct GNUNET_CRYPTO_CsPublicKey
{
struct GNUNET_CRYPTO_Cs25519Point point;
};
/**
* Secret used for blinding (alpha and beta).
*/
struct GNUNET_CRYPTO_CsBlindingSecret
{
struct GNUNET_CRYPTO_Cs25519Scalar alpha;
struct GNUNET_CRYPTO_Cs25519Scalar beta;
};
/**
* the private r used in the signature
*/
struct GNUNET_CRYPTO_CsRSecret
{
struct GNUNET_CRYPTO_Cs25519Scalar scalar;
};
/**
* the public R (derived from r) used in c
*/
struct GNUNET_CRYPTO_CsRPublic
{
struct GNUNET_CRYPTO_Cs25519Point point;
};
/**
* Schnorr c to be signed
*/
struct GNUNET_CRYPTO_CsC
{
struct GNUNET_CRYPTO_Cs25519Scalar scalar;
};
/**
* s in the signature
*/
struct GNUNET_CRYPTO_CsS
{
struct GNUNET_CRYPTO_Cs25519Scalar scalar;
};
/**
* blinded s in the signature
*/
struct GNUNET_CRYPTO_CsBlindS
{
struct GNUNET_CRYPTO_Cs25519Scalar scalar;
};
/**
* CS Signtature containing scalar s and point R
*/
struct GNUNET_CRYPTO_CsSignature
{
/**
* Schnorr signatures are composed of a scalar s and a curve point
*/
struct GNUNET_CRYPTO_CsS s_scalar;
struct GNUNET_CRYPTO_Cs25519Point r_point;
};
/**
* Nonce
*/
struct GNUNET_CRYPTO_CsNonce
{
/*a nonce*/
unsigned char nonce[256 / 8];
};
/**
* Create a new random private key.
*
* @param[out] priv where to write the fresh private key
*/
void
GNUNET_CRYPTO_cs_private_key_generate (struct GNUNET_CRYPTO_CsPrivateKey *priv);
/**
* Extract the public key of the given private key.
*
* @param priv the private key
* @param[out] pub where to write the public key
*/
void
GNUNET_CRYPTO_cs_private_key_get_public (const struct GNUNET_CRYPTO_CsPrivateKey *priv,
struct GNUNET_CRYPTO_CsPublicKey *pub);
/**
* Derive a new secret r pair r0 and r1.
* In original papers r is generated randomly
* To provide abort-idempotency, r needs to be derived but still needs to be UNPREDICTABLE
* To ensure unpredictability a new nonce should be used when a new r needs to be derived.
* Uses HKDF internally.
* Comment: Can be done in one HKDF shot and split output.
*
* @param nonce is a random nonce
* @param lts is a long-term-secret in form of a private key
* @param[out] r array containing derived secrets r0 and r1
*/
void
GNUNET_CRYPTO_cs_r_derive (const struct GNUNET_CRYPTO_CsNonce *nonce,
const struct GNUNET_CRYPTO_CsPrivateKey *lts,
struct GNUNET_CRYPTO_CsRSecret r[2]);
/**
* Extract the public R of the given secret r.
*
* @param r_priv the private key
* @param[out] r_pub where to write the public key
*/
void
GNUNET_CRYPTO_cs_r_get_public (const struct GNUNET_CRYPTO_CsRSecret *r_priv,
struct GNUNET_CRYPTO_CsRPublic *r_pub);
/**
* Derives new random blinding factors.
* In original papers blinding factors are generated randomly
* To provide abort-idempotency, blinding factors need to be derived but still need to be UNPREDICTABLE
* To ensure unpredictability a new nonce has to be used.
* Uses HKDF internally
*
* @param secret is secret to derive blinding factors
* @param secret_len secret length
* @param[out] bs array containing the two derivedGNUNET_CRYPTO_CsBlindingSecret
*/
void
GNUNET_CRYPTO_cs_blinding_secrets_derive (const struct GNUNET_CRYPTO_CsNonce *blind_seed,
struct GNUNET_CRYPTO_CsBlindingSecret bs[2]);
/**
* Calculate two blinded c's
* Comment: One would be insecure due to Wagner's algorithm solving ROS
*
* @param bs array of the two blinding factor structs each containing alpha and beta
* @param r_pub array of the two signer's nonce R
* @param pub the public key of the signer
* @param msg the message to blind in preparation for signing
* @param msg_len length of message msg
* @param[out] blinded_c array of the two blinded c's
*/
void
GNUNET_CRYPTO_cs_calc_blinded_c (const struct GNUNET_CRYPTO_CsBlindingSecret bs[2],
const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
const struct GNUNET_CRYPTO_CsPublicKey *pub,
const void *msg,
size_t msg_len,
struct GNUNET_CRYPTO_CsC blinded_c[2]);
/**
* Sign a blinded c
* This function derives b from a nonce and a longterm secret
* In original papers b is generated randomly
* To provide abort-idempotency, b needs to be derived but still need to be UNPREDICTABLE.
* To ensure unpredictability a new nonce has to be used for every signature
* HKDF is used internally for derivation
* r0 and r1 can be derived prior by using GNUNET_CRYPTO_cs_r_derive
*
* @param priv private key to use for the signing and as LTS in HKDF
* @param r array of the two secret nonce from the signer
* @param c array of the two blinded c to sign c_b
* @param nonce is a random nonce
* @param[out] blinded_signature_scalar where to write the signature
* @return 0 or 1 for b (see Clause Blind Signature Scheme)
*/
int
GNUNET_CRYPTO_cs_sign_derive(const struct GNUNET_CRYPTO_CsPrivateKey *priv,
const struct GNUNET_CRYPTO_CsRSecret r[2],
const struct GNUNET_CRYPTO_CsC c[2],
const struct GNUNET_CRYPTO_CsNonce *nonce,
struct GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar
);
/**
* Unblind a blind-signed signature using a c that was blinded
*
* @param blinded_signature_scalar the signature made on the blinded c
* @param bs the blinding factors used in the blinding
* @param[out] signature_scalar where to write the unblinded signature
*/
void
GNUNET_CRYPTO_cs_unblind (const struct GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar,
const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
struct GNUNET_CRYPTO_CsS *signature_scalar);
/**
* Verify whether the given message corresponds to the given signature and the
* signature is valid with respect to the given public key.
*
* @param sig signature that is being validated
* @param pub public key of the signer
* @param msg is the message that should be signed by @a sig (message is used to calculate c)
* @param msg_len is the message length
* @returns #GNUNET_YES on success, #GNUNET_SYSERR if key parameter(s) invalid #GNUNET_NO if signature invalid
*/
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_cs_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
const struct GNUNET_CRYPTO_CsPublicKey *pub,
const void *msg,
size_t msg_len);
\end{lstlisting}

View File

@ -0,0 +1,209 @@
\chapter{Redesigned RSA Protocols}
In order to bring the RSA and \gls{CSBS} protocols closer, this chapter describes a variant of the RSA protocols with the same changes as in the \gls{CSBS} versions (where they can be applied).
\section{Withdraw Protocol}
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{reserve keys } w_s, W_p & & \text{reserve public key } W_p
\\ \text{denomination public key } D_p = e, N & & \text{denomination keys } d_s, D_p
\\ & &
\\\text{generate withdraw secret:}
\\ \omega := randombytes(32)
\\ \text{persist } \langle \omega, D_p \rangle
\\\text{derive coin key pair:} & &
\\ c_s := \text{HKDF}(256, \omega, \text{"cs"})
\\ C_p := \text{Ed25519.GetPub}(c_s)
\\ \text{blind:} & &
\\ b_s := \text{HKDF}(256, \omega, \text{"b-seed"})
\\ r := \text{FDH}(b_s)
\\ m' := \text{FDH}(N, C_p)*r^{e} \mod N & &
\\ \text{sign with reserve private key:} & &
\\ \rho_W := \langle D_p, m' \rangle & &
\\ \sigma_W := \text{Ed25519.Sign}(w_s, \rho_W) & &
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{\rho = W_p, \sigma_W, \rho_W} &
\\ & & \langle D_p, m' \rangle := \rho_W
\\ & & \text{verify if } D_p \text{ is valid}
\\ & & \text{check } \text{Ed25519.Verify}(W_p, \rho_W, \sigma_W)
\\ & & \sigma'_c = (m')^{d_s} \mod N
\\ & & \text{decrease balance if sufficient and}
\\ & & \text{persist } \langle D_p, s \rangle
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{\sigma'_c} &
\\ \text{unblind:}& &
\\ \sigma_c = \sigma'_c*r^{-1} & &
\\ \text{verify signature:}& &
\\ \textbf{check if } \sigma_c^{e} = \text{FDH}(N, C_p) & &
\\ & &
\\ \text{resulting coin: } c_s, C_p, \sigma_c, D_p & &
\\ & &
\\ \text{implementation note: minimum of}
\\ \text{persisted values is } \langle \omega, \sigma_c \rangle
\end{array}$
}
\end{equation*}
\caption{Redesigned RSA withdrawal process}
\label{fig:withdrawal-process-rsa-redesign}
\end{figure}
The changes to the RSA witdhdraw protocol (see \autoref{fig:withdrawal-process-rsa-redesign}) are limited to the derivation of the coin and blinding factor.
\section{Refresh Protocol}
The changes to the refresh protocol are related to the derivation of transfer secrets and subsequent operations, see \autoref{fig:refresh-derive-rsa-redesign}, \autoref{fig:refresh-part1-rsa-redesign} and \autoref{fig:refresh-part2-rsa-redesign}.
\begin{figure}[htp]
\centering
\fbox{%
\procedure[codesize=\small]{$\text{RefreshDerive}(t, \langle e, N \rangle, C_p)$}{%
T := \text{Curve25519.GetPub}(t) \\
x := \textrm{ECDH-EC}(t, C_p) \\
b_s := \text{HKDF}(256, x, \text{"b-seed"}) \\
r := \text{FDH}(b_s) \\
c'_s := \text{HKDF}(256,x,"c") \\
C'_p := \text{Ed25519.GetPub}(c'_s) \\
\overline{m} := r^e * C'_p \mod N \\
\pcreturn \langle T, c_s', C_p', \overline{m} \rangle
}
}
\caption{Redesigned RSA RefreshDerive algorithm}
\label{fig:refresh-derive-rsa-redesign}
\end{figure}
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{denomination public key } D_{p(i)} & & \text{denomination keys } d_{s(i)}, D_{p(i)}
\\ \text{coin}_0 = \langle D_{p(0)}, c_s^{(0)}, C_p^{(0)}, \sigma_c^{(0)} \rangle & &
% refresh request
\\ \text{Select} \langle N_t, e_t\rangle := D_{p(t)} \in D_{p(i)}
\\ \omega := randombytes(32)
\\ \text{persist } \langle \omega, D_{p(t)} \rangle
\\ \textbf{for } i = 1, \dots, \kappa: % generate k derives
\\ t_i := \text{HKDF}(256, \omega,\text{"t} i \text{"} ) % seed generation
\\ X_i := \text{RefreshDerive}(t_i, D_{p(t)}, C_p^{(0)})
\\ (T_i, c_s^{(i)}, C_p^{(i)}, \overline{m}_i) := X_i
\\ \textbf{endfor}
\\ h_T := H(T_1, \dots, T_k)
\\ h_{\overline{m}} := H(\overline{m}_1, \dots, \overline{m}_k)
\\ h_C := H(h_t, h_{\overline{m}})
\\ \rho_{RC} := \langle h_C, D_{p(t)}, D_{p(0)}, C_p^{(0)}, \sigma_C^{(0)} \rangle
\\ \sigma_{RC} := \text{Ed25519.Sign}(c_s^{(0)}, \rho_{RC})
\\ \text{Persist refresh-request} \langle \omega, \rho_{RC}, \sigma_{RC} \rangle
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{\rho_{RC}, \sigma_{RC}} &
% Exchange checks refresh request
\\ & & (h_C, D_{p(t)}, D_{p(0)}, C_p^{(0)}, \sigma_C^{(0)} = \rho_{RC})
\\ & & \textbf{check} \text{Ed25519.Verify}(C_p^{(0)}, \sigma_{RC}, \rho_{RC})
\\ & & x \rightarrow \text{GetOldRefresh}(\rho_{RC})
\\ & & \textbf{Comment: }\text{GetOldRefresh} (\rho_{RC} \mapsto \{\bot,\gamma\})
\\ & & \pcif x = \bot
\\ & & v := \text{Denomination}(D_{p(t)})
\\ & & \langle e_0, N_0 \rangle := D_{p(0)}
\\ & & \textbf{check } \text{IsOverspending}(C_p^{(0)}, D_ {p(0)}, v)
\\ & & \textbf{check } D_{p(t)} \in \{D_{p(i)}\}
\\ & & \textbf{check } \text{FDH}(N_0, C_p^{(0)}) \equiv_{N_0} (\sigma_0^{(0)})^{e_0}
\\ & & \text{MarkFractionalSpend}(C_p^{(0)}, v)
\\ & & \gamma \leftarrow \{1, \dots, \kappa\}
\\ & & \text{Persist refresh-record } \langle \rho_{RC},\gamma \rangle
\\ & & \pcelse
\\ & & \gamma := x
\\ & & \textbf{endif}
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{\gamma} &
\\
\\
\\ & \textit{Continued in figure \ref{fig:refresh-part2}} &
%\\ \pcintertext[dotted]{(Continued in Figure)}
\end{array}$
}
\end{equation*}
\caption{Redesigned RSA refresh protocol (commit phase)}
\label{fig:refresh-part1-rsa-redesign}
\end{figure}
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ & \textit{Continuation of figure \ref{fig:refresh-part1}} &
\\
\\
% Check challenge and send challenge response (reveal not selected msgs)
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{\gamma} &
\\ \textbf{check } \text{IsConsistentChallenge}(\rho_{RC}, \gamma)
\\ \textbf{Comment: } \text{IsConsistentChallenge}\\(\rho_{RC}, \gamma) \mapsto \{ \bot,\top \}
\\
\\ \text{Persist refresh-challenge} \langle \rho_{RC}, \gamma \rangle
\\ S := \langle t_1, \dots, t_{\gamma-1}, t_{\gamma+1}, \dots, t_\kappa \rangle % all seeds without the gamma seed
\\ \rho_L = \langle C_p^{(0)}, D_{p(t)}, T_{\gamma},\overline{m}_\gamma \rangle
\\ \rho_{RR} = \langle T_\gamma, \overline{m}_\gamma, S \rangle
\\ \sigma_{L} = \text{Ed25519.Sign}(c_s^{(0)}, \rho_{L})
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{\rho_{RR},\rho_L, \sigma_{L}} &
% check revealed msgs and sign coin
\\ & & \langle T'_\gamma, \overline{m}'_\gamma, S \rangle := \rho_{RR}
\\ & & \langle t_1, \dots, t_{\gamma-1}, t_{\gamma+1}, \dots, t_\kappa \rangle ) := S
\\ & & \textbf{check } \text{Ed25519.Verify}(C_p^{(0)}, \sigma_L, \rho_L)
\\ & & \textbf{for} i = 1,\dots, \gamma-1, \gamma+1,\dots, \kappa
\\ & & X_i := \text{RefreshDerive}(t_i, D_{p(t)}, C_p^{(0)})
\\ & & \langle T_i, c_s^{(i)}, C_p^{(i)}, \overline{m}_i \rangle := X_i
\\ & & \textbf{endfor}
\\ & & h_T' = H(T_1,\dots,T_{\gamma-1},T'_{\gamma},T_{\gamma+1},\dots,T_\kappa)
\\ & & h_{\overline{m}}' = H(\overline{m}_1,\dots,\overline{m}_{\gamma-1},\overline{m}'_{\gamma},\overline{m}_{\gamma+1},\dots,\overline{m}_\kappa)
\\ & & h_C' = H(h_T', h_{\overline{m}}')
\\ & & \textbf{check } h_C = h_C'
\\ & & \overline{\sigma}_C^{(\gamma)} := \overline{m}^{d_{s(t)}}
\\ & & \text{persist } \langle \rho_L, \sigma_L \rangle
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{\overline{\sigma}_C^{(\gamma)}} &
% Check coin signature and persist coin
\\ \sigma_C^{(\gamma)} := r^{-1}\overline{\sigma}_C^{(\gamma)}
\\ \textbf{check if } (\sigma_C^{(\gamma)})^{e_t} \equiv_{N_t} C_p^{(\gamma)}
\\ \text{Persist coin} \langle D_{p(t)}, c_s^{(\gamma)}, C_p^{(\gamma)}, \sigma_C^{(\gamma)} \rangle
\end{array}$
}
\end{equation*}
\caption{Redesigned RSA refresh protocol (reveal phase)}
\label{fig:refresh-part2-rsa-redesign}
\end{figure}
\section{Linking Protocol}
The changes are described in \autoref{fig:refresh-link-rsa-redesign}.
\begin{figure}[htp]
\begin{equation*}
\resizebox{1.0\textwidth}{!}{$\displaystyle
\begin{array}{ l c l }
% preliminaries
\text{Customer} & & \text{Exchange}
\\ \text{knows:} & & \text{knows:}
\\ \text{coin}_0 = \langle D_{p(0)}, c_s^{(0)}, C_p^{(0)}, \sigma_{C}^{(0)} \rangle
\\ & \xrightarrow[\rule{2.5cm}{0pt}]{C_{p(0)}} &
\\ & & L := \text{LookupLink}(C_{p(0)})
\\ & & \textbf{Comment: } \text{LookupLink}(C_p) \mapsto \{\langle \rho_L^{(i)},
\\ & & \sigma_L^{(i)}, \overline{\sigma}_C^{(i)} \rangle\}
\\ & \xleftarrow[\rule{2.5cm}{0pt}]{L} &
\\ \pcfor \langle \rho_{L}^{(i)}, \overline{\sigma}_L^{(i)}, \sigma_C^{(i)} \rangle \in L
\\ \langle \hat{C}_p^{(i)}, D_{p(t)}^{(i)}, T_\gamma^{(i)}, \overline{m}_\gamma^{(i)} \rangle := \rho_L^{(i)}
\\ \langle e_t^{(i)}, N_t^{(i)} \rangle := D_{p(t)}^{(i)}
\\ \textbf{check } \hat{C}_p^{(i)} \equiv C_p^{(0)}
\\ \textbf{check } \text{Ed25519.Verify}(C_p^{(0)}, \rho_{L}^{(i)}, \sigma_L^{(i)})
\\ x_i := \text{ECDH}(c_s^{(0)}, T_{\gamma}^{(i)})
\\ c_s^{(i)} := \text{HKDF}(256,x_i,"c")
\\ C_p^{(i)} := \text{Ed25519.GetPub}(c_s^{(i)})
\\ b_s^{(i)} := \text{HKDF}(256, x_i, \text{"b-seed"})
\\ r_i := \text{FDH}(b_s^{(i)})
\\ \sigma_C^{(i)} := (r_i)^{-1} \cdot \overline{m}_\gamma^{(i)}
\\ \textbf{check } (\sigma_C^{(i)})^{e_t^{(i)}} \equiv_{N_t^{(i)}} C_p^{(i)}
\\ \text{(Re-)obtain coin} \langle D_{p(t)}^{(i)},c_s^{(i)}, C_p^{(i)}, \sigma_C^{(i)} \rangle
\end{array}$
}
\end{equation*}
\caption{Redesigned RSA linking protocol}
\label{fig:refresh-link-rsa-redesign}
\end{figure}

View File

@ -0,0 +1,35 @@
%!TEX root = ../dokumentation.tex
\chapter{Das erste Kapitel}
\section{Hello}
Erste Erwähnung eines Akronyms wird als Fußnote angezeigt. Jede weitere wird
nur verlinkt: \ac{AGPL}. Zweite Erwähnung \ac{AGPL}
Verweise auf das Glossar: \gls{Glossareintrag}, \glspl{Glossareintrag}
Nur erwähnte Literaturverweise werden auch im Literaturverzeichnis gedruckt:
\cite{baumgartner:2002}, \cite{dreyfus:1980}
\Gls{Glossareintrag}
Meine erste Fußnote\footnote{Ich bin eine Fußnote}
\begin{wrapfigure}{r}{.4\textwidth}
\includegraphics[height=.4\textwidth]{taler.png}
\vspace{-15pt}
\caption{Das Logo der Musterfirma\footnotemark}
\end{wrapfigure}
%Quelle muss in Fußnote stehen (da sonst aufgrund eines Fehlers nicht kompiliert
% wird)
\footnotetext{aus \cite{mustermann:2012}}
Ein ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der das Bild
umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der
das Bild umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer
Text, der das Bild umfließt. Ein ganz langer Text, der das Bild umfließt. Ein
ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der das Bild
umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der
das Bild umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der das Bild
umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der
das Bild umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer
Text, der das Bild umfließt. Ein ganz langer Text, der das Bild umfließt. Ein
ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der das Bild
umfließt. Ein ganz langer Text, der das Bild umfließt. Ein ganz langer Text, der
das Bild umfließt.

View File

@ -0,0 +1,160 @@
\section{Remediation of the Withdraw Loophole}
The withdraw loophole allows untaxed and untraceable payments by "misusing" the withdraw protocol.
It allows withdraw operations where owner of the resulting coins isn't the owner of the reserve that the coins where withdrawn from.
It is used for tipping and can therefore be seen as a feature.
Using the withdraw loophole for payments is illustrated in figure \ref{fig:withdraw-loophole-exploit}.
Note that we omitted the parts leading up to the coin creation (contract, agreement of price, number of coins and their denominations).
This is how it works on a high level:
\begin{enumerate}
\item The malicous merchant generates and blinds coins, which are then transmitted to the customer
\item The customer authorizes the withdraw from his reserve by signing the blinded coins with the private key of his reserve, thus generating withdraw confirmations.
\item The withdraw confirmations are transmitted to the exchange, which generates the signatures and returns them to the malicous merchant.
\item The malicous merchant unblinds the signatures.
He is now in possession of the coin, thus the payment is completed.
\end{enumerate}
\begin{figure}[h]
\begin{equation*}
\begin{array}{ l c l}
% preliminaries
\text{Customer} & & \text{malicous Merchant}
\\ \text{knows:} & & \text{knows:}
\\ \text{reserve keys } w_s, W_p
\\ \text{denomination public key } D_p = \langle e, N \rangle & & \text{denomination public key } D_p = \langle e, N \rangle
\\
% generate coin
\\ & & \text{generate coin key pair:}
\\ & & c_s, C_p \leftarrow \text{Ed25519.KeyGen}()
% blind
\\ & & \text{blind:}
\\ & & r \leftarrow random \in \mathbb{Z}_N^*
\\ & & m' := \text{FDH}(N, C_p)*r^{e} \mod N
% sing with reserve sk
\\ & \xleftarrow[\rule{2cm}{0pt}]{m'}
\\ \text{sign with reserve private key:}
\\ \rho_W := \langle D_p, m' \rangle
\\ \sigma_W := \text{Ed25519.Sign}(w_s, \rho_W)
\\ & \xrightarrow[\rule{2cm}{0pt}]{ \langle W_p, \sigma_W, \rho_W \rangle }
\\
% TODO add some kind of separator
\hline
\\
\text{malicous Merchant} & & \text{Exchange}
\\\text{knows:} & & \text{knows:}
\\& & \text{reserve public key } W_p
\\ \text{denomination public key } D_p = \langle e, N \rangle & & \text{denomination keys } d_s, D_p
\\
\\ & \xrightarrow[\rule{2cm}{0pt}]{ \langle W_p, \sigma_W, \rho_W \rangle }
\\ & & \langle D_p, m' \rangle := \rho_W
\\ & & \text{verify if } D_p \text{ is valid}
\\ & & \textbf{check } \text{Ed25519.Verify}(W_p, \rho_W, \sigma_W)
\\ & & \text{decrease balance if sufficient}
\\ & & \text{sign:}
\\ & & \sigma'_c := (m')^{d_s} \mod N
\\ & \xleftarrow[\rule{2cm}{0pt}]{\sigma'_c}
\\ \text{unblind:}
\\ \sigma_c := \sigma'_c*r^{-1}
\\ \text{verify signature:}
\\ \textbf{check if } \sigma_c = \text{FDH}(N, C_p)
\\
\\ \text{resulting coin: } \langle c_s, C_p, \sigma_c, D_p \rangle
\end{array}
\end{equation*}
\caption{untaxed payment using withdraw loophole}
\label{fig:withdraw-loophole-exploit}
\end{figure}
\subsection{Requirements For A Possible Solution}
A viable solution has to fix the withdraw loophole, while still providing a solution for tipping. In addition, Taler's security properties must not be weakened.
The underlying problem that has to be solved is to check that the person withdrawing a coin is also the owner of the reserve used in the withdraw.
This has to be solved in a way that prevents the customer and malicious merchant to work together.
% Requirements For A Perfect Solution}
% minimal adjustments to Taler
% Commitment to sk of Reserve -> constructed by customer (key owner)
% commitment to sk of coin
% how do we ensure that the customer is key owner? -> combine with reserve sk
% how do we verify? problems with blinding
% how do we ensure that the coin in the commitment is the coin that is signed?
% exchange must not learn anything about coin to prevent linking of withdraw and transaction
\subsection{Discussed Solution}
For our proposed solution, a few adjustments to Taler have to be made:
\begin{itemize}
\item The withdraw confirmation must include a commitment to the public key.
This commitment must be constructed in a way that requires the customer to know the public key.
The exception to this are special tipping reserves (to preserve the tipping feature).
\item Cut-and-choose is added to the withdraw protocol.
This means that the customer has to generate the coin and withdraw confirmation $ k $ times.
The exchange will then choose one of the $ k $ sessions.
The customer has to reveal the coin public key, blinding secret and commitment for all sessions except the chosen one.
If the customer isn't able to deliver, the reserve is locked for future withdraws until the other sessions are delivered.
Cut-and-choose is introduced to verify whether the customer honestly created the commitment and used the same coin public key for the signature creation and the commitment.
If the reserve is a special tip reserve (which has to be registered), this check is omitted.
\item An additional protocol is created that transfers the remaining value of a coin back to the reserve if anyone is able to reveal the commitment from the withdrawal.
The adjustments described up to this point lead to the customer knowing all the necessary values for using this protocol.
Besides the customer, no one must be able to reproduce the commitment, except in case of a reserve key compromise.
\item Reserves are limited (usually only one, unless justified) and bound to a customer (KYC).
% this goes further than fixing the loophole. It prevents people from creating new reserves that are then to be transfered
% TODO check if there are disadvantages to this, especially regarding privacy
\item For a coin to be refreshable, it must have been seen by the exchange before, meaning that it had to be used for a payment.
The purpose of this is to prevent a malicious merchant to simply refresh a coin after withdraw to prevent the customer from reverting the withdraw.
\item For any coin used in a payment, the subtracted value must be higher that a certain threshold (set globally or per denomination).
For example, if the threshold is $ 10\% $, at least CHF 10 of a 100 CHF coin must be used for a payment.
The goal of this change is to prevent a malicious merchant from buying a very cheap article to be able to refresh the coin.
\end{itemize}
The commitment has to fulfill the following properties:
\begin{enumerate}
\item It has to be constructed using the reserve private key and must be verifiable using the corresponding public key.
\item It has to include the coin public key.
\item It has to be constructed in a way that ensures that the customer has knowledge of the coin public key.
\item Everyone with knowledge of the two keys must be able to recreate it.
\end{enumerate}
A possible commitment that partially satisfies the properties can be constructed by hashing a signature of the coin's public key:
\begin{equation*}
H( \text{Ed25519.Sign} (w_w, C_p) )
\end{equation*}
Note that the PureEdDSA variant of Ed25519 has to be used for this.
This variant doesn't hash the message before signing (see \cite{rfc8032} for further details).\\
It is still possible for a customer and a malicious merchant to construct the commitment without the customer gaining knowledge of the coin public key.
However, the customer has to share one half of the hash of the reserve private key (which is practically one half of the private key, refer to section \ref{sec:eddsa-signature-creation} for details about EdDSA signature creation).
% was passiert wenn im Verhör und gezwungen wird, Keys herauszurücken?
There is one drawback to this solution:
In case of a reserve key compromise, coins generated by withdraw operation (not refreshed ones) can be linked to withdraw operations, thus revealing relationships between reserves and payments.
This is because an adversary (exchange or auditor) in possession of a reserve private key and coin public keys can calculate $ \text{H(Ed25519.Sign}(w_s, C_p)) $ and check in the database if there is a corresponding withdraw operation, thus linking reserve and coin.
\subsubsection{Discussion}
This is not perfect solution.
It is designed to make untaxed payments using the withdraw loophole less attractive to use for merchants.
If accepted, it should only be used in deployments where the withdraw loophole has to be prohibited.\\
The proposed modifications achieve that a malicious merchant, who wants to perform payments using the withdraw loophole, has to accept one of these drawbacks:
\begin{itemize}
\item He has to accept that the customer is able to revert the payment.
\item He has to spend the coins fully.\\
If he is registered as a merchant at an exchange, he can perform payments to himself to launder the money.
Here, Talers \ac{AML} capabilities come into play.\\
The other possibility is to buy goods at other merchants.
These goods then have to be liquidated, which requires effort.
This wouldn't be a problem (for the malicous merchant) if cryptocurrency can be bought using Taler.
\item He has to spend the coins partially to be able to refresh them (thus preventing payment reversion by the customer).
The goods that were bought using the coin fraction then would have to be liquidated (see previous point).
\item He has to add the threshold value that is lost in order to refresh the coin into the price for payments.
\end{itemize}
The commitment added to the withdrawal weakens the privacy of coins.
Blinding guarantees everlasting privacy, which would be neutralized by the commitment.
The added cut-and-choose makes withdrawing more intensive, which leads to increased infrastructure requirements (and therefore costs).
The added threshold makes coin spending less flexible.
Wallets either have to contain more coins to guarantee that there is always a coin (or multiple) available to guarantee a payment without violating the threshold limitations.
The other variant is that wallets refrain from withdrawing coins with big(ger) denominations, which leads to bigger sums of coins used per payment.
This discussed solution is submitted to the Taler team as a part of the thesis documentation, upon which they can review the protocol changes and decide whether to pursue further.
Therefore, the solution will not be implemented during this thesis.

373
doc/cs/content/x_taler.tex Normal file
View File

@ -0,0 +1,373 @@
\chapter{Taler}
\section{Taler Architecture}
\subsection{Auditor}
The following text is cited from section 4.4 in \cite{dold:the-gnu-taler-system}
\begin{center}
\textit{
"The auditor consists of two processes that are regularly run and generate auditing reports.
Both processes access the exchanges database, either directly (for exchange-internal auditing as part if its operational security) or over a replica (in the case of external auditors).
The taler-wire-auditor process checks that the incoming and outgoing transfers recorded in the exchanges database match wire transfers of the underlying bank account.
To access the transaction history (typically recorded by the bank), the wire auditor uses a wire plugin, with the same interface and implementation as the exchanges wire plugins.
The taler-auditor process generates a report with the following information:
}
\end{center}
\begin{itemize}
\item \textit{Do the operations stored in a reserves history match the reserves balance?}
\item \textit{Did the exchange record outgoing transactions to the right merchant for deposits after the deadline for the payment was reached?}
\item \textit{Do operations recorded on coins (deposit, refresh, refund) match the remaining value on the coin?}
\item \textit{Do operations respect the expiration of denominations?}
\item \textit{For a denomination, is the number of pairwise different coin public keys recorded in deposit/refresh operations smaller or equal to the number of blind signatures recorded in withdraw/refresh operations? If this invariant is violated, the corresponding denomination must be revoked.}
\item \textit{What is the income if the exchange from different fees?}
\end{itemize}
\begin{center}
\textit{
\dots
The auditor exposes a web server with the taler-auditor-httpd process.
Currently, it only shows a website that allows the customer to add the auditor to the list of trusted auditors in their wallet.
In future versions, the auditor will also have HTTP endpoints that allow merchants to submit samples of deposit confirmations, which will be checked against the deposit permissions in the exchanges database to detect compromised signing keys or missing writes.
Furthermore, in deployments that require the merchant to register with the exchange beforehand, the auditor also offers a list of exchanges it audits, so that the merchant backend can automatically register with all exchanges it transitively trusts."
}
\end{center}
Some details were left out (see at the dots) and can be read in section 4.4 in \cite{dold:the-gnu-taler-system}
\subsubsection{Technical Details}
Documentation: \cite{taler-documentation:auditor-operator-manual} \\
Git Repositories:
\begin{itemize}
\item Main repository: \cite{taler-git:exchange} (Part of exchange repository, inside ./src/auditor and ./src/auditordb)
\item Auditor's public website: \cite{taler-git:auditor}
\end{itemize}
Language: C \\
Dependencies:
\begin{itemize}
\item GNUnet >= 0.14.0
\item GNU libmicrohttpd >= 0.9.71
\item Postgres >= 9.6, including libpq
\item libjansson >= 2.7
\item libargon2 >= 20171227
\item libsodium >= 1.0
\item libgcrypt >= 1.6
\item libqrencode >= 4.0.0
\item libcurl >= 7.26 (or libgnurl >= 7.26)
\item GNU libunistring >= 0.9.3
\item libsqlite3 >= 3.16.2
\end{itemize}
The auditor API is implemented as REST API and uses \ac{JSON} as message format.
The auditor does not implement HTTPS (TLS), instead it recommends using a HTTP reverse Proxy that offers TLS termination.
By delegating the responsibility for TLS termination, the auditor implementation becomes lighter.
There are a lot more technical details written in the documentation linked above and in the README files.
Since Taler is actively developed and technical details could change, we refer to this documentation.
\subsection{Exchange}
The following text is cited from section 4.3 in \cite{dold:the-gnu-taler-system}
\begin{center}
\textit{
"The exchange consists of three independent processes:
}
\end{center}
\begin{itemize}
\item \textit{The taler-exchange-httpd process handles HTTP requests from clients, mainly merchants and wallets.}
\item \textit{The taler-exchange-wirewatch process watches for wire transfers to the exchanges bank account and updates reserves based on that.}
\item \textit{The taler-exchange-aggregator process aggregates outgoing transactions to merchants.}
\end{itemize}
\begin{center}
\textit{
All three processes exchange data via the same database.
Only taler-exchange-httpd needs access to the exchanges online signing keys and denomination keys.
The database is accessed via a Taler-specific database abstraction layer.
Different databases can be supported via plugins; at the time of writing this, only a PostgreSQL plugin has been implemented.
Wire plugins are used as an abstraction to access the account layer that Taler runs on.
Specifically, the wirewatch process uses the plugin to monitor incoming transfers, and the aggregator process uses the wire plugin to make wire transfers to merchants.
The following APIs are offered by the exchange:
}
\textbf{\textit{Announcing keys, bank accounts and other public information}}\\
\textit{
The exchange offers the list of denomination keys, signing keys, auditors, supported bank accounts, revoked keys and other general information needed to use the exchanges services via the /keys and /wire APIs.
}
\textbf{\textit{Reserve status and withdrawal}}\\
\textit{
After having wired money to the exchange, the status of the reserve can be checked via the /reserve/status API. Since the wire transfer usually takes some time to arrive at the exchange, wallets should periodically poll this API, and initiate a withdrawal with /reserve/withdraw once the exchange received the funds.
}
\textbf{\textit{Deposits and tracking}}\\
\textit{
Merchants transmit deposit permissions they have received from customers to the exchange via the/deposit API. Since multiple deposits are aggregated into one wire transfer, the merchant additionally can use the exchanges /track/transfer API that returns the list of deposits for an identifier included in the wire transfer to the merchant, as well as the /track/transaction API to look up which wire transfer included the payment for a given deposit.
}
\textbf{\textit{Refunds}}\\
\textit{
The refund API (/refund) can “undo” a deposit if the merchant gave their signature, and the aggregation deadline for the payment has not occurred yet.
}
\textbf{\textit{Emergency payback}}\\
\textit{
The emergency payback API (/payback) allows customers to be compensated for coins whose denomination key has been revoked.
Customers must send either a full withdrawal transcript that includes their private blinding factor, or a refresh transcript (of a refresh that had the revoked denominations as one of the targets) that includes blinding factors.
In the former case, the reserve is credited, in the latter case, the source coin of the refresh is refunded and can be refreshed again."
}
\end{center}
Additional information for how the exchange generates new denomination and signing keys can be found in the end of section 4.3 of \cite{dold:the-gnu-taler-system}.
\begin{figure}[h!]
\centering
\includegraphics[height=0.5\textwidth]{taler-exchange.png}
\caption{Architecture of the Taler Exchange reference implementation. Source: \cite{dold:the-gnu-taler-system}}
\label{fig:taler-arch-exchange}
\end{figure}
\subsubsection{Technical Details}
Documentation: \cite{taler-documentation:exchange-operator-manual} \\
Git Repository: Main repository: \cite{taler-git:exchange} \\
Language: C \\
Dependencies:
\begin{itemize}
\item GNUnet >= 0.14.0
\item GNU libmicrohttpd >= 0.9.71
\item Postgres >= 9.6, including libpq
\item libjansson >= 2.7
\item libargon2 >= 20171227
\item libsodium >= 1.0
\item libgcrypt >= 1.6
\item libqrencode >= 4.0.0
\item libcurl >= 7.26 (or libgnurl >= 7.26)
\item GNU libunistring >= 0.9.3
\item libsqlite3 >= 3.16.2
\end{itemize}
The exchanges API is implemented as REST API and uses \ac{JSON} as message format.
There are a lot more technical details written in the documentation linked above and in the README files.
Since Taler is actively developed and technical details could change, we refer to this documentation.
\subsection{Merchant}
The following text is cited from section 4.5 in \cite{dold:the-gnu-taler-system}
\begin{center}
\textit{
"The Taler merchant backend is a component that abstracts away the details of processing Taler payments and provides a simple HTTP API.
The merchant backend handles cryptographic operations (signature verification, signing), secret management and communication with the exchange.
The backend API (see \url{https://docs.taler.net/api/}) is divided into two types of HTTP endpoints:
}
\end{center}
\begin{enumerate}
\item \textit{Functionality that is accessed internally by the merchant.
These API stypically require authentication and/or are only accessible from within the private network of the merchant.}
\item \textit{Functionality that is exposed publicly on the Internet and accessed by the customers wallet and browser.}
\end{enumerate}
\begin{center}
\textit{
A typical merchant has a storefront component that customers visit with their browser, as well as a back office component that allows the merchant to view information about payments that customers made and that integrates with other components such as order processing and shipping."
}
\end{center}
\subsubsection{Processing Payments}
\begin{center}
\textit{
"To process a payment, the storefront first instructs the backend to create an order.
The order contains information relevant to the purchase, and is in fact a subset of the information contained in the contract terms.
The backend automatically adds missing information to the order details provided by the storefront.
The full contract terms can only be signed once the customer provides the claim public key for the contract.\\
Each order is uniquely identified by an order ID, which can be chosen by the storefront or automatically generated by the backend.
The order ID can be used to query the status of the payment.
If the customer did not pay for an order ID yet, the response from the backend includes a payment redirect URL.
The storefront can redirect the customer to this payment redirect URL; visiting the URL will trigger the customers browser/wallet to prompt for a payment.\\
To simplify the implementation of the storefront, the merchant backend can serve a page to the customers browser that triggers the payment via the HTTP402 status code and the corresponding headers, and provides a fallback (in the form of a taler:pay link) for loosely integrated browsers.
When checking the status of a payment that is not settled yet, the response from the merchant backend will contains a payment redirect URL.
The storefront redirects the browser to this URL, which is served by the merchant backend and triggers the payment.
\dots "
}
\end{center}
\subsubsection{Back Office APIs}
\begin{center}
\textit{
"The back office API allows the merchant to query information about the history and status of payments, as well as correlate wire transfers to the merchants bank account with the respective GNU Taler payment.
This API is necessary to allow integration with other parts of the merchants e-commerce infrastructure."
}
\end{center}
% Nachfolgende Section nicht notwendig.
% \subsubsection{Example Merchant Frontends}
% This section is included to provide an overview of the reference implementation for the merchant.
% This helps to get a better understanding of how a merchant could potentially look like.
% Note that the actual code could differ from the cited part.
% The actual code can be found in \url{git.taler.net}
% \begin{center}
% \textit{
% We implemented the following applications using the merchant backend API.
% }
% \textit{\textbf{Blog Merchant}}\\
% \textit{
% The blog merchants landing page has a list of article titles with a teaser.
% When following the link to the article, the customer is asked to pay to view the article.
% }
% \textit{\textbf{Donations}}\\
% \textit{
% The donations frontend allows the customer to select a project to donate to.
% The fulfillment page shows a donation receipt.
% }
% \textit{\textbf{Codeless Payments}}\\
% \textit{
% The codeless payment frontend is a prototype for a user interface that allows merchants to sell products on their website without having to write code to integrate with the merchant backend.
% Instead, the merchant uses a web interface to manage products and their available stock.
% The codeless payment frontend then creates an HTML snippet with a payment button that the merchant can copy-and-paste integrate into their storefront.
% }
% \textit{\textbf{Survey}}\\
% \textit{
% The survey frontend showcases the tipping functionality of GNU Taler.
% The user fills out a survey and receives a tip for completing it.
% }
% \textit{\textbf{Back Office}}\\
% \textit{
% The example back-office application shows the history and status of payments processed by the merchant.
% }
% \end{center}
\begin{figure}[h!]
\centering
\includegraphics[height=0.5\textwidth]{taler-merchant.png}
\caption{Architecture Taler Merchant reference implementation. Source: \cite{dold:the-gnu-taler-system}}
\label{fig:taler-arch-merchant}
\end{figure}
\subsubsection{Technical Details}
Documentation: \cite{taler-documentation:merchant-backend-operator-manual} \\
API Documentation: \cite{taler-documentation:merchant-api} \\
Back-office Documentation: \cite{taler-documentation:back-office} \\
Point-of-Sales Documentation: \cite{taler-documentation:pos-manual} \\
Git Repositories:
\begin{itemize}
\item Backend: \cite{taler-git:merchant}
\item Backoffice: \cite{taler-git:backoffice}
\item Point-of-Sales App: \cite{taler-git:android} (part of android repo)
\end{itemize}
Language: C (Backend), Kotlin (\ac{PoS}), [Python, Javascript] (Backoffice)\\
Dependencies:
\begin{itemize}
\item GNUnet >= 0.14.0
\item GNU libmicrohttpd >= 0.9.71
\item Postgres >= 9.6, including libpq
\item libjansson >= 2.7
\item libargon2 >= 20171227
\item libsodium >= 1.0
\item libgcrypt >= 1.6
\item libqrencode >= 4.0.0
\item libcurl >= 7.26 (or libgnurl >= 7.26)
\item GNU libunistring >= 0.9.3
\item libsqlite3 >= 3.16.2
\item Flask (Backoffice)
\end{itemize}
Frontend Repositories:
\begin{itemize}
\item Payments with Django: \cite{taler-git:django-payments}
\item Wordpress woocommerce plugin: \cite{taler-git:woocommerce}
\item Saleor Frontend: \cite{taler-git:saleor}
\item Demo Frontends: \cite{taler-git:merchant-demos}
\end{itemize}
The merchants API is implemented as REST API and uses \ac{JSON} as message format.
The \ac{PoS} app is for the merchant to process customer's orders by adding or removing products, calculating the amount owed by the customer or letting the customer make a Taler payment via QR code or NFC.
The back office Web service allows a merchant to check status of their Taler transactions.
There are already a lot of demo pages and integrations for different e-commerce solutions.
There are a lot more technical details written in the documentation linked above and in the README files.
Since Taler is actively developed and technical details could change, we refer to this documentation.
\subsection{Wallet}
The following text is cited from section 4.6 in \cite{dold:the-gnu-taler-system}
\begin{center}
\textit{
"The wallet manages the customers reserves and coins, lets the customer view and pay for contracts from merchants. \dots
The reference implementation of the GNU Taler wallet is written in the Type-Script language against the WebExtension API, a cross-browser mechanism for browser extensions.
The reference wallet is a “tightly integrated” wallet, as it directly hooks into the browser to process responses with the HTTP status code“402 Payment Required”.\\
Many cryptographic operations needed to implement the wallet are not commonly available in a browser environment.
We cross-compile the GNU Taler utility library written in C as well as its dependencies (such as libgcrypt) to asm.js (and WebAssembly on supported platforms) using the LLVM-based emscripten toolchain.
Cryptographic operations run in an isolated process implemented as a WebWorker.
This design allows the relatively slow cryptographic operations to run concurrently in the background in multiple threads.
Since the crypto WebWorkers are started on-demand, the wallet only uses minimal resources when not actively used."
}
\end{center}
\subsubsection{Optimizations}
\begin{center}
\textit{
"To improve the perceived performance of cryptographic operations, the wallet optimistically creates signatures in the background while the user is looking at the “confirm payment” dialog.
If the user does not accept the contract, these signatures are thrown away instead of being sent to the merchant.
This effectively hides the latency of the most expensive cryptographic operations, as they are done while the user consciously needs to make a decision on whether to proceed with a payment."
}
\end{center}
\subsubsection{Wallet Detection}
\begin{center}
\textit{
" \dots
Browser fingerprinting is a concern with any additional APIs made available to websites, either by the browser itself or by browser extensions.
Since a website can simply try to trigger a payment to determine whether a tightly integrated Taler wallet is installed, one bit of additional fingerprinting information is already available through the usage of Taler.
The dynamic detection methods do not, however, expose any information that is not already available to websites by signaling the wallet through HTTP headers."
}
\end{center}
\subsubsection{Further Wallet Features}
More information about other Wallet Features like coin selection, wallet liquidation and wallet signaling can be found in sections 4.6.2, 4.6.5 and 4.6.6 of \cite{dold:the-gnu-taler-system}.
\begin{figure}[h!]
\centering
\includegraphics[height=0.5\textwidth]{taler-wallet.png}
\caption{Architecture of the Taler Wallet reference implementation. Source: \cite{dold:the-gnu-taler-system}}
\label{fig:taler-wallet-reference-impl}
\end{figure}
\subsubsection{Technical Details}
Documentation: \cite{taler-documentation:wallet-developer-manual} \\
Wallet-CLI documentation: \cite{taler-documentation:wallet-cli-manual} \\
Git Repository:
\begin{itemize}
\item Main repository: \cite{taler-git:wallet-core} \\
This Repository includes the wallet-core and the implementations for the web extension and CLI.
\item Android app: \cite{taler-git:android}
\item iOS app: \cite{taler-git:ios}
\end{itemize}
Language: Typescript, Javascript (wallet-core, web extension), Kotlin (Android app), Swift (iOS app)\\
Dependencies:
\begin{itemize}
\item prettier
\item rimraf
\item rollup
\item typescript
\item ava
\item esbuild
\item axios
\item tslib
\item cancellationtoken
\item minimatch
\item source-map-support
\item big-integer
\item fflate
\item esm
\item jed
\item nyc
\item po2json
\item typedoc
\item api-extractor
\end{itemize}
There are a lot more technical details written in the documentation linked above and in the README files.
Since Taler is actively developed and technical details could change, we refer to this documentation.

BIN
doc/cs/images/bfh_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
doc/cs/images/logo-2021.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
doc/cs/images/taler-pki.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

93
doc/cs/thesis.tex Normal file
View File

@ -0,0 +1,93 @@
%============================ MAIN DOCUMENT ================================
% define document class
\documentclass[
a4paper % paper format
% ,10.5pt % fontsize
% ,BCOR=18mm % Binding correction
% ,twoside
% ,headings=openright
,bibliography=totoc % If enabled add bibliography to TOC
,class=scrreprt % If removed, makes book format (1 side left, 1 right)
,listof=totoc % If enabled add lists to TOC
% ,bilingual
,monolingual
% ,invert-title % Invert the BFH colors
]{bfhthesis} % KOMA-script report
\input{ads/header.tex}
\begin{document}
\frontmatter
\input{variable.sty}
%---------------- BFH tile page -----------------------------------------
\maketitle
% Abstract
\input{ads/abstract}
%------------ TABLEOFCONTENTS ----------------
\tableofcontents
%------------ START MAIN PART ----------------
\mainmatter
%------------ Introduction
\input{content/1_introduction.tex}
%------------ Project management
% \input{content/2_project_management.tex}
%------------ Preliminaries
\input{content/3_preliminaries.tex}
%------------ Execution
\input{content/4_execution.tex}
%------------ Discussion
\input{content/5_discussion.tex}
%------------ Conclusion
\input{content/6_conclusion.tex}
%------------ Eidesstattliche Erklärung
\includepdf[pages=1]{ads/Erklaerung}
%------------ History
% \input{ads/history}
%------------ Abbildungsverzeichnis
%\cleardoublepage
\listoffigures
%------------ Tabellenverzeichnis
%\cleardoublepage
\listoftables
% TODO
%------------ Quellcodeverzeichnis
% \cleardoublepage
\phantomsection \label{listoflist}
\addcontentsline{toc}{chapter}{List of listings}
\lstlistoflistings
%------------ Literaturverzeichnis
%\cleardoublepage
\printbibliography
%------------ Abkürzungsverzeichnis
%\cleardoublepage
\phantomsection \label{listofacs}
\addcontentsline{toc}{chapter}{Abbreviations}
\input{ads/abbreviation}
%------------ Glossar
\printglossary[style=altlist,title=Glossary]
%------------ Anhang
\input{content/appendix.tex}
\end{document}

15
doc/cs/variable.sty Normal file
View File

@ -0,0 +1,15 @@
\title{Adding Schnorr's Blind Signature in Taler}
\subtitle{An improved Taler Blind Signature System}
\author{{Gian Demarmels}, {Lucien Heuzeveldt}}
\institution{Bern University of Applied Science}
\department{Engineering and Computer Sciences}
\institute{Institute for Cybersecurity and Engineering ICE}
\version{1.0}
\titlegraphic{\includegraphics[width=\width]{logo-2021.png}}
\advisor{Prof. Dr. Emmanuel Benoist}
\expert{Elektronikingenieur HTL Daniel Voisard}
\degreeprogram{Bachelor of Science in Computer Science}
\setupSignature{
G. Demarmels={\includegraphics[width=\linewidth]{logo-2021.png}},
L. Heuzeveldt={\includegraphics[width=\linewidth]{bfh_logo.png}\vskip-\baselineskip}
}

View File

@ -89,13 +89,6 @@ static struct GNUNET_SIGNAL_Context *sig_int;
static struct GNUNET_SIGNAL_Context *sig_term;
/**
* Test if the audit should be aborted because the user
* pressed CTRL-C.
*
* @return false to continue the audit, true to terminate
* cleanly as soon as possible
*/
bool
TALER_ARL_do_abort (void)
{
@ -103,12 +96,6 @@ TALER_ARL_do_abort (void)
}
/**
* Add @a object to the report @a array. Fail hard if this fails.
*
* @param array report array to append @a object to
* @param object object to append, should be check that it is not NULL
*/
void
TALER_ARL_report (json_t *array,
json_t *object)
@ -183,14 +170,6 @@ add_denomination (
}
/**
* Obtain information about a @a denom_pub.
*
* @param dh hash of the denomination public key to look up
* @param[out] issue set to detailed information about @a denom_pub, NULL if not found, must
* NOT be freed by caller
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
TALER_ARL_get_denomination_info_by_hash (
const struct TALER_DenominationHash *dh,
@ -262,15 +241,6 @@ TALER_ARL_get_denomination_info_by_hash (
}
/**
* Obtain information about a @a denom_pub.
*
* @param denom_pub key to look up
* @param[out] issue set to detailed information about @a denom_pub, NULL if not found, must
* NOT be freed by caller
* @param[out] dh set to the hash of @a denom_pub, may be NULL
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
TALER_ARL_get_denomination_info (
const struct TALER_DenominationPublicKey *denom_pub,
@ -298,7 +268,7 @@ TALER_ARL_get_denomination_info (
* #GNUNET_NO if we had an error on commit (retry may help)
* #GNUNET_SYSERR on hard errors
*/
static int
static enum GNUNET_GenericReturnValue
transact (TALER_ARL_Analysis analysis,
void *analysis_cls)
{
@ -369,14 +339,7 @@ transact (TALER_ARL_Analysis analysis,
}
/**
* Initialize DB sessions and run the analysis.
*
* @param ana analysis to run
* @param ana_cls closure for @a ana
* @return #GNUNET_OK on success
*/
int
enum GNUNET_GenericReturnValue
TALER_ARL_setup_sessions_and_run (TALER_ARL_Analysis ana,
void *ana_cls)
{
@ -424,19 +387,6 @@ test_master_present (void *cls,
}
/**
* Perform addition of amounts. If the addition fails, logs
* a detailed error and calls exit() to terminate the process (!).
*
* Do not call this function directly, use #TALER_ARL_amount_add().
*
* @param[out] sum where to store @a a1 + @a a2, set to "invalid" on overflow
* @param a1 first amount to add
* @param a2 second amount to add
* @param filename where is the addition called
* @param functionname name of the function where the addition is called
* @param line line number of the addition
*/
void
TALER_ARL_amount_add_ (struct TALER_Amount *sum,
const struct TALER_Amount *a1,
@ -485,19 +435,6 @@ TALER_ARL_amount_add_ (struct TALER_Amount *sum,
}
/**
* Perform subtraction of amounts. If the subtraction fails, logs
* a detailed error and calls exit() to terminate the process (!).
*
* Do not call this function directly, use #TALER_ARL_amount_subtract().
*
* @param[out] diff where to store (@a a1 - @a a2)
* @param a1 amount to subtract from
* @param a2 amount to subtract
* @param filename where is the addition called
* @param functionname name of the function where the addition is called
* @param line line number of the addition
*/
void
TALER_ARL_amount_subtract_ (struct TALER_Amount *diff,
const struct TALER_Amount *a1,
@ -546,24 +483,6 @@ TALER_ARL_amount_subtract_ (struct TALER_Amount *diff,
}
/**
* Perform subtraction of amounts. Negative results should be signalled by the
* return value (leaving @a diff set to 'invalid'). If the subtraction fails
* for other reasons (currency mismatch, normalization failure), logs a
* detailed error and calls exit() to terminate the process (!).
*
* Do not call this function directly, use #TALER_ARL_amount_subtract_neg().
*
* @param[out] diff where to store (@a a1 - @a a2)
* @param a1 amount to subtract from
* @param a2 amount to subtract
* @param filename where is the addition called
* @param functionname name of the function where the addition is called
* @param line line number of the addition
* @return #TALER_ARL_SR_INVALID_NEGATIVE if the result was negative (and @a diff is now invalid),
* #TALER_ARL_SR_ZERO if the result was zero,
* #TALER_ARL_SR_POSITIVE if the result is positive
*/
enum TALER_ARL_SubtractionResult
TALER_ARL_amount_subtract_neg_ (struct TALER_Amount *diff,
const struct TALER_Amount *a1,
@ -622,13 +541,7 @@ handle_sigint (void)
}
/**
* Setup global variables based on configuration.
*
* @param c configuration to use
* @return #GNUNET_OK on success
*/
int
enum GNUNET_GenericReturnValue
TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c)
{
TALER_ARL_cfg = c;
@ -822,11 +735,6 @@ TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c)
}
/**
* Generate the report and close connectios to the database.
*
* @param report the report to output, may be NULL for no report
*/
void
TALER_ARL_done (json_t *report)
{

View File

@ -276,7 +276,7 @@ TALER_ARL_amount_subtract_neg_ (struct TALER_Amount *diff,
* @param ana_cls closure for @a ana
* @return #GNUNET_OK on success
*/
int
enum GNUNET_GenericReturnValue
TALER_ARL_setup_sessions_and_run (TALER_ARL_Analysis ana,
void *ana_cls);
@ -298,7 +298,7 @@ TALER_ARL_do_abort (void);
* @param c configuration to use
* @return #GNUNET_OK on success
*/
int
enum GNUNET_GenericReturnValue
TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c);

View File

@ -221,20 +221,6 @@ verify_and_execute_deposit_confirmation (
}
/**
* Handle a "/deposit-confirmation" request. Parses the JSON, and, if
* successful, passes the JSON data to #verify_and_execute_deposit_confirmation()
* to further check the details of the operation specified. If
* everything checks out, this will ultimately lead to the "/deposit-confirmation"
* being stored in the database.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
MHD_RESULT
TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
struct MHD_Connection *connection,
@ -323,9 +309,6 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
}
/**
* Initialize subsystem.
*/
void
TEAH_DEPOSIT_CONFIRMATION_init (void)
{
@ -335,9 +318,6 @@ TEAH_DEPOSIT_CONFIRMATION_init (void)
}
/**
* Shut down subsystem.
*/
void
TEAH_DEPOSIT_CONFIRMATION_done (void)
{

View File

@ -1086,9 +1086,6 @@ struct RevealContext
* @param cls closure with a `struct RevealContext *` in it
* @param num_freshcoins size of the @a rrcs array
* @param rrcs array of @a num_freshcoins information about coins to be created
* @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
* @param tprivs array of @e num_tprivs transfer private keys
* @param tp transfer public key information
*/
static void
reveal_data_cb (void *cls,

View File

@ -508,12 +508,7 @@ handle_reserve_out (void *cls,
struct GNUNET_TIME_Timestamp valid_start;
struct GNUNET_TIME_Timestamp expire_withdraw;
enum GNUNET_DB_QueryStatus qs;
struct TALER_WithdrawRequestPS wsrd = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW),
.purpose.size = htonl (sizeof (wsrd)),
.reserve_pub = *reserve_pub,
.h_coin_envelope = *h_blind_ev
};
struct TALER_DenominationHash h_denom_pub;
/* should be monotonically increasing */
GNUNET_assert (rowid >= ppr.last_reserve_out_serial_id);
@ -523,7 +518,7 @@ handle_reserve_out (void *cls,
initializes wsrd.h_denomination_pub! */
qs = TALER_ARL_get_denomination_info (denom_pub,
&issue,
&wsrd.h_denomination_pub);
&h_denom_pub);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
@ -569,17 +564,16 @@ handle_reserve_out (void *cls,
GNUNET_JSON_pack_data_auto ("reserve_pub",
reserve_pub),
GNUNET_JSON_pack_data_auto ("denompub_h",
&wsrd.h_denomination_pub)));
&h_denom_pub)));
}
/* check reserve_sig (first: setup remaining members of wsrd) */
TALER_amount_hton (&wsrd.amount_with_fee,
amount_with_fee);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
&wsrd,
&reserve_sig->eddsa_signature,
&reserve_pub->eddsa_pub))
TALER_wallet_withdraw_verify (&h_denom_pub,
amount_with_fee,
h_blind_ev,
reserve_pub,
reserve_sig))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (

View File

@ -24,12 +24,6 @@
#include <ltdl.h>
/**
* Initialize the plugin.
*
* @param cfg configuration to use
* @return #GNUNET_OK on success
*/
struct TALER_AUDITORDB_Plugin *
TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
@ -62,11 +56,6 @@ TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
}
/**
* Shutdown the plugin.
*
* @param plugin the plugin to unload
*/
void
TALER_AUDITORDB_plugin_unload (struct TALER_AUDITORDB_Plugin *plugin)
{

View File

@ -144,7 +144,7 @@ postgres_create_tables (void *cls)
* @param[in,out] pg the plugin-specific state
* @return #GNUNET_OK on success
*/
static int
static enum GNUNET_GenericReturnValue
setup_connection (struct PostgresClosure *pg)
{
struct GNUNET_PQ_PreparedStatement ps[] = {

View File

@ -65,18 +65,7 @@ struct WirePackP
GNUNET_NETWORK_STRUCT_END
/**
* Prepare for execution of a wire transfer from the exchange to some
* merchant.
*
* @param destination_account_payto_uri payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of this exchange (included in subject
* to facilitate use of tracking API by merchant backend)
* @param wtid wire transfer identifier to use
* @param[out] buf set to transfer data to persist, NULL on error
* @param[out] buf_size set to number of bytes in @a buf, 0 on error
*/
void
TALER_BANK_prepare_transfer (
const char *destination_account_payto_uri,
@ -247,17 +236,6 @@ handle_transfer_finished (void *cls,
}
/**
* Execute a wire transfer.
*
* @param ctx curl context for our event loop
* @param auth authentication data to authenticate with the bank
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
struct TALER_BANK_TransferHandle *
TALER_BANK_transfer (
struct GNUNET_CURL_Context *ctx,
@ -366,21 +344,6 @@ TALER_BANK_transfer (
}
/**
* Abort execution of a wire transfer. For example, because we are shutting
* down. Note that if an execution is aborted, it may or may not still
* succeed.
*
* The caller MUST run #TALER_BANK_transfer() again for the same request as
* soon as possible, to ensure that the request either ultimately succeeds or
* ultimately fails. Until this has been done, the transaction is in limbo
* (i.e. may or may not have been committed).
*
* This function cannot be used on a request handle if a response is already
* served for it.
*
* @param th the wire transfer request handle
*/
void
TALER_BANK_transfer_cancel (struct TALER_BANK_TransferHandle *th)
{

View File

@ -490,7 +490,7 @@ run (void *cls,
struct TALER_CoinPubHash c_hash;
struct TALER_PlanchetDetail pd;
struct TALER_BlindedDenominationSignature bds;
struct TALER_PlanchetSecretsP ps;
struct TALER_PlanchetMasterSecretP ps;
struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_CoinSpendPublicKeyP coin_pub;
union TALER_DenominationBlindingKeyP bks;
@ -542,6 +542,8 @@ run (void *cls,
TALER_denom_sig_unblind (&denom_sig,
&bds,
&bks,
&c_hash,
&alg_values,
&denom_pub));
TALER_blinded_denom_sig_free (&bds);
TALER_denom_pub_free (&denom_pub);

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file util/taler-crypto-worker.c
* @file exchange-tools/taler-crypto-worker.c
* @brief Standalone process to perform various cryptographic operations.
* @author Florian Dold
*/

View File

@ -2525,12 +2525,12 @@ do_download (char *const *args)
* Check that the security module keys are the same as before. If we had no
* keys in store before, remember them (Trust On First Use).
*
* @param secm security module keys, must be an array of length 2
* @param secmset security module keys
* @return #GNUNET_OK if keys match with what we have in store
* #GNUNET_NO if we had nothing in store but now do
* #GNUNET_SYSERR if keys changed from what we remember or other error
*/
static int
static enum GNUNET_GenericReturnValue
tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
{
char *fn;
@ -2603,6 +2603,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
"SECM_ESIGN_PUBKEY",
"key malformed");
GNUNET_free (key);
GNUNET_free (fn);
return GNUNET_SYSERR;
}
GNUNET_free (key);
@ -2612,6 +2613,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n");
GNUNET_free (fn);
return GNUNET_SYSERR;
}
}
@ -2634,6 +2636,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
"SECM_DENOM_PUBKEY",
"key malformed");
GNUNET_free (key);
GNUNET_free (fn);
return GNUNET_SYSERR;
}
GNUNET_free (key);
@ -2643,6 +2646,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n");
GNUNET_free (fn);
return GNUNET_SYSERR;
}
}
@ -2665,6 +2669,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
"SECM_DENOM_CS_PUBKEY",
"key malformed");
GNUNET_free (key);
GNUNET_free (fn);
return GNUNET_SYSERR;
}
GNUNET_free (key);
@ -2674,6 +2679,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n");
GNUNET_free (fn);
return GNUNET_SYSERR;
}
}
@ -2700,6 +2706,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
GNUNET_free (fn);
return GNUNET_SYSERR;
}
GNUNET_free (fn);
return GNUNET_NO;
}
@ -2796,13 +2803,12 @@ show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
/**
* Output @a denomkeys for human consumption.
*
* @param secm_pub security module public key used to sign the denominations
* element 0: RSA
* element 1: CS
* @param secm_pub_rsa security module public key used to sign the RSA denominations
* @param secm_pub_cs security module public key used to sign the CS denominations
* @param denomkeys keys to output
* @return #GNUNET_OK on success
*/
static int
static enum GNUNET_GenericReturnValue
show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
const json_t *denomkeys)
@ -3156,7 +3162,7 @@ do_show (char *const *args)
* @param[in,out] result array where to output the signatures
* @return #GNUNET_OK on success
*/
static int
static enum GNUNET_GenericReturnValue
sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
const json_t *signkeys,
json_t *result)
@ -3250,9 +3256,8 @@ sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
/**
* Sign @a denomkeys with offline key.
*
* @param secm_pub security module public key used to sign the denominations
* element 0: RSA
* element 1: CS
* @param secm_pub_rsa security module public key used to sign the RSA denominations
* @param secm_pub_cs security module public key used to sign the CS denominations
* @param denomkeys keys to output
* @param[in,out] result array where to output the signatures
* @return #GNUNET_OK on success

View File

@ -199,7 +199,7 @@ static int test_mode;
* Main work function that queries the DB and aggregates transactions
* into larger wire transfers.
*
* @param cls NULL
* @param cls a `struct Shard *`
*/
static void
run_aggregation (void *cls);
@ -679,12 +679,6 @@ release_shard (struct Shard *s)
}
/**
* Main work function that queries the DB and aggregates transactions
* into larger wire transfers.
*
* @param cls a `struct Shard *`
*/
static void
run_aggregation (void *cls)
{

View File

@ -188,7 +188,7 @@ static unsigned long long req_max;
struct GNUNET_CURL_Context *TEH_curl_ctx;
/**
* Context for integrating #exchange_curl_ctx with the
* Context for integrating #TEH_curl_ctx with the
* GNUnet event loop.
*/
static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;

View File

@ -39,8 +39,8 @@ TEH_handler_csr (struct TEH_RequestContext *rc,
{
unsigned int csr_requests_num;
json_t *csr_requests;
json_t *csr_response_ewvs;
json_t *csr_response;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("nks",
&csr_requests),
@ -58,18 +58,17 @@ TEH_handler_csr (struct TEH_RequestContext *rc,
res = TALER_MHD_parse_json_data (rc->connection,
root,
spec);
GNUNET_JSON_parse_free (spec);
if (GNUNET_OK != res)
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
csr_requests_num = json_array_size (csr_requests);
if (TALER_MAX_FRESH_COINS <= csr_requests_num)
{
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
rc->connection,
MHD_HTTP_BAD_REQUEST,
// FIXME: generalize error message
TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
NULL);
}
struct TALER_CsNonce nonces[GNUNET_NZL (csr_requests_num)];
@ -90,21 +89,27 @@ TEH_handler_csr (struct TEH_RequestContext *rc,
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_array (rc->connection,
root,
csr_requests,
csr_spec,
i,
-1);
if (GNUNET_OK != res)
{
GNUNET_JSON_parse_free (spec);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
}
GNUNET_JSON_parse_free (spec);
struct TALER_DenominationCSPublicRPairP r_pubs[GNUNET_NZL (csr_requests_num)];
struct TALER_ExchangeWithdrawValues ewvs[GNUNET_NZL (csr_requests_num)];
for (unsigned int i = 0; i < csr_requests_num; i++)
{
const struct TALER_CsNonce *nonce = &nonces[i];
const struct TALER_DenominationHash *denom_pub_hash = &denom_pub_hashes[i];
struct TALER_DenominationCSPublicRPairP *r_pub = &r_pubs[i];
struct TALER_DenominationCSPublicRPairP *r_pub
= &ewvs[i].details.cs_values;
ewvs[i].cipher = TALER_DENOMINATION_CS;
// check denomination referenced by denom_pub_hash
{
struct TEH_KeyStateHandle *ksh;
@ -179,27 +184,26 @@ TEH_handler_csr (struct TEH_RequestContext *rc,
}
// send response
csr_response = json_array ();
csr_response_ewvs = json_array ();
for (unsigned int i = 0; i < csr_requests_num; i++)
{
const struct TALER_DenominationCSPublicRPairP *r_pub = &r_pubs[i];
json_t *csr_obj;
csr_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_varsize ("r_pub_0",
&r_pub->r_pub[0],
sizeof(struct GNUNET_CRYPTO_CsRPublic)),
GNUNET_JSON_pack_data_varsize ("r_pub_1",
&r_pub->r_pub[1],
sizeof(struct GNUNET_CRYPTO_CsRPublic)));
TALER_JSON_pack_exchange_withdraw_values ("ewv",
&ewvs[i]));
GNUNET_assert (NULL != csr_obj);
GNUNET_assert (0 ==
json_array_append_new (csr_response,
json_array_append_new (csr_response_ewvs,
csr_obj));
}
return TALER_MHD_reply_json (rc->connection,
csr_response,
MHD_HTTP_OK);
csr_response = GNUNET_JSON_PACK (
GNUNET_JSON_pack_array_steal ("ewvs",
csr_response_ewvs));
GNUNET_assert (NULL != csr_response);
return TALER_MHD_reply_json_steal (rc->connection,
csr_response,
MHD_HTTP_OK);
}

View File

@ -51,6 +51,7 @@
* @param h_contract_terms hash of contract details
* @param exchange_timestamp exchange's timestamp
* @param refund_deadline until when this deposit be refunded
* @param wire_deadline until when will the exchange wire the funds
* @param merchant merchant public key
* @param amount_without_fee fraction of coin value to deposit, without the fee
* @return MHD result code

View File

@ -874,7 +874,6 @@ helper_rsa_cb (
GNUNET_STRINGS_relative_time_to_string (validity_duration,
GNUNET_NO));
key_generation++;
// FIXME: wait for sync?
TEH_resume_keys_requests (false);
hd = GNUNET_CONTAINER_multihashmap_get (hs->rsa_keys,
&h_rsa->hash);
@ -956,7 +955,6 @@ helper_cs_cb (
GNUNET_STRINGS_relative_time_to_string (validity_duration,
GNUNET_NO));
key_generation++;
// FIXME: wait for sync?
TEH_resume_keys_requests (false);
hd = GNUNET_CONTAINER_multihashmap_get (hs->cs_keys,
&h_cs->hash);
@ -1916,6 +1914,10 @@ create_krd (struct TEH_KeyStateHandle *ksh,
r = json_object_update (keys, sig);
GNUNET_assert (0 == r);
}
else
{
json_decref (extensions);
}
// Special case for age restrictions: if enabled, provide the lits of
// age-restricted denominations.
@ -2409,52 +2411,40 @@ TEH_keys_denomination_by_hash2 (
}
struct TALER_BlindedDenominationSignature
enum TALER_ErrorCode
TEH_keys_denomination_sign (const struct TALER_DenominationHash *h_denom_pub,
const struct TEH_SignDetails *msg,
enum TALER_ErrorCode *ec)
const struct TALER_BlindedPlanchet *bp,
struct TALER_BlindedDenominationSignature *bs)
{
struct TEH_KeyStateHandle *ksh;
struct TALER_BlindedDenominationSignature none;
struct HelperDenomination *hd;
memset (&none,
0,
sizeof (none));
ksh = TEH_keys_get_state ();
if (NULL == ksh)
{
*ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
return none;
}
return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
&h_denom_pub->hash);
if (NULL == hd)
{
*ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
return none;
}
if (msg->cipher != hd->denom_pub.cipher)
{
*ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
return none;
}
return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
if (bp->cipher != hd->denom_pub.cipher)
return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
switch (hd->denom_pub.cipher)
{
case TALER_DENOMINATION_RSA:
return TALER_CRYPTO_helper_rsa_sign (ksh->helpers->rsadh,
&hd->h_details.h_rsa,
msg->details.rsa_message.msg,
msg->details.rsa_message.msg_size,
ec);
return TALER_CRYPTO_helper_rsa_sign (
ksh->helpers->rsadh,
&hd->h_details.h_rsa,
bp->details.rsa_blinded_planchet.blinded_msg,
bp->details.rsa_blinded_planchet.blinded_msg_size,
bs);
case TALER_DENOMINATION_CS:
return TALER_CRYPTO_helper_cs_sign (ksh->helpers->csdh,
&hd->h_details.h_cs,
&msg->details.cs_message,
ec);
return TALER_CRYPTO_helper_cs_sign (
ksh->helpers->csdh,
&hd->h_details.h_cs,
&bp->details.cs_blinded_planchet,
bs);
default:
*ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
return none;
return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
}
}
@ -2467,7 +2457,6 @@ TEH_keys_denomination_cs_r_pub (const struct
{
struct TEH_KeyStateHandle *ksh;
struct HelperDenomination *hd;
enum TALER_ErrorCode r_derive_ec;
ksh = TEH_keys_get_state ();
if (NULL == ksh)
@ -2485,11 +2474,10 @@ TEH_keys_denomination_cs_r_pub (const struct
return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
}
*r_pub = TALER_CRYPTO_helper_cs_r_derive (ksh->helpers->csdh,
&hd->h_details.h_cs,
nonce,
&r_derive_ec);
return r_derive_ec;
return TALER_CRYPTO_helper_cs_r_derive (ksh->helpers->csdh,
&hd->h_details.h_cs,
nonce,
r_pub);
}

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2020 Taler Systems SA
Copyright (C) 2020-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -82,42 +82,6 @@ struct TEH_DenominationKey
};
struct TEH_SignDetails_RSA
{
/**
* message to sign
*/
const void *msg;
/**
* number of bytes in msg
*/
size_t msg_size;
};
struct TEH_SignDetails
{
/**
* Cipher type of the message
*/
enum TALER_DenominationCipher cipher;
union
{
/**
* If we use #TALER_DENOMINATION_RSA in @a cipher.
*/
struct TEH_SignDetails_RSA rsa_message;
/**
* If we use #TALER_DENOMINATION_CS in @a cipher.
*/
struct TALER_BlindedCsPlanchet cs_message;
} details;
};
/**
* Snapshot of the (coin and signing) keys (including private keys) of
* the exchange. There can be multiple instances of this struct, as it is
@ -196,48 +160,46 @@ TEH_keys_denomination_by_hash (const struct TALER_DenominationHash *h_denom_pub,
* or NULL if @a h_denom_pub could not be found
*/
struct TEH_DenominationKey *
TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
const struct
TALER_DenominationHash *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret);
TEH_keys_denomination_by_hash2 (
struct TEH_KeyStateHandle *ksh,
const struct TALER_DenominationHash *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret);
/**
* Request to sign @a msg using the public key corresponding to
* @a h_denom_pub.
*
* @param h_denom_pub hash of the public key to use to sign
* @param msg message to sign
* @param msg_size number of bytes in @a msg
* @param[out] ec set to the error code (or #TALER_EC_NONE on success)
* @return signature, the value inside the structure will be NULL on failure,
* see @a ec for details about the failure
* @param bp blinded planchet to sign
* @param[out] bs set to the blind signature on success
* @return #TALER_EC_NONE on success
*/
struct TALER_BlindedDenominationSignature
enum TALER_ErrorCode
TEH_keys_denomination_sign (const struct TALER_DenominationHash *h_denom_pub,
const struct TEH_SignDetails *msg,
enum TALER_ErrorCode *ec);
const struct TALER_BlindedPlanchet *bp,
struct TALER_BlindedDenominationSignature *bs);
/**
* Request to derive CS r_pub using the denomination corresponding to @a h_denom_pub
* Request to derive CS @a r_pub using the denomination corresponding to @a h_denom_pub
* and @a nonce.
*
* @param h_denom_pub hash of the public key to use to derive r_pub
* @param nonce withdraw/refresh nonce
* @param[out] ec set to the error code (or #TALER_EC_NONE on success)
* @return r_pub, the value inside the structure will be NULL on failure,
* see @a ec for details about the failure
* @param[out] r_pub where to write the result
* @return #TALER_EC_NONE on success
*/
enum TALER_ErrorCode
TEH_keys_denomination_cs_r_pub (const struct
TALER_DenominationHash *h_denom_pub,
const struct TALER_CsNonce *nonce,
struct TALER_DenominationCSPublicRPairP *r_pub);
TEH_keys_denomination_cs_r_pub (
const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_CsNonce *nonce,
struct TALER_DenominationCSPublicRPairP *r_pub);
/**
* Revoke the public key associated with @param h_denom_pub .
* Revoke the public key associated with @a h_denom_pub.
* This function should be called AFTER the database was
* updated, as it also triggers #TEH_keys_update_states().
*

View File

@ -29,7 +29,7 @@
* Handle a "/kyc-check" request. Checks the KYC
* status of the given account and returns it.
*
* @param connection request to handle
* @param rc details about the request to handle
* @param args one argument with the payment_target_uuid
* @return MHD result code
*/

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014-2019 Taler Systems SA
Copyright (C) 2014-2019, 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -86,8 +86,18 @@ handle_link_data (void *cls,
&pos->denom_pub),
TALER_JSON_pack_blinded_denom_sig ("ev_sig",
&pos->ev_sig),
GNUNET_JSON_pack_uint64 ("coin_idx",
pos->coin_refresh_offset),
TALER_JSON_pack_exchange_withdraw_values ("ewv",
&pos->alg_values),
GNUNET_JSON_pack_data_auto ("link_sig",
&pos->orig_coin_link_sig));
&pos->orig_coin_link_sig),
GNUNET_JSON_pack_allow_null (
pos->have_nonce
? GNUNET_JSON_pack_data_auto ("cs_nonce",
&pos->nonce)
: GNUNET_JSON_pack_string ("cs_nonce",
NULL)));
if ( (NULL == obj) ||
(0 !=
json_array_append_new (list,

View File

@ -30,10 +30,10 @@
/**
* Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON
* components and then hands things of to #check_for_denomination_key() to
* components and then hands things of to #check_melt_valid() to
* validate the melted coins, the signature and execute the melt using
* handle_melt().
* melt_transaction().
*
* @param connection the MHD connection to handle
* @param coin_pub public key of the coin
* @param root uploaded JSON data

View File

@ -162,6 +162,8 @@ recoup_refresh_transaction (void *cls,
*
* @param connection the MHD connection to handle
* @param coin information about the coin
* @param exchange_vals values contributed by the exchange
* during refresh
* @param coin_bks blinding data of the coin (to be checked)
* @param coin_sig signature of the coin
* @return MHD result code
@ -170,7 +172,9 @@ static MHD_RESULT
verify_and_execute_recoup_refresh (
struct MHD_Connection *connection,
const struct TALER_CoinPublicInfo *coin,
const struct TALER_ExchangeWithdrawValues *exchange_vals,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_CsNonce *nonce,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
struct RecoupContext pc;
@ -249,7 +253,7 @@ verify_and_execute_recoup_refresh (
coin_bks,
NULL, /* FIXME-Oec: TALER_AgeHash * */
&coin->coin_pub,
NULL, /* FIXME: Implement CS */
exchange_vals,
&c_hash,
&blinded_planchet))
{
@ -260,6 +264,9 @@ verify_and_execute_recoup_refresh (
TALER_EC_EXCHANGE_RECOUP_REFRESH_BLINDING_FAILED,
NULL);
}
if (TALER_DENOMINATION_CS == blinded_planchet.cipher)
blinded_planchet.details.cs_blinded_planchet.nonce
= *nonce;
TALER_coin_ev_hash (&blinded_planchet,
&coin->denom_pub_hash,
&h_blind);
@ -356,21 +363,31 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection,
struct TALER_CoinPublicInfo coin;
union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_ExchangeWithdrawValues exchange_vals;
struct TALER_CsNonce nonce;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&coin.denom_pub_hash),
TALER_JSON_spec_denom_sig ("denom_sig",
&coin.denom_sig),
TALER_JSON_spec_exchange_withdraw_values ("ewv",
&exchange_vals),
GNUNET_JSON_spec_fixed_auto ("coin_blind_key_secret",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
&nonce)),
GNUNET_JSON_spec_end ()
};
memset (&coin,
0,
sizeof (coin));
memset (&nonce,
0,
sizeof (nonce));
coin.coin_pub = *coin_pub;
ret = TALER_MHD_parse_json_data (connection,
root,
@ -384,7 +401,9 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection,
res = verify_and_execute_recoup_refresh (connection,
&coin,
&exchange_vals,
&coin_bks,
&nonce,
&coin_sig);
GNUNET_JSON_parse_free (spec);
return res;

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-exchange-httpd_recoup_refresh.h
* @file taler-exchange-httpd_recoup-refresh.h
* @brief Handle /recoup-refresh requests
* @author Christian Grothoff
*/

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2017-2021 Taler Systems SA
Copyright (C) 2017-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -40,9 +40,9 @@
struct RecoupContext
{
/**
* Hash of the blinded coin.
* Hash identifying the withdraw request.
*/
struct TALER_BlindedCoinHash h_blind;
struct TALER_WithdrawIdentificationHash wih;
/**
* Set by #recoup_transaction() to the reserve that will
@ -165,6 +165,8 @@ recoup_transaction (void *cls,
*
* @param connection the MHD connection to handle
* @param coin information about the coin
* @param exchange_vals values contributed by the exchange
* during withdrawal
* @param coin_bks blinding data of the coin (to be checked)
* @param coin_sig signature of the coin
* @return MHD result code
@ -173,7 +175,9 @@ static MHD_RESULT
verify_and_execute_recoup (
struct MHD_Connection *connection,
const struct TALER_CoinPublicInfo *coin,
const struct TALER_ExchangeWithdrawValues *exchange_vals,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_CsNonce *nonce,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
struct RecoupContext pc;
@ -242,6 +246,9 @@ verify_and_execute_recoup (
NULL);
}
/* re-compute client-side blinding so we can
(a bit later) check that this coin was indeed
signed by us. */
{
struct TALER_CoinPubHash c_hash;
struct TALER_BlindedPlanchet blinded_planchet;
@ -251,7 +258,7 @@ verify_and_execute_recoup (
coin_bks,
NULL, /* FIXME-Oec: TALER_AgeHash * */
&coin->coin_pub,
NULL, /* FIXME: handle CS */
exchange_vals,
&c_hash,
&blinded_planchet))
{
@ -262,9 +269,13 @@ verify_and_execute_recoup (
TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
NULL);
}
if (GNUNET_OK != TALER_coin_ev_hash (&blinded_planchet,
&coin->denom_pub_hash,
&pc.h_blind))
if (TALER_DENOMINATION_CS == blinded_planchet.cipher)
blinded_planchet.details.cs_blinded_planchet.nonce
= *nonce;
if (GNUNET_OK !=
TALER_withdraw_request_hash (&blinded_planchet,
&coin->denom_pub_hash,
&pc.wih))
{
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
@ -297,10 +308,10 @@ verify_and_execute_recoup (
{
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
&pc.h_blind,
&pc.reserve_pub,
&pc.reserve_out_serial_id);
qs = TEH_plugin->get_reserve_by_wih (TEH_plugin->cls,
&pc.wih,
&pc.reserve_pub,
&pc.reserve_out_serial_id);
if (0 > qs)
{
GNUNET_break (0);
@ -308,13 +319,13 @@ verify_and_execute_recoup (
connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_reserve_by_h_blind");
"get_reserve_by_wih");
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Recoup requested for unknown envelope %s\n",
GNUNET_h2s (&pc.h_blind.hash));
GNUNET_h2s (&pc.wih.hash));
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_NOT_FOUND,
@ -365,21 +376,31 @@ TEH_handler_recoup (struct MHD_Connection *connection,
struct TALER_CoinPublicInfo coin;
union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_ExchangeWithdrawValues exchange_vals;
struct TALER_CsNonce nonce;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&coin.denom_pub_hash),
TALER_JSON_spec_denom_sig ("denom_sig",
&coin.denom_sig),
TALER_JSON_spec_exchange_withdraw_values ("ewv",
&exchange_vals),
GNUNET_JSON_spec_fixed_auto ("coin_blind_key_secret",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
&nonce)),
GNUNET_JSON_spec_end ()
};
memset (&coin,
0,
sizeof (coin));
memset (&nonce,
0,
sizeof (nonce));
coin.coin_pub = *coin_pub;
ret = TALER_MHD_parse_json_data (connection,
root,
@ -388,12 +409,17 @@ TEH_handler_recoup (struct MHD_Connection *connection,
return MHD_NO; /* hard failure */
if (GNUNET_NO == ret)
return MHD_YES; /* failure */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Recoup coin with BKS=%s\n",
TALER_B2S (&coin_bks));
{
MHD_RESULT res;
res = verify_and_execute_recoup (connection,
&coin,
&exchange_vals,
&coin_bks,
&nonce,
&coin_sig);
GNUNET_JSON_parse_free (spec);
return res;

View File

@ -36,7 +36,7 @@
*
* @param connection the connection to send the response to
* @param num_freshcoins number of new coins for which we reveal data
* @param sigs array of @a num_freshcoins signatures revealed
* @param rrcs array of @a num_freshcoins signatures revealed
* @return a MHD result code
*/
static MHD_RESULT
@ -102,6 +102,14 @@ struct RevealContext
*/
const struct TEH_DenominationKey **dks;
/**
* Array of information about fresh coins being revealed.
*/
/* FIXME: const would be nicer here, but we initalize
the 'alg_values' in the verification
routine; suboptimal to be fixed... */
struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs;
/**
* Envelopes to be signed.
*/
@ -136,6 +144,79 @@ check_commitment (struct RevealContext *rctx,
struct MHD_Connection *connection,
MHD_RESULT *mhd_ret)
{
struct TALER_CsNonce nonces[rctx->num_fresh_coins];
unsigned int aoff = 0;
for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
{
const struct TALER_DenominationPublicKey *dk = &rctx->dks[j]->denom_pub;
if (dk->cipher != rctx->rcds[j].blinded_planchet.cipher)
{
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
NULL);
return GNUNET_SYSERR;
}
switch (dk->cipher)
{
case TALER_DENOMINATION_INVALID:
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
NULL);
return GNUNET_SYSERR;
case TALER_DENOMINATION_RSA:
continue;
case TALER_DENOMINATION_CS:
nonces[aoff]
= rctx->rcds[j].blinded_planchet.details.cs_blinded_planchet.nonce;
aoff++;
break;
}
}
// OPTIMIZE: do this in batch later!
aoff = 0;
for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
{
const struct TALER_DenominationPublicKey *dk = &rctx->dks[j]->denom_pub;
struct TALER_ExchangeWithdrawValues *alg_values
= &rctx->rrcs[j].exchange_vals;
alg_values->cipher = dk->cipher;
switch (dk->cipher)
{
case TALER_DENOMINATION_INVALID:
GNUNET_assert (0);
return GNUNET_SYSERR;
case TALER_DENOMINATION_RSA:
continue;
case TALER_DENOMINATION_CS:
{
enum TALER_ErrorCode ec;
ec = TEH_keys_denomination_cs_r_pub (
&rctx->rrcs[j].h_denom_pub,
&nonces[aoff],
&alg_values->details.cs_values);
if (TALER_EC_NONE != ec)
{
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
ec,
NULL);
return GNUNET_SYSERR;
}
aoff++;
}
}
}
/* Verify commitment */
{
/* Note that the contents of rcs[melt.session.noreveal_index]
@ -171,35 +252,43 @@ check_commitment (struct RevealContext *rctx,
&ts);
rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
struct TALER_RefreshCoinData);
aoff = 0;
for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
{
const struct TALER_DenominationPublicKey *dk
= &rctx->dks[j]->denom_pub;
struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
struct TALER_CoinSpendPrivateKeyP coin_priv;
union TALER_DenominationBlindingKeyP bks;
struct TALER_ExchangeWithdrawValues alg_values;
const struct TALER_ExchangeWithdrawValues *alg_value
= &rctx->rrcs[j].exchange_vals;
struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash;
struct TALER_PlanchetSecretsP ps;
struct TALER_PlanchetMasterSecretP ps;
rcd->dk = &rctx->dks[j]->denom_pub;
TALER_transfer_secret_to_planchet_secret (&ts,
j,
&ps);
// TODO: implement cipher handling
alg_values.cipher = TALER_DENOMINATION_RSA;
TALER_planchet_setup_coin_priv (&ps,
&alg_values,
alg_value,
&coin_priv);
TALER_planchet_blinding_secret_create (&ps,
&alg_values,
alg_value,
&bks);
GNUNET_assert (GNUNET_OK ==
TALER_planchet_prepare (rcd->dk,
&alg_values,
alg_value,
&bks,
&coin_priv,
&c_hash,
&pd));
if (TALER_DENOMINATION_CS == dk->cipher)
{
pd.blinded_planchet.details.cs_blinded_planchet.nonce =
nonces[aoff];
aoff++;
}
rcd->blinded_planchet = pd.blinded_planchet;
}
}
@ -505,6 +594,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
}
rctx->dks = dks;
rctx->rcds = rcds;
rctx->rrcs = rrcs;
if (GNUNET_OK !=
check_commitment (rctx,
connection,
@ -517,20 +607,12 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
/* create fresh coin signatures */
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
{
enum TALER_ErrorCode ec = TALER_EC_NONE;
struct TEH_SignDetails sign_details;
const struct TALER_BlindedRsaPlanchet *rp;
enum TALER_ErrorCode ec;
// FIXME: implement cipher handling
rp = &rcds[i].blinded_planchet.details.rsa_blinded_planchet;
sign_details.cipher = TALER_DENOMINATION_RSA;
sign_details.details.rsa_message.msg = rp->blinded_msg;
sign_details.details.rsa_message.msg_size = rp->blinded_msg_size;
rrcs[i].coin_sig
= TEH_keys_denomination_sign (
&rrcs[i].h_denom_pub,
&sign_details,
&ec);
ec = TEH_keys_denomination_sign (
&rrcs[i].h_denom_pub,
&rcds[i].blinded_planchet,
&rrcs[i].coin_sig);
if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
@ -622,7 +704,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
NULL);
}

View File

@ -538,18 +538,6 @@ TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
}
/**
* Send proof that a request is invalid to client because of
* insufficient funds. This function will create a message with all
* of the operations affecting the coin that demonstrate that the coin
* has insufficient value.
*
* @param connection connection to the client
* @param ec error code to return
* @param coin_pub public key of the coin
* @param tl transaction list to use to build reply
* @return MHD result code
*/
MHD_RESULT
TEH_RESPONSE_reply_coin_insufficient_funds (
struct MHD_Connection *connection,

View File

@ -107,7 +107,7 @@ wire_update_event_cb (void *cls,
}
int
enum GNUNET_GenericReturnValue
TEH_wire_init ()
{
struct GNUNET_DB_EventHeaderP es = {
@ -147,8 +147,7 @@ TEH_wire_done ()
/**
* Create standard JSON response format using
* @param ec and @a detail
* Create standard JSON response format using @a ec and @a detail.
*
* @param ec error code to return
* @param detail optional detail text to return, can be NULL

View File

@ -90,10 +90,26 @@ reply_withdraw_insufficient_funds (
*/
struct WithdrawContext
{
/**
* Details about the withdrawal request.
* Hash that uniquely identifies the withdraw request.
*/
struct TALER_WithdrawRequestPS wsrd;
struct TALER_WithdrawIdentificationHash wih;
/**
* Hash of the (blinded) message to be signed by the Exchange.
*/
struct TALER_BlindedCoinHash h_coin_envelope;
/**
* Value of the coin being exchanged (matching the denomination key)
* plus the transaction fee. We include this in what is being
* signed so that we can verify a reserve's remaining total balance
* without needing to access the respective denomination key
* information each time.
*/
struct TALER_Amount amount_with_fee;
/**
* Blinded planchet.
@ -143,9 +159,8 @@ withdraw_transaction (void *cls,
uint64_t ruuid;
now = GNUNET_TIME_timestamp_get ();
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
&wc->wih,
&wc->collectable,
now,
&found,
@ -173,7 +188,6 @@ withdraw_transaction (void *cls,
{
struct TALER_EXCHANGEDB_ReserveHistory *rh;
struct TALER_Amount balance;
struct TALER_Amount requested_amount;
TEH_plugin->rollback (TEH_plugin->cls);
// FIXME: maybe start read-committed here?
@ -192,7 +206,7 @@ withdraw_transaction (void *cls,
/* The reserve does not have the required amount (actual
* amount + withdraw fee) */
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
&wc->wsrd.reserve_pub,
&wc->collectable.reserve_pub,
&balance,
&rh);
if (NULL == rh)
@ -204,12 +218,11 @@ withdraw_transaction (void *cls,
"reserve history");
return GNUNET_DB_STATUS_HARD_ERROR;
}
TALER_amount_ntoh (&requested_amount,
&wc->wsrd.amount_with_fee);
*mhd_ret = reply_withdraw_insufficient_funds (connection,
&balance,
&requested_amount,
rh);
*mhd_ret = reply_withdraw_insufficient_funds (
connection,
&balance,
&wc->collectable.amount_with_fee,
rh);
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
return GNUNET_DB_STATUS_HARD_ERROR;
@ -287,7 +300,7 @@ check_request_idempotent (struct TEH_RequestContext *rc,
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
&wc->wsrd.h_coin_envelope,
&wc->wih,
&wc->collectable);
if (0 > qs)
{
@ -336,8 +349,8 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&wc.wsrd.reserve_pub,
sizeof (wc.wsrd.reserve_pub)))
&wc.collectable.reserve_pub,
sizeof (wc.collectable.reserve_pub)))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
@ -460,20 +473,11 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
NULL);
}
TALER_amount_hton (&wc.wsrd.amount_with_fee,
&wc.collectable.amount_with_fee);
// FIXME: move this logic into libtalerutil!
/* verify signature! */
wc.wsrd.purpose.size
= htonl (sizeof (wc.wsrd));
wc.wsrd.purpose.purpose
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
wc.wsrd.h_denomination_pub
= wc.collectable.denom_pub_hash;
if (GNUNET_OK != TALER_coin_ev_hash (&wc.blinded_planchet,
&wc.collectable.denom_pub_hash,
&wc.wsrd.h_coin_envelope))
if (GNUNET_OK !=
TALER_coin_ev_hash (&wc.blinded_planchet,
&wc.collectable.denom_pub_hash,
&wc.collectable.h_coin_envelope))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
@ -482,15 +486,15 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
NULL);
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (
TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
&wc.wsrd,
&wc.collectable.reserve_sig.eddsa_signature,
&wc.wsrd.reserve_pub.eddsa_pub))
TALER_wallet_withdraw_verify (&wc.collectable.denom_pub_hash,
&wc.collectable.amount_with_fee,
&wc.collectable.h_coin_envelope,
&wc.collectable.reserve_pub,
&wc.collectable.reserve_sig))
{
TALER_LOG_WARNING (
"Client supplied invalid signature for withdraw request\n");
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_FORBIDDEN,
@ -498,38 +502,24 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
NULL);
}
// TODO: if CS: check nonce for reuse
if (GNUNET_OK !=
TALER_withdraw_request_hash (&wc.blinded_planchet,
&wc.collectable.denom_pub_hash,
&wc.wih))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
NULL);
}
/* Sign before transaction! */
ec = TALER_EC_NONE;
{
struct TEH_SignDetails sign_details;
sign_details.cipher = wc.blinded_planchet.cipher;
switch (wc.blinded_planchet.cipher)
{
case TALER_DENOMINATION_RSA:
sign_details.details.rsa_message.msg =
wc.blinded_planchet.details.rsa_blinded_planchet.blinded_msg;
sign_details.details.rsa_message.msg_size =
wc.blinded_planchet.details.rsa_blinded_planchet.blinded_msg_size;
break;
case TALER_DENOMINATION_CS:
sign_details.details.cs_message =
wc.blinded_planchet.details.cs_blinded_planchet;
break;
default:
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
NULL);
}
wc.collectable.sig = TEH_keys_denomination_sign (
&wc.collectable.denom_pub_hash,
&sign_details,
&ec);
}
ec = TEH_keys_denomination_sign (
&wc.collectable.denom_pub_hash,
&wc.blinded_planchet,
&wc.collectable.sig);
if (TALER_EC_NONE != ec)
{
GNUNET_break (0);

View File

@ -196,7 +196,8 @@ CREATE INDEX IF NOT EXISTS reserves_close_by_reserve_pub_index
CREATE TABLE IF NOT EXISTS reserves_out
(reserve_out_serial_id BIGSERIAL -- UNIQUE
,h_blind_ev BYTEA PRIMARY KEY CHECK (LENGTH(h_blind_ev)=64)
,wih BYTEA PRIMARY KEY CHECK (LENGTH(wih)=64)
,h_blind_ev BYTEA CHECK (LENGTH(h_blind_ev)=64) -- UNIQUE
,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial)
,denom_sig BYTEA NOT NULL
,reserve_uuid INT8 NOT NULL -- REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
@ -205,9 +206,11 @@ CREATE TABLE IF NOT EXISTS reserves_out
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
)
PARTITION BY HASH (h_blind_ev);
PARTITION BY HASH (wih);
COMMENT ON TABLE reserves_out
IS 'Withdraw operations performed on reserves.';
COMMENT ON COLUMN reserves_out.wih
IS 'Hash that uniquely identifies the withdraw request. Used to detect request replays (crucial for CS) and to check the withdraw existed during recoup.';
COMMENT ON COLUMN reserves_out.h_blind_ev
IS 'Hash of the blinded coin, used as primary key here so that broken clients that use a non-random coin or blinding factor fail to withdraw (otherwise they would fail on deposit when the coin is not unique there).';
COMMENT ON COLUMN reserves_out.denominations_serial
@ -377,6 +380,7 @@ CREATE TABLE IF NOT EXISTS refresh_revealed_coins
,coin_ev BYTEA NOT NULL -- UNIQUE
,h_coin_ev BYTEA NOT NULL CHECK(LENGTH(h_coin_ev)=64) -- UNIQUE
,ev_sig BYTEA NOT NULL
,ewv BYTEA NOT NULL
-- ,PRIMARY KEY (melt_serial_id, freshcoin_index) -- done per shard
)
PARTITION BY HASH (melt_serial_id);
@ -390,6 +394,8 @@ COMMENT ON COLUMN refresh_revealed_coins.freshcoin_index
IS 'index of the fresh coin being created (one melt operation may result in multiple fresh coins)';
COMMENT ON COLUMN refresh_revealed_coins.coin_ev
IS 'envelope of the new coin to be signed';
COMMENT ON COLUMN refresh_revealed_coins.ewv
IS 'exchange contributed values in the creation of the fresh coin (see /csr)';
COMMENT ON COLUMN refresh_revealed_coins.h_coin_ev
IS 'hash of the envelope of the new coin to be signed (for lookups)';
COMMENT ON COLUMN refresh_revealed_coins.ev_sig
@ -634,7 +640,7 @@ COMMENT ON TABLE recoup
COMMENT ON COLUMN recoup.known_coin_id
IS 'Coin that is being debited in the recoup. Do not CASCADE ON DROP on the coin_pub, as we may keep the coin alive!';
COMMENT ON COLUMN recoup.reserve_out_serial_id
IS 'Identifies the h_blind_ev of the recouped coin and provides the link to the credited reserve.';
IS 'Identifies the wih of the recouped coin and provides the link to the credited reserve.';
COMMENT ON COLUMN recoup.coin_sig
IS 'Signature by the coin affirming the recoup, of type TALER_SIGNATURE_WALLET_COIN_RECOUP';
COMMENT ON COLUMN recoup.coin_blind
@ -809,6 +815,7 @@ CREATE INDEX IF NOT EXISTS revolving_work_shards_by_job_name_active_last_attempt
CREATE OR REPLACE FUNCTION exchange_do_withdraw(
IN in_wih BYTEA,
IN amount_val INT8,
IN amount_frac INT4,
IN h_denom_pub BYTEA,
@ -822,7 +829,8 @@ CREATE OR REPLACE FUNCTION exchange_do_withdraw(
OUT balance_ok BOOLEAN,
OUT kycok BOOLEAN,
OUT account_uuid INT8,
OUT ruuid INT8)
OUT ruuid INT8,
OUT out_denom_sig BYTEA)
LANGUAGE plpgsql
AS $$
DECLARE
@ -835,7 +843,7 @@ DECLARE
reserve_frac INT4;
BEGIN
-- Shards: reserves by reserve_pub (SELECT)
-- reserves_out (INSERT, with CONFLICT detection) by h_blind_ev
-- reserves_out (INSERT, with CONFLICT detection) by wih
-- reserves by reserve_pub (UPDATE)
-- reserves_in by reserve_pub (SELECT)
-- wire_targets by wire_target_serial_id
@ -884,6 +892,7 @@ END IF;
-- the query successful due to idempotency.
INSERT INTO reserves_out
(h_blind_ev
,wih
,denominations_serial
,denom_sig
,reserve_uuid
@ -893,6 +902,7 @@ INSERT INTO reserves_out
,amount_with_fee_frac)
VALUES
(h_coin_envelope
,in_wih
,denom_serial
,denom_sig
,ruuid
@ -905,6 +915,25 @@ ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
-- idempotent query, all constraints must be satisfied
SELECT
denom_sig
INTO
out_denom_sig
FROM reserves_in
WHERE wih=in_wih
LIMIT 1; -- limit 1 should not be required (without p2p transfers)
IF NOT FOUND
THEN
reserve_found=FALSE;
balance_ok=FALSE;
kycok=FALSE;
account_uuid=0;
ruuid=0;
ASSERT false, 'internal logic error';
END IF;
reserve_found=TRUE;
balance_ok=TRUE;
kycok=TRUE;
@ -964,9 +993,13 @@ SELECT
WHERE reserve_pub=rpub
LIMIT 1; -- limit 1 should not be required (without p2p transfers)
-- Return denomination signature as result that
-- was given as the argument.
out_denom_sig=denom_sig;
END $$;
COMMENT ON FUNCTION exchange_do_withdraw(INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result';

View File

@ -22,17 +22,7 @@
#include "taler_exchangedb_lib.h"
/**
* Calculate the total value of all transactions performed.
* Stores @a off plus the cost of all transactions in @a tl
* in @a ret.
*
* @param tl transaction list to process
* @param off offset to use as the starting value
* @param[out] ret where the resulting total is to be stored (may alias @a off)
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
int
enum GNUNET_GenericReturnValue
TALER_EXCHANGEDB_calculate_transaction_list_totals (
struct TALER_EXCHANGEDB_TransactionList *tl,
const struct TALER_Amount *off,

View File

@ -440,6 +440,8 @@ irbt_cb_table_refresh_revealed_coins (
GNUNET_PQ_query_param_auto_from_type (&h_coin_ev),
TALER_PQ_query_param_blinded_denom_sig (
&td->details.refresh_revealed_coins.ev_sig),
TALER_PQ_query_param_exchange_withdraw_values (
&td->details.refresh_revealed_coins.ewv),
GNUNET_PQ_query_param_uint64 (
&td->details.refresh_revealed_coins.denominations_serial),
GNUNET_PQ_query_param_uint64 (

View File

@ -790,6 +790,9 @@ lrbt_cb_table_refresh_revealed_coins (void *cls,
TALER_PQ_result_spec_blinded_denom_sig (
"ev_sig",
&td.details.refresh_revealed_coins.ev_sig),
TALER_PQ_result_spec_exchange_withdraw_values (
"ewv",
&td.details.refresh_revealed_coins.ewv),
GNUNET_PQ_result_spec_uint64 (
"denominations_serial",
&td.details.refresh_revealed_coins.denominations_serial),

View File

@ -560,13 +560,6 @@ prepare_statements (struct PostgresClosure *pg)
" ON (wire_source_serial_id = wire_target_serial_id)"
" WHERE reserve_pub=$1;",
1),
/* Lock withdraw table; NOTE: we may want to eventually shard the
deposit table to avoid this lock being the main point of
contention limiting transaction performance. */
GNUNET_PQ_make_prepare (
"lock_withdraw",
"LOCK TABLE reserves_out;",
0),
/* Used in #postgres_do_withdraw() to store
the signature of a blinded coin with the blinded coin's
details before returning it during /reserve/withdraw. We store
@ -582,9 +575,10 @@ prepare_statements (struct PostgresClosure *pg)
",kycok AS kyc_ok"
",account_uuid AS payment_target_uuid"
",ruuid"
",out_denom_sig"
" FROM exchange_do_withdraw"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
9),
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
10),
/* Used in #postgres_do_withdraw_limit_check() to check
if the withdrawals remain below the limit under which
KYC is not required. */
@ -659,6 +653,7 @@ prepare_statements (struct PostgresClosure *pg)
",reserve_sig"
",reserves.reserve_pub"
",execution_date"
",h_blind_ev"
",amount_with_fee_val"
",amount_with_fee_frac"
",denom.fee_withdraw_val"
@ -668,7 +663,7 @@ prepare_statements (struct PostgresClosure *pg)
" USING (reserve_uuid)"
" JOIN denominations denom"
" USING (denominations_serial)"
" WHERE h_blind_ev=$1;",
" WHERE wih=$1;",
1),
/* Used during #postgres_get_reserve_history() to
obtain all of the /reserve/withdraw operations that
@ -891,13 +886,14 @@ prepare_statements (struct PostgresClosure *pg)
",link_sig "
",denominations_serial "
",coin_ev"
",ewv"
",h_coin_ev"
",ev_sig"
") SELECT $1, $2, $3, "
" denominations_serial, $5, $6, $7"
" denominations_serial, $5, $6, $7, $8"
" FROM denominations"
" WHERE denom_pub_hash=$4;",
7),
8),
/* Obtain information about the coins created in a refresh
operation, used in #postgres_get_refresh_reveal() */
GNUNET_PQ_make_prepare (
@ -908,6 +904,7 @@ prepare_statements (struct PostgresClosure *pg)
",rrc.h_coin_ev"
",rrc.link_sig"
",rrc.coin_ev"
",rrc.ewv"
",rrc.ev_sig"
" FROM refresh_commitments"
" JOIN refresh_revealed_coins rrc"
@ -1213,7 +1210,10 @@ prepare_statements (struct PostgresClosure *pg)
" tp.transfer_pub"
",denoms.denom_pub"
",rrc.ev_sig"
",rrc.ewv"
",rrc.link_sig"
",rrc.freshcoin_index"
",rrc.coin_ev"
" FROM refresh_commitments"
" JOIN refresh_revealed_coins rrc"
" USING (melt_serial_id)"
@ -1666,16 +1666,16 @@ prepare_statements (struct PostgresClosure *pg)
" ON (denoms.denominations_serial = coins.denominations_serial)"
" WHERE coins.coin_pub=$1;",
1),
/* Used in #postgres_get_reserve_by_h_blind() */
/* Used in #postgres_get_reserve_by_wih() */
GNUNET_PQ_make_prepare (
"reserve_by_h_blind",
"reserve_by_wih",
"SELECT"
" reserves.reserve_pub"
",reserve_out_serial_id"
" FROM reserves_out"
" JOIN reserves"
" USING (reserve_uuid)"
" WHERE h_blind_ev=$1"
" WHERE wih=$1"
" LIMIT 1;",
1),
/* Used in #postgres_get_old_coin_by_h_blind() */
@ -2241,6 +2241,7 @@ prepare_statements (struct PostgresClosure *pg)
",link_sig"
",coin_ev"
",ev_sig"
",ewv"
",denominations_serial"
",melt_serial_id"
" FROM refresh_revealed_coins"
@ -2532,11 +2533,12 @@ prepare_statements (struct PostgresClosure *pg)
",coin_ev"
",h_coin_ev"
",ev_sig"
",ewv"
",denominations_serial"
",melt_serial_id"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8);",
8),
"($1, $2, $3, $4, $5, $6, $7, $8, $9);",
9),
GNUNET_PQ_make_prepare (
"insert_into_table_refresh_transfer_keys",
"INSERT INTO refresh_transfer_keys"
@ -3874,9 +3876,8 @@ postgres_select_kyc_status (void *cls,
* Get the KYC status for a wallet. If the status is unknown,
* inserts a new status record (hence INsertSELECT).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pg the plugin-specific state
* @param payto_uri the payto URI to check
* @param oauth_username user ID to store
* @param[out] kyc set to the KYC status of the wallet
* @return transaction status
*/
@ -4298,8 +4299,7 @@ postgres_reserves_in_insert (void *cls,
* key of the hash of the blinded message.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param h_blind hash of the blinded coin to be signed (will match
* `h_coin_envelope` in the @a collectable to be returned)
* @param wih hash that uniquely identifies the withdraw operation
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
* @return statement execution status
@ -4307,12 +4307,12 @@ postgres_reserves_in_insert (void *cls,
static enum GNUNET_DB_QueryStatus
postgres_get_withdraw_info (
void *cls,
const struct TALER_BlindedCoinHash *h_blind,
const struct TALER_WithdrawIdentificationHash *wih,
struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_blind),
GNUNET_PQ_query_param_auto_from_type (wih),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@ -4324,24 +4324,15 @@ postgres_get_withdraw_info (
&collectable->reserve_sig),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&collectable->reserve_pub),
GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
&collectable->h_coin_envelope),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&collectable->amount_with_fee),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
&collectable->withdraw_fee),
GNUNET_PQ_result_spec_end
};
#if EXPLICIT_LOCKS
struct GNUNET_PQ_QueryParam no_params[] = {
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"lock_withdraw",
no_params)))
return qs;
#endif
collectable->h_coin_envelope = *h_blind;
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_withdraw_info",
params,
@ -4354,19 +4345,20 @@ postgres_get_withdraw_info (
* and possibly persisting the withdrawal details.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
* @param wih hash that uniquely identifies the withdraw operation
* @param[in,out] collectable corresponding collectable coin (blind signature) if a coin is found; possibly updated if a (different) signature exists already
* @param now current time (rounded)
* @param[out] found set to true if the reserve was found
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] kyc_ok set to true if the kyc status of the reserve is satisfied
* @param[out] kyc set to true if the kyc status of the reserve is satisfied
* @param[out] ruuid set to the reserve's UUID (reserves table row)
* @return query execution status
*/
static enum GNUNET_DB_QueryStatus
postgres_do_withdraw (
void *cls,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
const struct TALER_WithdrawIdentificationHash *wih,
struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
struct GNUNET_TIME_Timestamp now,
bool *found,
bool *balance_ok,
@ -4376,6 +4368,7 @@ postgres_do_withdraw (
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Timestamp gc;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (wih),
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
@ -4386,6 +4379,9 @@ postgres_do_withdraw (
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
bool no_out_sig;
struct TALER_BlindedDenominationSignature out_sig;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("reserve_found",
found),
@ -4397,18 +4393,33 @@ postgres_do_withdraw (
&kyc->payment_target_uuid),
GNUNET_PQ_result_spec_uint64 ("ruuid",
ruuid),
GNUNET_PQ_result_spec_allow_null (
TALER_PQ_result_spec_blinded_denom_sig ("out_denom_sig",
&out_sig),
&no_out_sig),
GNUNET_PQ_result_spec_end
};
#if 0
memset (&out_sig,
0,
sizeof (out_sig));
#endif
gc = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (now.abs_time,
pg->legal_reserve_expiration_time));
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_withdraw",
params,
rs);
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_withdraw",
params,
rs);
if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
(! no_out_sig) )
{
TALER_blinded_denom_sig_free (&collectable->sig);
collectable->sig = out_sig;
}
return qs;
}
@ -4452,9 +4463,9 @@ postgres_do_withdraw_limit_check (
/**
* Compute the shard number of a given @a deposit
* Compute the shard number of a given @a merchant_pub.
*
* @param deposit deposit to compute shard for
* @param merchant_pub merchant public key to compute shard for
* @return shard number
*/
static uint64_t
@ -4484,11 +4495,11 @@ compute_shard (const struct TALER_MerchantPublicKeyP *merchant_pub)
* Perform deposit operation, checking for sufficient balance
* of the coin and possibly persisting the deposit details.
*
* FIXME: parameters missing in description!
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param deposit deposit operation details
* @param known_coin_id row of the coin in the known_coins table
* @param h_payto hash of the merchant's bank account details
* @param extension_blocked true if an extension is blocking the wire transfer
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] in_conflict set to true if the deposit conflicted
@ -6095,6 +6106,7 @@ postgres_insert_refresh_reveal (
GNUNET_PQ_query_param_auto_from_type (&rrc->orig_coin_link_sig),
GNUNET_PQ_query_param_auto_from_type (&rrc->h_denom_pub),
TALER_PQ_query_param_blinded_planchet (&rrc->blinded_planchet),
TALER_PQ_query_param_exchange_withdraw_values (&rrc->exchange_vals),
GNUNET_PQ_query_param_auto_from_type (&rrc->coin_envelope_hash),
TALER_PQ_query_param_blinded_denom_sig (&rrc->coin_sig),
GNUNET_PQ_query_param_end
@ -6203,6 +6215,8 @@ add_revealed_coins (void *cls,
&rrc->coin_envelope_hash),
TALER_PQ_result_spec_blinded_planchet ("coin_ev",
&rrc->blinded_planchet),
TALER_PQ_result_spec_exchange_withdraw_values ("ewv",
&rrc->exchange_vals),
TALER_PQ_result_spec_blinded_denom_sig ("ev_sig",
&rrc->coin_sig),
GNUNET_PQ_result_spec_end
@ -6377,6 +6391,7 @@ add_ldl (void *cls,
pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkList);
{
struct TALER_BlindedPlanchet bp;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
&transfer_pub),
@ -6384,8 +6399,14 @@ add_ldl (void *cls,
&pos->orig_coin_link_sig),
TALER_PQ_result_spec_blinded_denom_sig ("ev_sig",
&pos->ev_sig),
GNUNET_PQ_result_spec_uint32 ("freshcoin_index",
&pos->coin_refresh_offset),
TALER_PQ_result_spec_exchange_withdraw_values ("ewv",
&pos->alg_values),
TALER_PQ_result_spec_denom_pub ("denom_pub",
&pos->denom_pub),
TALER_PQ_result_spec_blinded_planchet ("coin_ev",
&bp),
GNUNET_PQ_result_spec_end
};
@ -6399,6 +6420,12 @@ add_ldl (void *cls,
ldctx->status = GNUNET_SYSERR;
return;
}
if (TALER_DENOMINATION_CS == bp.cipher)
{
pos->nonce = bp.details.cs_blinded_planchet.nonce;
pos->have_nonce = true;
}
TALER_blinded_planchet_free (&bp);
}
if ( (NULL != ldctx->last) &&
(0 == GNUNET_memcmp (&transfer_pub,
@ -7178,11 +7205,10 @@ postgres_lookup_wire_transfer (
* @param merchant_pub merchant public key
* @param[out] pending set to true if the transaction is still pending
* @param[out] wtid wire transfer identifier, only set if @a pending is false
* @param[out] coin_contribution how much did the coin we asked about
* contribute to the total transfer value? (deposit value including fee)
* @param[out] coin_fee how much did the exchange charge for the deposit fee
* @param[out] execution_time when was the transaction done, or
* @param[out] exec_time when was the transaction done, or
* when we expect it to be done (if @a pending is false)
* @param[out] amount_with_fee set to the total deposited amount
* @param[out] deposit_fee set to how much the exchange did charge for the deposit
* @param[out] kyc set to the kyc status of the receiver (if @a pending)
* @return transaction status code
*/
@ -9352,20 +9378,21 @@ postgres_select_reserve_closed_above_serial_id (
* from given the hash of the blinded coin.
*
* @param cls closure
* @param h_blind_ev hash of the blinded coin
* @param wih hash that uniquely identifies the withdraw request
* @param[out] reserve_pub set to information about the reserve (on success only)
* @param[out] reserve_out_serial_id set to row of the @a h_blind_ev in reserves_out
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
postgres_get_reserve_by_h_blind (void *cls,
const struct TALER_BlindedCoinHash *h_blind_ev,
struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t *reserve_out_serial_id)
postgres_get_reserve_by_wih (
void *cls,
const struct TALER_WithdrawIdentificationHash *wih,
struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t *reserve_out_serial_id)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
GNUNET_PQ_query_param_auto_from_type (wih),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@ -9377,7 +9404,7 @@ postgres_get_reserve_by_h_blind (void *cls,
};
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserve_by_h_blind",
"reserve_by_wih",
params,
rs);
}
@ -9390,6 +9417,7 @@ postgres_get_reserve_by_h_blind (void *cls,
* @param cls closure
* @param h_blind_ev hash of the blinded coin
* @param[out] old_coin_pub set to information about the old coin (on success only)
* @param[out] rrc_serial set to serial number of the entry in the database
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
@ -11641,8 +11669,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_recoup_refresh_above_serial_id;
plugin->select_reserve_closed_above_serial_id
= &postgres_select_reserve_closed_above_serial_id;
plugin->get_reserve_by_h_blind
= &postgres_get_reserve_by_h_blind;
plugin->get_reserve_by_wih
= &postgres_get_reserve_by_wih;
plugin->get_old_coin_by_h_blind
= &postgres_get_old_coin_by_h_blind;
plugin->insert_denomination_revocation

View File

@ -1346,13 +1346,14 @@ run (void *cls)
struct GNUNET_TIME_Timestamp now;
struct TALER_WireSaltP salt;
struct TALER_CoinPubHash c_hash;
struct TALER_WithdrawIdentificationHash wih;
uint64_t known_coin_id;
uint64_t rrc_serial;
struct TALER_EXCHANGEDB_Refresh refresh;
struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
uint64_t reserve_out_serial_id;
uint64_t melt_serial_id;
struct TALER_PlanchetSecretsP ps;
struct TALER_PlanchetMasterSecretP ps;
union TALER_DenominationBlindingKeyP bks;
struct TALER_ExchangeWithdrawValues alg_values = {
/* RSA is simpler, and for the DB there is no real difference between
@ -1383,7 +1384,7 @@ run (void *cls)
plugin->create_tables (plugin->cls))
{
result = 77;
goto drop;
goto cleanup;
}
plugin->preflight (plugin->cls);
FAILIF (GNUNET_OK !=
@ -1480,7 +1481,6 @@ run (void *cls)
&age_hash
};
// FIXME:
/* Call TALER_denom_blind()/TALER_denom_sign_blinded() twice, once without
* age_hash, once with age_hash */
RND_BLK (&age_hash);
@ -1500,9 +1500,14 @@ run (void *cls)
&cbc.denom_pub_hash,
&cbc.h_coin_envelope));
GNUNET_assert (GNUNET_OK ==
TALER_denom_sign_blinded (&cbc.sig,
&dkp->priv,
&pd.blinded_planchet));
TALER_withdraw_request_hash (&pd.blinded_planchet,
&cbc.denom_pub_hash,
&wih)); GNUNET_assert (
GNUNET_OK ==
TALER_denom_sign_blinded (
&cbc.sig,
&dkp->priv,
&pd.blinded_planchet));
TALER_blinded_planchet_free (&pd.blinded_planchet);
}
}
@ -1512,6 +1517,7 @@ run (void *cls)
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (CURRENCY,
&cbc.withdraw_fee));
{
bool found;
bool balance_ok;
@ -1520,6 +1526,7 @@ run (void *cls)
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_withdraw (plugin->cls,
&wih,
&cbc,
now,
&found,
@ -1541,16 +1548,16 @@ run (void *cls)
value.fraction,
value.currency));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_reserve_by_h_blind (plugin->cls,
&cbc.h_coin_envelope,
&reserve_pub3,
&reserve_out_serial_id));
plugin->get_reserve_by_wih (plugin->cls,
&wih,
&reserve_pub3,
&reserve_out_serial_id));
FAILIF (0 != GNUNET_memcmp (&reserve_pub,
&reserve_pub3));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_withdraw_info (plugin->cls,
&cbc.h_coin_envelope,
&wih,
&cbc2));
FAILIF (0 != GNUNET_memcmp (&cbc2.reserve_sig,
&cbc.reserve_sig));
@ -1565,6 +1572,8 @@ run (void *cls)
TALER_denom_sig_unblind (&ds,
&cbc2.sig,
&bks,
&c_hash,
&alg_values,
&dkp->pub));
FAILIF (GNUNET_OK !=
TALER_denom_pub_verify (&dkp->pub,
@ -1582,6 +1591,8 @@ run (void *cls)
TALER_denom_sig_unblind (&deposit.coin.denom_sig,
&cbc.sig,
&bks,
&c_hash,
&alg_values,
&dkp->pub));
deadline = GNUNET_TIME_timestamp_get ();
{
@ -1760,6 +1771,7 @@ run (void *cls)
rp->blinded_msg_size);
TALER_denom_pub_hash (&new_dkp[cnt]->pub,
&ccoin->h_denom_pub);
ccoin->exchange_vals = alg_values;
TALER_coin_ev_hash (bp,
&ccoin->h_denom_pub,
&ccoin->coin_envelope_hash);
@ -2167,6 +2179,8 @@ run (void *cls)
TALER_denom_sig_unblind (&deposit.coin.denom_sig,
&cbc.sig,
&bks,
&c_hash,
&alg_values,
&dkp->pub));
RND_BLK (&deposit.csig);
RND_BLK (&deposit.merchant_pub);
@ -2394,6 +2408,7 @@ drop:
rh = NULL;
GNUNET_break (GNUNET_OK ==
plugin->drop_tables (plugin->cls));
cleanup:
if (NULL != dkp)
destroy_denom_key_pair (dkp);
if (NULL != revealed_coins)

View File

@ -259,7 +259,7 @@ TALER_AUDITOR_deposit_confirmation (
const struct TALER_MerchantWireHash *h_wire,
const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_PrivateContractHash *h_contract_terms,
struct GNUNET_TIME_Timestamp timestamp,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
const struct TALER_Amount *amount_without_fee,

View File

@ -462,11 +462,27 @@ struct TALER_RsaPubHashP
};
/**
* Master key material for the deriviation of
* private coins and blinding factors during
* withdraw or refresh.
*/
struct TALER_PlanchetMasterSecretP
{
/**
* Key material.
*/
uint32_t key_data[8];
};
/**
* Master key material for the deriviation of
* private coins and blinding factors.
*/
struct TALER_PlanchetSecretsP
struct TALER_RefreshMasterSecretP
{
/**
@ -555,6 +571,22 @@ struct TALER_BlindedCoinHash
};
/**
* Hash used to uniquely represent a withdraw process so as to perform
* idempotency checks (and prevent clients from harmfully replaying withdraw
* operations with problematic variations on the inputs). In the CS case,
* this is a hash over the DK and nonce, while in the RSA case, it is simply a
* hash over the DK and the blinded coin.
*/
struct TALER_WithdrawIdentificationHash
{
/**
* Actual hash value.
*/
struct GNUNET_HashCode hash;
};
/**
* Hash used to represent the hash of the public
* key of a coin (without blinding).
@ -840,7 +872,7 @@ struct TALER_BlindedCsPlanchet
struct GNUNET_CRYPTO_CsC c[2];
/**
* Public Nonce
* Public nonce.
*/
struct TALER_CsNonce nonce;
};
@ -875,18 +907,6 @@ struct TALER_BlindedPlanchet
};
/**
* Withdraw nonce for CS denominations
*/
struct TALER_RefreshNonceXXXDEADFIXME
{
/**
* 32 bit nonce to include in withdrawals
*/
struct GNUNET_CRYPTO_CsNonce nonce;
};
/**
* Pair of Public R values for Cs denominations
*/
@ -965,18 +985,6 @@ struct TALER_TrackTransferDetails
};
/**
* @brief Type of CS Values for withdrawal
*/
struct TALER_ExchangeWithdrawCsValues
{
/**
* (non-blinded) r_pub
*/
struct TALER_DenominationCSPublicRPairP r_pub_pair;
};
/**
* @brief Type of algorithm specific Values for withdrawal
*/
@ -996,7 +1004,7 @@ struct TALER_ExchangeWithdrawValues
/**
* If we use #TALER_DENOMINATION_CS in @a cipher.
*/
struct TALER_ExchangeWithdrawCsValues cs_values;
struct TALER_DenominationCSPublicRPairP cs_values;
} details;
@ -1020,7 +1028,7 @@ TALER_denom_pub_free (struct TALER_DenominationPublicKey *denom_pub);
*/
void
TALER_planchet_setup_coin_priv (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
const struct TALER_ExchangeWithdrawValues *alg_values,
struct TALER_CoinSpendPrivateKeyP *coin_priv);
@ -1033,7 +1041,7 @@ TALER_planchet_setup_coin_priv (
*/
void
TALER_cs_withdraw_nonce_derive (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
struct TALER_CsNonce *nonce);
@ -1041,13 +1049,13 @@ TALER_cs_withdraw_nonce_derive (
* @brief Method to derive /csr nonce
* to be used during refresh/melt operation.
*
* @param coin_priv private key of the coin
* @param rms secret input for the refresh operation
* @param idx index of the fresh coin
* @param[out] nonce set to nonce included in the request to generate R_0 and R_1
*/
void
TALER_cs_refresh_nonce_derive (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
uint32_t idx,
struct TALER_CsNonce *nonce);
@ -1060,7 +1068,7 @@ TALER_cs_refresh_nonce_derive (
* be passed.
*
* @param[out] denom_priv where to write the private key
* @param[out] deonm_pub where to write the public key
* @param[out] denom_pub where to write the public key
* @param cipher which type of cipher to use
* @param ... RSA key size (eg. 2048/3072/4096)
* @return #GNUNET_OK on success, #GNUNET_NO if parameters were invalid
@ -1090,32 +1098,20 @@ void
TALER_denom_sig_free (struct TALER_DenominationSignature *denom_sig);
/**
* @brief Function for CS signatures to derive public R_0 and R_1
*
* @param nonce withdraw nonce from a client
* @param denom_priv denomination privkey as long-term secret
* @param r_pub the resulting R_0 and R_1
* @return enum GNUNET_GenericReturnValue
*/
enum GNUNET_GenericReturnValue
TALER_denom_cs_derive_r_public (
const struct TALER_CsNonce *nonce,
const struct TALER_DenominationPrivateKey *denom_priv,
struct TALER_DenominationCSPublicRPairP *r_pub);
/**
* Blind coin for blind signing with @a dk using blinding secret @a coin_bks.
*
* NOTE: As a particular oddity, the @a blinded_planchet is only partially
* initialized by this function in the case of CS-denominations. Here, the
* 'nonce' must be initialized separately!
*
* @param dk denomination public key to blind for
* @param coin_bks blinding secret to use
* @param age_commitment_hash hash of the age commitment to be used for the coin. NULL if no commitment is made.
* @param coin_pub public key of the coin to blind
* @param alg_values algorithm specific values to blind the planchet
* @param[out] c_hash resulting hashed coin
* @param[out] coin_ev blinded coin to submit
* @param[out] coin_ev_size number of bytes in @a coin_ev
* @param[out] blinded_planchet planchet data to initialize
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
@ -1148,8 +1144,9 @@ TALER_denom_sign_blinded (struct TALER_BlindedDenominationSignature *denom_sig,
* @param[out] denom_sig where to write the unblinded signature
* @param bdenom_sig the blinded signature
* @param bks blinding secret to use
* @param denom_pub public key used for signing
* @param c_hash hash of the coin's public key for verification of the signature
* @param alg_values algorithm specific values
* @param denom_pub public key used for signing
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
@ -1157,6 +1154,8 @@ TALER_denom_sig_unblind (
struct TALER_DenominationSignature *denom_sig,
const struct TALER_BlindedDenominationSignature *bdenom_sig,
const union TALER_DenominationBlindingKeyP *bks,
const struct TALER_CoinPubHash *c_hash,
const struct TALER_ExchangeWithdrawValues *alg_values,
const struct TALER_DenominationPublicKey *denom_pub);
@ -1258,8 +1257,8 @@ TALER_blinded_denom_sig_cmp (
/**
* Compare two blinded planchets.
*
* @param sig1 first blinded planchet
* @param sig2 second blinded planchet
* @param bp1 first blinded planchet
* @param bp2 second blinded planchet
* @return 0 if the keys are equal, otherwise -1 or 1
*/
int
@ -1315,7 +1314,7 @@ TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info,
* Compute the hash of a blinded coin.
*
* @param blinded_planchet blinded planchet
* @param denom_pub denomination publick key
* @param denom_hash hash of the denomination publick key
* @param[out] bch where to write the hash
* @return #GNUNET_OK when successful, #GNUNET_SYSERR if an internal error occured
*/
@ -1325,6 +1324,22 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
struct TALER_BlindedCoinHash *bch);
/**
* Compute the hash to uniquely identify a withdraw
* request.
*
* @param blinded_planchet blinded planchet
* @param denom_hash hash of the denomination publick key
* @param[out] wih where to write the hash
* @return #GNUNET_OK when successful, #GNUNET_SYSERR if an internal error occured
*/
enum GNUNET_GenericReturnValue
TALER_withdraw_request_hash (
const struct TALER_BlindedPlanchet *blinded_planchet,
const struct TALER_DenominationHash *denom_hash,
struct TALER_WithdrawIdentificationHash *wih);
/**
* Compute the hash of a coin.
*
@ -1494,34 +1509,44 @@ void
TALER_transfer_secret_to_planchet_secret (
const struct TALER_TransferSecretP *secret_seed,
uint32_t coin_num_salt,
struct TALER_PlanchetSecretsP *ps);
struct TALER_PlanchetMasterSecretP *ps);
/**
* Derive the @a coin_num transfer private key @a tpriv from a refresh from
* the @a ps seed of the refresh operation. The transfer private key
* the @a rms seed of the refresh operation. The transfer private key
* derivation is based on the @a ps with a KDF salted by the @a coin_num.
*
* @param ps seed to use for KDF to derive transfer keys
* @param rms seed to use for KDF to derive transfer keys
* @param cnc_num cut and choose number to include in KDF
* @param[out] tpriv value to initialize
*/
void
TALER_planchet_secret_to_transfer_priv (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
uint32_t cnc_num,
struct TALER_TransferPrivateKeyP *tpriv);
/**
* Setup information for fresh coins to be withdrawn
* or refreshed.
* Setup secret seed information for fresh coins to be
* withdrawn.
*
* @param[out] ps value to initialize
*/
void
TALER_planchet_setup_random (
struct TALER_PlanchetSecretsP *ps);
TALER_planchet_master_setup_random (
struct TALER_PlanchetMasterSecretP *ps);
/**
* Setup secret seed for fresh coins to be refreshed.
*
* @param[out] rms value to initialize
*/
void
TALER_refresh_master_setup_random (
struct TALER_RefreshMasterSecretP *rms);
/**
@ -1534,7 +1559,7 @@ TALER_planchet_setup_random (
*/
void
TALER_planchet_blinding_secret_create (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
const struct TALER_ExchangeWithdrawValues *alg_values,
union TALER_DenominationBlindingKeyP *bks);
@ -1548,8 +1573,8 @@ TALER_planchet_blinding_secret_create (
* @param coin_priv coin private key
* @param[out] c_hash set to the hash of the public key of the coin (needed later)
* @param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and
* other withdraw operations, pd->blinded_planchet.cipher will be set
* to cipher from dk
* other withdraw operations, `pd->blinded_planchet.cipher` will be set
* to cipher from @a dk
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
@ -1558,8 +1583,7 @@ TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
const union TALER_DenominationBlindingKeyP *bks,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_CoinPubHash *c_hash,
struct TALER_PlanchetDetail *pd
);
struct TALER_PlanchetDetail *pd);
/**
@ -1799,21 +1823,20 @@ TALER_CRYPTO_helper_rsa_poll (struct TALER_CRYPTO_RsaDenominationHelper *dh);
* @param h_rsa hash of the RSA public key to use to sign
* @param msg message to sign
* @param msg_size number of bytes in @a msg
* @param[out] ec set to the error code (or #TALER_EC_NONE on success)
* @return signature, the value inside the structure will be NULL on failure,
* see @a ec for details about the failure
* @param[out] bs set to the blind signature
* @return #TALER_EC_NONE on success
*/
struct TALER_BlindedDenominationSignature
enum TALER_ErrorCode
TALER_CRYPTO_helper_rsa_sign (
struct TALER_CRYPTO_RsaDenominationHelper *dh,
const struct TALER_RsaPubHashP *h_rsa,
const void *msg,
size_t msg_size,
enum TALER_ErrorCode *ec);
struct TALER_BlindedDenominationSignature *bs);
/**
* Ask the helper to revoke the public key associated with @param h_denom_pub .
* Ask the helper to revoke the public key associated with @a h_denom_pub.
* Will cause the helper to tell all clients that the key is now unavailable,
* and to create a replacement key.
*
@ -1920,20 +1943,19 @@ TALER_CRYPTO_helper_cs_poll (struct TALER_CRYPTO_CsDenominationHelper *dh);
* @param dh helper process connection
* @param h_cs hash of the CS public key to use to sign
* @param blinded_planchet blinded planchet containing c and nonce
* @param[out] ec set to the error code (or #TALER_EC_NONE on success)
* @return signature, the value inside the structure will be NULL on failure,
* see @a ec for details about the failure
* @param[out] bs set to the blind signature
* @return #TALER_EC_NONE on success
*/
struct TALER_BlindedDenominationSignature
enum TALER_ErrorCode
TALER_CRYPTO_helper_cs_sign (
struct TALER_CRYPTO_CsDenominationHelper *dh,
const struct TALER_CsPubHashP *h_cs,
const struct TALER_BlindedCsPlanchet *blinded_planchet,
enum TALER_ErrorCode *ec);
struct TALER_BlindedDenominationSignature *bs);
/**
* Ask the helper to revoke the public key associated with @param h_cs .
* Ask the helper to revoke the public key associated with @a h_cs.
* Will cause the helper to tell all clients that the key is now unavailable,
* and to create a replacement key.
*
@ -1954,8 +1976,8 @@ TALER_CRYPTO_helper_cs_revoke (
/**
* Ask the helper to derive R using the @param nonce and denomination key
* associated with @param h_cs.
* Ask the helper to derive R using the @a nonce and denomination key
* associated with @a h_cs.
*
* This operation will block until the R has been obtained. Should
* this process receive a signal (that is not ignored) while the operation is
@ -1966,15 +1988,14 @@ TALER_CRYPTO_helper_cs_revoke (
* @param dh helper to process connection
* @param h_cs hash of the CS public key to revoke
* @param nonce witdhraw nonce
* @param[out] ec set to the error code (or #TALER_EC_NONE on success)
* @return R, the value inside the structure will be NULL on failure,
* see @a ec for details about the failure
* @param[out] crp set to the pair of R values
* @return set to the error code (or #TALER_EC_NONE on success)
*/
struct TALER_DenominationCSPublicRPairP
enum TALER_ErrorCode
TALER_CRYPTO_helper_cs_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh,
const struct TALER_CsPubHashP *h_cs,
const struct TALER_CsNonce *nonce,
enum TALER_ErrorCode *ec);
struct TALER_DenominationCSPublicRPairP *crp);
/**
@ -2097,7 +2118,7 @@ TALER_CRYPTO_helper_esign_sign_ (
/**
* Ask the helper to revoke the public key @param exchange_pub .
* Ask the helper to revoke the public key @a exchange_pub .
* Will cause the helper to tell all clients that the key is now unavailable,
* and to create a replacement key.
*
@ -2162,6 +2183,17 @@ TALER_exchange_deposit_confirm_verify (
/* ********************* wallet signing ************************** */
/**
* Sign a request by a wallet to perform a KYC check.
*
* @param reserve_priv key identifying the wallet/account
* @param reserve_sig resulting signature
*/
void
TALER_wallet_account_setup_sign (
const struct TALER_ReservePrivateKeyP *reserve_priv,
struct TALER_ReserveSignatureP *reserve_sig);
/**
* Sign a deposit permission. Function for wallets.
@ -2227,7 +2259,7 @@ TALER_wallet_deposit_verify (
/**
* Sign a melt request.
*
* @param amount the amount to be melted (with fee)
* @param amount_with_fee the amount to be melted (with fee)
* @param melt_fee the melt fee we expect to pay
* @param rc refresh session we are committed to
* @param h_denom_pub hash of the coin denomination's public key
@ -2247,7 +2279,7 @@ TALER_wallet_melt_sign (
/**
* Verify a melt request.
*
* @param amount the amount to be melted (with fee)
* @param amount_with_fee the amount to be melted (with fee)
* @param melt_fee the melt fee we expect to pay
* @param rc refresh session we are committed to
* @param h_denom_pub hash of the coin denomination's public key
@ -2301,6 +2333,60 @@ TALER_wallet_link_verify (
const struct TALER_CoinSpendSignatureP *coin_sig);
/**
* Sign withdraw request.
*
* @param h_denom_pub hash of the denomiantion public key of the coin to withdraw
* @param amount_with_fee amount to debit the reserve for
* @param bch blinded coin hash
* @param reserve_priv private key to sign with
* @param[out] reserve_sig resulting signature
*/
void
TALER_wallet_withdraw_sign (
const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_BlindedCoinHash *bch,
const struct TALER_ReservePrivateKeyP *reserve_priv,
struct TALER_ReserveSignatureP *reserve_sig);
/**
* Verify withdraw request.
*
* @param h_denom_pub hash of the denomiantion public key of the coin to withdraw
* @param amount_with_fee amount to debit the reserve for
* @param bch blinded coin hash
* @param reserve_pub public key of the reserve
* @param reserve_sig resulting signature
* @return #GNUNET_OK if the signature is valid
*/
enum GNUNET_GenericReturnValue
TALER_wallet_withdraw_verify (
const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_BlindedCoinHash *bch,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig);
/**
* Verify exchange melt confirmation.
*
* @param rc refresh session this is about
* @param noreveal_index gamma value chosen by the exchange
* @param exchange_pub public signing key used
* @param exchange_sig signature to check
* @return #GNUNET_OK if the signature is valid
*/
enum GNUNET_GenericReturnValue
TALER_exchange_melt_confirmation_verify (
const struct TALER_RefreshCommitmentP *rc,
uint32_t noreveal_index,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct TALER_ExchangeSignatureP *exchange_sig);
/**
* Verify recoup signature.
*
@ -2323,7 +2409,6 @@ TALER_wallet_recoup_verify (
*
* @param h_denom_pub hash of the denomiantion public key of the coin
* @param coin_bks blinding factor used when withdrawing the coin
* @param requested_amount amount that is left to be recouped
* @param coin_priv coin key of the coin to be recouped
* @param coin_sig resulting signature
*/
@ -2357,7 +2442,6 @@ TALER_wallet_recoup_refresh_verify (
*
* @param h_denom_pub hash of the denomiantion public key of the coin
* @param coin_bks blinding factor used when withdrawing the coin
* @param requested_amount amount that is left to be recouped
* @param coin_priv coin key of the coin to be recouped
* @param coin_sig resulting signature
*/

View File

@ -1386,6 +1386,36 @@ TALER_EXCHANGE_reserves_get_cancel (
struct TALER_EXCHANGE_WithdrawHandle;
/**
* All the details about a coin that are generated during withdrawal and that
* may be needed for future operations on the coin.
*/
struct TALER_EXCHANGE_PrivateCoinDetails
{
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Value used to blind the key for the signature.
* Needed for recoup operations.
*/
union TALER_DenominationBlindingKeyP bks;
/**
* Signature over the coin.
*/
struct TALER_DenominationSignature sig;
/**
* Values contributed from the exchange during the
* withdraw protocol.
*/
struct TALER_ExchangeWithdrawValues exchange_vals;
};
/**
* Details about a response for a withdraw request.
*/
@ -1404,30 +1434,7 @@ struct TALER_EXCHANGE_WithdrawResponse
/**
* Details if the status is #MHD_HTTP_OK.
*/
struct
{
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Value used to blind the key for the signature.
* Needed for recoup operations.
*/
union TALER_DenominationBlindingKeyP bks;
/**
* Signature over the coin.
*/
struct TALER_DenominationSignature sig;
/**
* Values contributed from the exchange during the
* withdraw protocol.
*/
struct TALER_ExchangeWithdrawValues exchange_vals;
} success;
struct TALER_EXCHANGE_PrivateCoinDetails success;
/**
* Details if the status is #MHD_HTTP_ACCEPTED.
@ -1500,7 +1507,7 @@ TALER_EXCHANGE_withdraw (
struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
TALER_EXCHANGE_WithdrawCallback res_cb,
void *res_cb_cls);
@ -1630,6 +1637,67 @@ struct TALER_EXCHANGE_RefreshData
struct TALER_EXCHANGE_MeltHandle;
/**
* Information we obtain per coin during melting.
*/
struct TALER_EXCHANGE_MeltBlindingDetail
{
/**
* Exchange values contributed to the refresh operation
*/
struct TALER_ExchangeWithdrawValues alg_value;
};
/**
* Response returned to a /melt request.
*/
struct TALER_EXCHANGE_MeltResponse
{
/**
* Full HTTP response details.
*/
struct TALER_EXCHANGE_HttpResponse hr;
/**
* Parsed response details, variant depending on the
* @e hr.http_status.
*/
union
{
/**
* Results for status #MHD_HTTP_SUCCESS.
*/
struct
{
/**
* Information returned per coin.
*/
const struct TALER_EXCHANGE_MeltBlindingDetail *mbds;
/**
* Key used by the exchange to sign the response.
*/
struct TALER_ExchangePublicKeyP sign_key;
/**
* Length of the @a mbds array with the exchange values
* and blinding keys we are using.
*/
unsigned int num_mbds;
/**
* Gamma value chosen by the exchange.
*/
uint32_t noreveal_index;
} success;
} details;
};
/**
* Callbacks of this type are used to notify the application about the result
* of the /coins/$COIN_PUB/melt stage. If successful, the @a noreveal_index
@ -1637,23 +1705,12 @@ struct TALER_EXCHANGE_MeltHandle;
* #TALER_EXCHANGE_refreshes_reveal().
*
* @param cls closure
* @param hr HTTP response data
* @param num_coins number of fresh coins to be created, length of the @a exchange_vals array, 0 if the operation failed
* @param alg_values array @a num_coins of exchange values contributed to the refresh operation
* @param bks array of @a num_coins blinding keys used to blind the fresh coins
* @param noreveal_index choice by the exchange in the cut-and-choose protocol,
* UINT32_MAX on error
* @param sign_key exchange key used to sign @a full_response, or NULL
* @param mr response details
*/
typedef void
(*TALER_EXCHANGE_MeltCallback) (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
unsigned int num_coins,
const struct TALER_ExchangeWithdrawValues *alg_values,
const union TALER_DenominationBlindingKeyP *bks,
uint32_t noreveal_index,
const struct TALER_ExchangePublicKeyP *sign_key);
const struct TALER_EXCHANGE_MeltResponse *mr);
/**
@ -1662,12 +1719,11 @@ typedef void
*
* This API is typically used by a wallet. Note that to ensure that
* no money is lost in case of hardware failures, the provided
* argument should have been constructed using
* #TALER_EXCHANGE_refresh_prepare and committed to persistent storage
* argument @a rd should be committed to persistent storage
* prior to calling this function.
*
* @param exchange the exchange handle; the exchange must be ready to operate
* @param ps the fresh secret that defines the refresh operation
* @param rms the fresh secret that defines the refresh operation
* @param rd the refresh data specifying the characteristics of the operation
* @param melt_cb the callback to call with the result
* @param melt_cb_cls closure for @a melt_cb
@ -1676,7 +1732,7 @@ typedef void
*/
struct TALER_EXCHANGE_MeltHandle *
TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_EXCHANGE_RefreshData *rd,
TALER_EXCHANGE_MeltCallback melt_cb,
void *melt_cb_cls);
@ -1695,28 +1751,87 @@ TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh);
/* ********************* /refreshes/$RCH/reveal ***************************** */
/**
* Information about a coin obtained via /refreshes/$RCH/reveal.
*/
struct TALER_EXCHANGE_RevealedCoinInfo
{
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Master secret of this coin.
*/
struct TALER_PlanchetMasterSecretP ps;
/**
* Blinding keys used to blind the fresh coin.
*/
union TALER_DenominationBlindingKeyP bks;
/**
* Signature affirming the validity of the coin.
*/
struct TALER_DenominationSignature sig;
};
/**
* Result of a /refreshes/$RCH/reveal request.
*/
struct TALER_EXCHANGE_RevealResult
{
/**
* HTTP status.
*/
struct TALER_EXCHANGE_HttpResponse hr;
/**
* Parsed response details, variant depending on the
* @e hr.http_status.
*/
union
{
/**
* Results for status #MHD_HTTP_SUCCESS.
*/
struct
{
/**
* Array of @e num_coins values about the coins obtained via the refresh
* operation. The array give the coins in the same order (and should
* have the same length) in which the original melt request specified the
* respective denomination keys.
*/
const struct TALER_EXCHANGE_RevealedCoinInfo *coins;
/**
* Number of coins returned.
*/
unsigned int num_coins;
} success;
} details;
};
/**
* Callbacks of this type are used to return the final result of
* submitting a refresh request to a exchange. If the operation was
* successful, this function returns the signatures over the coins
* that were remelted. The @a coin_privs and @a sigs arrays give the
* coins in the same order (and should have the same length) in which
* the original request specified the respective denomination keys.
* that were remelted.
*
* @param cls closure
* @param hr HTTP response data
* @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed
* @param exchange_vals array of contributions from the exchange on the refreshes
* @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error
* @param sigs array of signature over @a num_coins coins, NULL on error
* @param rr result of the reveal operation
*/
typedef void
(*TALER_EXCHANGE_RefreshesRevealCallback)(
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_DenominationSignature *sigs);
const struct TALER_EXCHANGE_RevealResult *rr);
/**
@ -1735,7 +1850,7 @@ struct TALER_EXCHANGE_RefreshesRevealHandle;
* prior to calling this function.
*
* @param exchange the exchange handle; the exchange must be ready to operate
* @param ps the fresh secret that defines the refresh operation
* @param rms the fresh secret that defines the refresh operation
* @param rd the refresh data that characterizes the refresh operation
* @param num_coins number of fresh coins to be created, length of the @a exchange_vals array, must match value in @a rd
* @param alg_values array @a num_coins of exchange values contributed to the refresh operation
@ -1750,7 +1865,7 @@ struct TALER_EXCHANGE_RefreshesRevealHandle;
struct TALER_EXCHANGE_RefreshesRevealHandle *
TALER_EXCHANGE_refreshes_reveal (
struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_EXCHANGE_RefreshData *rd,
unsigned int num_coins,
const struct TALER_ExchangeWithdrawValues *alg_values,
@ -1779,6 +1894,71 @@ TALER_EXCHANGE_refreshes_reveal_cancel (
struct TALER_EXCHANGE_LinkHandle;
/**
* Information about a coin obtained via /link.
*/
struct TALER_EXCHANGE_LinkedCoinInfo
{
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Master secret of this coin.
*/
struct TALER_PlanchetMasterSecretP ps;
/**
* Signature affirming the validity of the coin.
*/
struct TALER_DenominationSignature sig;
/**
* Denomination public key of the coin.
*/
struct TALER_DenominationPublicKey pub;
};
/**
* Result of a /link request.
*/
struct TALER_EXCHANGE_LinkResult
{
/**
* HTTP status.
*/
struct TALER_EXCHANGE_HttpResponse hr;
/**
* Parsed response details, variant depending on the
* @e hr.http_status.
*/
union
{
/**
* Results for status #MHD_HTTP_SUCCESS.
*/
struct
{
/**
* Array of @e num_coins values about the
* coins obtained via linkage.
*/
const struct TALER_EXCHANGE_LinkedCoinInfo *coins;
/**
* Number of coins returned.
*/
unsigned int num_coins;
} success;
} details;
};
/**
* Callbacks of this type are used to return the final result of submitting a
* /coins/$COIN_PUB/link request to a exchange. If the operation was
@ -1786,20 +1966,12 @@ struct TALER_EXCHANGE_LinkHandle;
* created when the original coin was melted.
*
* @param cls closure
* @param hr HTTP response data
* @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed
* @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error
* @param sigs array of signature over @a num_coins coins, NULL on error
* @param pubs array of public keys for the @a sigs, NULL on error
* @param lr result of the /link operation
*/
typedef void
(*TALER_EXCHANGE_LinkCallback) (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_DenominationSignature *sigs,
const struct TALER_DenominationPublicKey *pubs);
const struct TALER_EXCHANGE_LinkResult *lr);
/**
@ -2032,8 +2204,7 @@ struct TALER_EXCHANGE_GetDepositResponse
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param hr HTTP response data
* @param dd details about the deposit (NULL on errors)
* @param dr details about the deposit response
*/
typedef void
(*TALER_EXCHANGE_DepositGetCallback)(
@ -2185,7 +2356,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_DenominationSignature *denom_sig,
const struct TALER_ExchangeWithdrawValues *exchange_vals,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
TALER_EXCHANGE_RecoupResultCallback recoup_cb,
void *recoup_cb_cls);
@ -2235,7 +2406,9 @@ typedef void
* @param pk kind of coin to pay back
* @param denom_sig signature over the coin by the exchange using @a pk
* @param exchange_vals contribution from the exchange on the withdraw
* @param ps secret internals of the original refresh-reveal operation
* @param rms melt secret of the refreshing operation
* @param ps coin-specific secrets derived for this coin during the refreshing operation
* @param idx index of the fresh coin in the refresh operation that is now being recouped
* @param recoup_cb the callback to call when the final result for this request is available
* @param recoup_cb_cls closure for @a recoup_cb
* @return NULL
@ -2248,7 +2421,9 @@ TALER_EXCHANGE_recoup_refresh (
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_DenominationSignature *denom_sig,
const struct TALER_ExchangeWithdrawValues *exchange_vals,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_PlanchetMasterSecretP *ps,
unsigned int idx,
TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb,
void *recoup_cb_cls);
@ -2308,7 +2483,7 @@ struct TALER_EXCHANGE_KycStatus
/**
* Signature of purpose
* #TALER_SIGNATURE_ACCOUNT_SETUP_SUCCESS affirming
* #TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS affirming
* the successful KYC process.
*/
struct TALER_ExchangeSignatureP exchange_sig;
@ -2402,7 +2577,7 @@ struct TALER_EXCHANGE_KycProofResponse
* Function called with the result of a KYC check.
*
* @param cls closure
* @param ks the account's KYC status details
* @param kpr the account's KYC status details
*/
typedef void
(*TALER_EXCHANGE_KycProofCallback)(

View File

@ -317,6 +317,7 @@ struct TALER_EXCHANGEDB_TableData
uint64_t denominations_serial;
void *coin_ev;
size_t coin_ev_size;
struct TALER_ExchangeWithdrawValues ewv;
// h_coin_ev omitted, to be recomputed!
struct TALER_BlindedDenominationSignature ev_sig;
} refresh_revealed_coins;
@ -1368,12 +1369,32 @@ struct TALER_EXCHANGEDB_LinkList
*/
struct TALER_BlindedDenominationSignature ev_sig;
/**
* Exchange-provided values during the coin generation.
*/
struct TALER_ExchangeWithdrawValues alg_values;
/**
* Signature of the original coin being refreshed over the
* link data, of type #TALER_SIGNATURE_WALLET_COIN_LINK
*/
struct TALER_CoinSpendSignatureP orig_coin_link_sig;
/**
* CS nonce, if cipher is CS.
*/
struct TALER_CsNonce nonce;
/**
* Offset that generated this coin in the refresh
* operation.
*/
uint32_t coin_refresh_offset;
/**
* Set to true if @e nonce was initialized.
*/
bool have_nonce;
};
@ -1645,6 +1666,12 @@ struct TALER_EXCHANGEDB_RefreshRevealedCoin
*/
struct TALER_BlindedDenominationSignature coin_sig;
/**
* Values contributed from the exchange to the
* coin generation (see /csr).
*/
struct TALER_ExchangeWithdrawValues exchange_vals;
/**
* Blinded message to be signed (in envelope).
*/
@ -1868,7 +1895,6 @@ typedef void
* @param cls closure
* @param rowid which row in the table is the information from (for diagnostics)
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param account_payto_uri which account did the transfer go to?
* @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
* @param h_contract_terms which proposal was this payment about
@ -2450,20 +2476,19 @@ struct TALER_EXCHANGEDB_Plugin
/**
* Locate the response for a withdraw request under the
* key of the hash of the blinded message. Used to ensure
* idempotency of the request.
* Locate the response for a withdraw request under a hash that uniquely
* identifies the withdraw operation. Used to ensure idempotency of the
* request.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param h_blind hash of the blinded coin to be signed (will match
* `h_coin_envelope` in the @a collectable to be returned)
* @param collectable corresponding collectable coin (blind signature)
* @param wih hash that uniquely identifies the withdraw operation
* @param[out] collectable corresponding collectable coin (blind signature)
* if a coin is found
* @return statement execution status
*/
enum GNUNET_DB_QueryStatus
(*get_withdraw_info)(void *cls,
const struct TALER_BlindedCoinHash *h_blind,
const struct TALER_WithdrawIdentificationHash *wih,
struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
@ -2472,7 +2497,8 @@ struct TALER_EXCHANGEDB_Plugin
* and possibly persisting the withdrawal details.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param collectable corresponding collectable coin (blind signature)
* @param wih hash that uniquely identifies the withdraw operation
* @param[in,out] collectable corresponding collectable coin (blind signature)
* if a coin is found
* @param now current time (rounded)
* @param[out] found set to true if the reserve was found
@ -2484,7 +2510,8 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus
(*do_withdraw)(
void *cls,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
const struct TALER_WithdrawIdentificationHash *wih,
struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
struct GNUNET_TIME_Timestamp now,
bool *found,
bool *balance_ok,
@ -2520,6 +2547,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param deposit deposit operation details
* @param known_coin_id row of the coin in the known_coins table
* @param h_payto hash of the merchant's payto URI
* @param extension_blocked true if an extension is blocking the wire transfer
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] in_conflict set to true if the deposit conflicted
@ -3490,16 +3518,16 @@ struct TALER_EXCHANGEDB_Plugin
* from given the hash of the blinded coin.
*
* @param cls closure
* @param h_blind_ev hash of the blinded coin
* @param wih hash identifying the withdraw operation
* @param[out] reserve_pub set to information about the reserve (on success only)
* @param[out] reserve_out_serial_id set to row of the @a h_blind_ev in reserves_out
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
(*get_reserve_by_h_blind)(void *cls,
const struct TALER_BlindedCoinHash *h_blind_ev,
struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t *reserve_out_serial_id);
(*get_reserve_by_wih)(void *cls,
const struct TALER_WithdrawIdentificationHash *wih,
struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t *reserve_out_serial_id);
/**

View File

@ -131,6 +131,20 @@ TALER_JSON_pack_blinded_planchet (
const struct TALER_BlindedPlanchet *blinded_planchet);
/**
* Generate packer instruction for a JSON field of type
* exchange withdraw values (/csr).
*
* @param name name of the field to add to the object
* @param ewv values to transmit
* @return json pack specification
*/
struct GNUNET_JSON_PackSpec
TALER_JSON_pack_exchange_withdraw_values (
const char *name,
const struct TALER_ExchangeWithdrawValues *ewv);
/**
* Generate packer instruction for a JSON field of type
* amount.
@ -274,6 +288,20 @@ TALER_JSON_spec_blinded_denom_sig (
struct TALER_BlindedDenominationSignature *sig);
/**
* Generate line in parser specification for
* exchange withdraw values (/csr).
*
* @param field name of the field
* @param[out] ewv the exchange withdraw values to initialize
* @return corresponding field spec
*/
struct GNUNET_JSON_Specification
TALER_JSON_spec_exchange_withdraw_values (
const char *field,
struct TALER_ExchangeWithdrawValues *ewv);
/**
* Generate line in parser specification for a
* blinded planchet.
@ -580,7 +608,7 @@ TALER_JSON_extensions_config_hash (const json_t *config,
struct TALER_ExtensionConfigHash *eh);
/**
* Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }.
* Parses a JSON object `{ "extension": "age_restriction", "mask": uint32 }`.
*
* @param root is the json object
* @param[out] mask on succes, will contain the age mask

View File

@ -547,7 +547,7 @@ struct TALER_MHD_Legal;
/**
* Load set of legal documents as specified in @a cfg in section @a section
* where the Etag is given under the @param tagoption and the directory under
* where the Etag is given under the @a tagoption and the directory under
* the @a diroption.
*
* @param cfg configuration to use

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 Taler Systems SA
Copyright (C) 2014-2022 Taler Systems SA
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
@ -58,7 +58,7 @@ TALER_PQ_query_param_amount (const struct TALER_Amount *x);
* public key will be serialized into on variable-size
* BLOB.
*
* @param x pointer to the query parameter to pass
* @param denom_pub pointer to the query parameter to pass
*/
struct GNUNET_PQ_QueryParam
TALER_PQ_query_param_denom_pub (
@ -70,7 +70,7 @@ TALER_PQ_query_param_denom_pub (
* various attributes of the signature will be serialized into on
* variable-size BLOB.
*
* @param x pointer to the query parameter to pass
* @param denom_sig pointer to the query parameter to pass
*/
struct GNUNET_PQ_QueryParam
TALER_PQ_query_param_denom_sig (
@ -83,7 +83,7 @@ TALER_PQ_query_param_denom_sig (
* planchet will be serialized into on
* variable-size BLOB.
*
* @param x pointer to the query parameter to pass
* @param bp pointer to the query parameter to pass
*/
struct GNUNET_PQ_QueryParam
TALER_PQ_query_param_blinded_planchet (
@ -95,13 +95,25 @@ TALER_PQ_query_param_blinded_planchet (
* the various attributes of the signature will be serialized into on
* variable-size BLOB.
*
* @param x pointer to the query parameter to pass
* @param denom_sig pointer to the query parameter to pass
*/
struct GNUNET_PQ_QueryParam
TALER_PQ_query_param_blinded_denom_sig (
const struct TALER_BlindedDenominationSignature *denom_sig);
/**
* Generate query parameter for the exchange's contribution during a
* withdraw. Internally, the various attributes of the @a alg_values will be
* serialized into on variable-size BLOB.
*
* @param alg_values pointer to the query parameter to pass
*/
struct GNUNET_PQ_QueryParam
TALER_PQ_query_param_exchange_withdraw_values (
const struct TALER_ExchangeWithdrawValues *alg_values);
/**
* Generate query parameter for a JSON object (stored as a string
* in the DB). Note that @a x must really be a JSON object or array,
@ -179,6 +191,19 @@ TALER_PQ_result_spec_blinded_denom_sig (
struct TALER_BlindedDenominationSignature *denom_sig);
/**
* Exchange withdraw values expected.
*
* @param name name of the field in the table
* @param[out] ewv where to store the exchange values
* @return array entry for the result specification to use
*/
struct GNUNET_PQ_ResultSpec
TALER_PQ_result_spec_exchange_withdraw_values (
const char *name,
struct TALER_ExchangeWithdrawValues *ewv);
/**
* Blinded planchet expected.
*

View File

@ -164,7 +164,6 @@
*/
#define TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN 1042
/**
* Signature where the Exchange confirms that it does not consider a denomination valid for the given operation
* at this time.
@ -440,13 +439,7 @@ struct TALER_WithdrawRequestPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Reserve public key (which reserve to withdraw from). This is
* the public key which must match the signature.
*/
struct TALER_ReservePublicKeyP reserve_pub;
/**
* Value of the coin being exchangeed (matching the denomination key)
* Value of the coin being exchanged (matching the denomination key)
* plus the transaction fee. We include this in what is being
* signed so that we can verify a reserve's remaining total balance
* without needing to access the respective denomination key
@ -1221,7 +1214,7 @@ struct TALER_MerchantWireDetailsPS
/**
* Salted hash over the account holder's payto:// URL and
* the salt, as done by #TALER_exchange_wire_signature_hash().
* the salt, as done by #TALER_merchant_wire_signature_hash().
*/
struct TALER_MerchantWireHash h_wire_details GNUNET_PACKED;

View File

@ -998,10 +998,11 @@ TALER_TESTING_cmd_bank_debits (const char *label,
/**
* Create transfer command.
*
* @param label command label.
* @param amount amount to transfer.
* @param label command label
* @param amount amount to transfer
* @param auth authentication data to use
* @param payto_credit_account which account receives money.
* @param payto_debit_account which account to withdraw money from
* @param payto_credit_account which account receives money
* @param wtid wire transfer identifier to use
* @param exchange_base_url exchange URL to use
* @return the command.
@ -1709,14 +1710,13 @@ TALER_TESTING_cmd_check_bank_empty (const char *label);
* provide a coin to be refunded.
* @param refund_transaction_id transaction id to use
* in the request.
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refund_with_id (const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *deposit_reference,
const char *coin_reference,
uint64_t refund_transaction_id);
@ -1728,14 +1728,13 @@ TALER_TESTING_cmd_refund_with_id (const char *label,
* @param refund_amount the amount to ask a refund for.
* @param coin_reference reference to a command that can
* provide a coin to be refunded.
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refund (const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *deposit_reference);
const char *coin_reference);
/**
@ -2084,6 +2083,7 @@ TALER_TESTING_cmd_auditor_add_denom_sig (const char *label,
const char *denom_ref,
bool bad_sig);
/**
* Add statement about wire fees of the exchange. This is always
* done for a few hours around the current time (for the test).
@ -2208,26 +2208,6 @@ TALER_TESTING_cmd_revoke_sign_key (
const char *signkey_ref);
/**
* Have the auditor affirm that it is auditing the given
* denomination key and upload the auditor's signature to
* the exchange.
*
* @param label command label.
* @param expected_http_status expected HTTP status from exchange
* @param denom_ref reference to a command that identifies
* a denomination key (i.e. because it was used to
* withdraw a coin).
* @param bad_sig should we use a bogus signature?
* @return the command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_auditor_add_denom_sig (const char *label,
unsigned int expected_http_status,
const char *denom_ref,
bool bad_sig);
/**
* Create a request for a wallet's KYC UUID.
*
@ -2443,8 +2423,9 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
#define TALER_TESTING_SIMPLE_TRAITS(op) \
op (bank_row, const uint64_t) \
op (reserve_priv, const struct TALER_ReservePrivateKeyP) \
op (planchet_secret, const struct TALER_PlanchetSecretsP) \
op (reserve_pub, const struct TALER_ReservePublicKeyP) \
op (planchet_secret, const struct TALER_PlanchetMasterSecretP) \
op (refresh_secret, const struct TALER_RefreshMasterSecretP) \
op (reserve_pub, const struct TALER_ReservePublicKeyP) \
op (merchant_priv, const struct TALER_MerchantPrivateKeyP) \
op (merchant_pub, const struct TALER_MerchantPublicKeyP) \
op (merchant_sig, const struct TALER_MerchantSignatureP) \
@ -2483,7 +2464,7 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
#define TALER_TESTING_INDEXED_TRAITS(op) \
op (denom_pub, const struct TALER_EXCHANGE_DenomPublicKey) \
op (denom_sig, const struct TALER_DenominationSignature) \
op (planchet_secrets, const struct TALER_PlanchetSecretsP) \
op (planchet_secrets, const struct TALER_PlanchetMasterSecretP) \
op (exchange_wd_value, const struct TALER_ExchangeWithdrawValues) \
op (coin_priv, const struct TALER_CoinSpendPrivateKeyP) \
op (coin_pub, const struct TALER_CoinSpendPublicKeyP) \

View File

@ -131,6 +131,7 @@ TALER_b2s (const void *buf,
/**
* Obtain denomination amount from configuration file.
*
* @param cfg configuration to extract data from
* @param section section of the configuration to access
* @param option option of the configuration to access
* @param[out] denom set to the amount found in configuration
@ -329,7 +330,7 @@ TALER_payto_get_method (const char *payto_uri);
/**
* Construct a payto://-URI from a Taler @a reserve_pub at
* a @exchange_base_url
* @a exchange_base_url
*
* @param exchange_base_url the URL of the exchange
* @param reserve_pub public key of the reserve

View File

@ -26,6 +26,25 @@
#include "taler_json_lib.h"
/**
* Convert string value to numeric cipher value.
*
* @param cipher_s input string
* @return numeric cipher value
*/
static enum TALER_DenominationCipher
string_to_cipher (const char *cipher_s)
{
if (0 == strcasecmp (cipher_s,
"RSA"))
return TALER_DENOMINATION_RSA;
if (0 == strcasecmp (cipher_s,
"CS"))
return TALER_DENOMINATION_CS;
return TALER_DENOMINATION_INVALID;
}
json_t *
TALER_JSON_from_amount (const struct TALER_Amount *amount)
{
@ -229,9 +248,9 @@ parse_denom_pub (void *cls,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_DenominationPublicKey *denom_pub = spec->ptr;
uint32_t cipher;
const char *cipher;
struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_uint32 ("cipher",
GNUNET_JSON_spec_string ("cipher",
&cipher),
GNUNET_JSON_spec_uint32 ("age_mask",
&denom_pub->age_mask.mask),
@ -250,7 +269,7 @@ parse_denom_pub (void *cls,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
denom_pub->cipher = (enum TALER_DenominationCipher) cipher;
denom_pub->cipher = string_to_cipher (cipher);
switch (denom_pub->cipher)
{
case TALER_DENOMINATION_RSA:
@ -346,9 +365,9 @@ parse_denom_sig (void *cls,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_DenominationSignature *denom_sig = spec->ptr;
uint32_t cipher;
const char *cipher;
struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_uint32 ("cipher",
GNUNET_JSON_spec_string ("cipher",
&cipher),
GNUNET_JSON_spec_end ()
};
@ -365,7 +384,7 @@ parse_denom_sig (void *cls,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
denom_sig->cipher = (enum TALER_DenominationCipher) cipher;
denom_sig->cipher = string_to_cipher (cipher);
switch (denom_sig->cipher)
{
case TALER_DENOMINATION_RSA:
@ -462,9 +481,9 @@ parse_blinded_denom_sig (void *cls,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_BlindedDenominationSignature *denom_sig = spec->ptr;
uint32_t cipher;
const char *cipher;
struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_uint32 ("cipher",
GNUNET_JSON_spec_string ("cipher",
&cipher),
GNUNET_JSON_spec_end ()
};
@ -481,7 +500,7 @@ parse_blinded_denom_sig (void *cls,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
denom_sig->cipher = (enum TALER_DenominationCipher) cipher;
denom_sig->cipher = string_to_cipher (cipher);
switch (denom_sig->cipher)
{
case TALER_DENOMINATION_RSA:
@ -581,9 +600,9 @@ parse_blinded_planchet (void *cls,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_BlindedPlanchet *blinded_planchet = spec->ptr;
uint32_t cipher;
const char *cipher;
struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_uint32 ("cipher",
GNUNET_JSON_spec_string ("cipher",
&cipher),
GNUNET_JSON_spec_end ()
};
@ -600,7 +619,7 @@ parse_blinded_planchet (void *cls,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
blinded_planchet->cipher = (enum TALER_DenominationCipher) cipher;
blinded_planchet->cipher = string_to_cipher (cipher);
switch (blinded_planchet->cipher)
{
case TALER_DENOMINATION_RSA:
@ -690,6 +709,91 @@ TALER_JSON_spec_blinded_planchet (const char *field,
}
/**
* Parse given JSON object to exchange withdraw values (/csr).
*
* @param cls closure, NULL
* @param root the json object representing data
* @param[out] spec where to write the data
* @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
*/
static enum GNUNET_GenericReturnValue
parse_exchange_withdraw_values (void *cls,
json_t *root,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_ExchangeWithdrawValues *ewv = spec->ptr;
const char *cipher;
struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_string ("cipher",
&cipher),
GNUNET_JSON_spec_end ()
};
const char *emsg;
unsigned int eline;
(void) cls;
if (GNUNET_OK !=
GNUNET_JSON_parse (root,
dspec,
&emsg,
&eline))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
ewv->cipher = string_to_cipher (cipher);
switch (ewv->cipher)
{
case TALER_DENOMINATION_RSA:
return GNUNET_OK;
case TALER_DENOMINATION_CS:
{
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed (
"r_pub_0",
&ewv->details.cs_values.r_pub[0],
sizeof (struct GNUNET_CRYPTO_CsRPublic)),
GNUNET_JSON_spec_fixed (
"r_pub_1",
&ewv->details.cs_values.r_pub[1],
sizeof (struct GNUNET_CRYPTO_CsRPublic)),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (root,
ispec,
&emsg,
&eline))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
default:
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
struct GNUNET_JSON_Specification
TALER_JSON_spec_exchange_withdraw_values (const char *field,
struct TALER_ExchangeWithdrawValues *
ewv)
{
struct GNUNET_JSON_Specification ret = {
.parser = &parse_exchange_withdraw_values,
.field = field,
.ptr = ewv
};
return ret;
}
/**
* Closure for #parse_i18n_string.
*/

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2021 Taler Systems SA
Copyright (C) 2021, 2022 Taler Systems SA
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
@ -61,8 +61,8 @@ TALER_JSON_pack_denom_pub (
case TALER_DENOMINATION_RSA:
ps.object
= GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_RSA),
GNUNET_JSON_pack_string ("cipher",
"RSA"),
GNUNET_JSON_pack_uint64 ("age_mask",
pk->age_mask.mask),
GNUNET_JSON_pack_rsa_public_key ("rsa_public_key",
@ -71,8 +71,8 @@ TALER_JSON_pack_denom_pub (
case TALER_DENOMINATION_CS:
ps.object
= GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_CS),
GNUNET_JSON_pack_string ("cipher",
"CS"),
GNUNET_JSON_pack_uint64 ("age_mask",
pk->age_mask.mask),
GNUNET_JSON_pack_data_varsize ("cs_public_key",
@ -99,15 +99,15 @@ TALER_JSON_pack_denom_sig (
{
case TALER_DENOMINATION_RSA:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_RSA),
GNUNET_JSON_pack_string ("cipher",
"RSA"),
GNUNET_JSON_pack_rsa_signature ("rsa_signature",
sig->details.rsa_signature));
break;
case TALER_DENOMINATION_CS:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_CS),
GNUNET_JSON_pack_string ("cipher",
"CS"),
GNUNET_JSON_pack_data_auto ("cs_signature_r",
&sig->details.cs_signature.r_point),
GNUNET_JSON_pack_data_auto ("cs_signature_s",
@ -120,6 +120,43 @@ TALER_JSON_pack_denom_sig (
}
struct GNUNET_JSON_PackSpec
TALER_JSON_pack_exchange_withdraw_values (
const char *name,
const struct TALER_ExchangeWithdrawValues *ewv)
{
struct GNUNET_JSON_PackSpec ps = {
.field_name = name,
};
switch (ewv->cipher)
{
case TALER_DENOMINATION_RSA:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("cipher",
"RSA"));
break;
case TALER_DENOMINATION_CS:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("cipher",
"CS"),
GNUNET_JSON_pack_data_varsize (
"r_pub_0",
&ewv->details.cs_values.r_pub[0],
sizeof(struct GNUNET_CRYPTO_CsRPublic)),
GNUNET_JSON_pack_data_varsize (
"r_pub_1",
&ewv->details.cs_values.r_pub[1],
sizeof(struct GNUNET_CRYPTO_CsRPublic))
);
break;
default:
GNUNET_assert (0);
}
return ps;
}
struct GNUNET_JSON_PackSpec
TALER_JSON_pack_blinded_denom_sig (
const char *name,
@ -133,15 +170,15 @@ TALER_JSON_pack_blinded_denom_sig (
{
case TALER_DENOMINATION_RSA:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_RSA),
GNUNET_JSON_pack_string ("cipher",
"RSA"),
GNUNET_JSON_pack_rsa_signature ("blinded_rsa_signature",
sig->details.blinded_rsa_signature));
break;
case TALER_DENOMINATION_CS:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_CS),
GNUNET_JSON_pack_string ("cipher",
"CS"),
GNUNET_JSON_pack_uint64 ("b",
sig->details.blinded_cs_answer.b),
GNUNET_JSON_pack_data_auto ("s",
@ -167,8 +204,8 @@ TALER_JSON_pack_blinded_planchet (
{
case TALER_DENOMINATION_RSA:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_RSA),
GNUNET_JSON_pack_string ("cipher",
"RSA"),
GNUNET_JSON_pack_data_varsize (
"rsa_blinded_planchet",
blinded_planchet->details.rsa_blinded_planchet.blinded_msg,
@ -176,8 +213,8 @@ TALER_JSON_pack_blinded_planchet (
break;
case TALER_DENOMINATION_CS:
ps.object = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("cipher",
TALER_DENOMINATION_CS),
GNUNET_JSON_pack_string ("cipher",
"CS"),
GNUNET_JSON_pack_data_auto (
"cs_nonce",
&blinded_planchet->details.cs_blinded_planchet.nonce),

View File

@ -365,12 +365,6 @@ version_completed_cb (void *cls,
/* ********************* library internal API ********* */
/**
* Get the context of a auditor.
*
* @param h the auditor handle to query
* @return ctx context to execute jobs in
*/
struct GNUNET_CURL_Context *
TALER_AUDITOR_handle_to_context_ (struct TALER_AUDITOR_Handle *h)
{
@ -378,13 +372,7 @@ TALER_AUDITOR_handle_to_context_ (struct TALER_AUDITOR_Handle *h)
}
/**
* Check if the handle is ready to process requests.
*
* @param h the auditor handle to query
* @return #GNUNET_YES if we are ready, #GNUNET_NO if not
*/
int
enum GNUNET_GenericReturnValue
TALER_AUDITOR_handle_is_ready_ (struct TALER_AUDITOR_Handle *h)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -396,13 +384,6 @@ TALER_AUDITOR_handle_is_ready_ (struct TALER_AUDITOR_Handle *h)
}
/**
* Obtain the URL to use for an API request.
*
* @param h handle for the auditor
* @param path Taler API path (i.e. "/deposit-confirmation")
* @return the full URL to use with cURL
*/
char *
TALER_AUDITOR_path_to_url_ (struct TALER_AUDITOR_Handle *h,
const char *path)
@ -417,21 +398,6 @@ TALER_AUDITOR_path_to_url_ (struct TALER_AUDITOR_Handle *h,
/* ********************* public API ******************* */
/**
* Initialise a connection to the auditor. Will connect to the
* auditor and obtain information about the auditor's master public
* key and the auditor's auditor. The respective information will
* be passed to the @a version_cb once available, and all future
* interactions with the auditor will be checked to be signed
* (where appropriate) by the respective master key.
*
* @param ctx the context
* @param url HTTP base URL for the auditor
* @param version_cb function to call with the
* auditor's version information
* @param version_cb_cls closure for @a version_cb
* @return the auditor handle; NULL upon error
*/
struct TALER_AUDITOR_Handle *
TALER_AUDITOR_connect (struct GNUNET_CURL_Context *ctx,
const char *url,
@ -515,11 +481,6 @@ request_version (void *cls)
}
/**
* Disconnect from the auditor
*
* @param auditor the auditor handle
*/
void
TALER_AUDITOR_disconnect (struct TALER_AUDITOR_Handle *auditor)
{

View File

@ -124,7 +124,8 @@ TALER_EXCHANGE_parse_reserve_history (
"WITHDRAW"))
{
struct TALER_ReserveSignatureP sig;
struct TALER_WithdrawRequestPS withdraw_purpose;
struct TALER_DenominationHash h_denom_pub;
struct TALER_BlindedCoinHash bch;
struct TALER_Amount withdraw_fee;
struct GNUNET_JSON_Specification withdraw_spec[] = {
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
@ -132,9 +133,9 @@ TALER_EXCHANGE_parse_reserve_history (
TALER_JSON_spec_amount_any ("withdraw_fee",
&withdraw_fee),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&withdraw_purpose.h_denomination_pub),
&h_denom_pub),
GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
&withdraw_purpose.h_coin_envelope),
&bch),
GNUNET_JSON_spec_end ()
};
@ -147,19 +148,14 @@ TALER_EXCHANGE_parse_reserve_history (
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
withdraw_purpose.purpose.size
= htonl (sizeof (withdraw_purpose));
withdraw_purpose.purpose.purpose
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
withdraw_purpose.reserve_pub = *reserve_pub;
TALER_amount_hton (&withdraw_purpose.amount_with_fee,
&amount);
/* Check that the signature is a valid withdraw request */
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
&withdraw_purpose,
&sig.eddsa_signature,
&reserve_pub->eddsa_pub))
TALER_wallet_withdraw_verify (&h_denom_pub,
&amount,
&bch,
reserve_pub,
&sig))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (withdraw_spec);
@ -172,8 +168,7 @@ TALER_EXCHANGE_parse_reserve_history (
key_state = TALER_EXCHANGE_get_keys (exchange);
dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
&withdraw_purpose.
h_denomination_pub);
&h_denom_pub);
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&withdraw_fee,
&dki->fee_withdraw)) ||
@ -193,10 +188,10 @@ TALER_EXCHANGE_parse_reserve_history (
/* Check check that the same withdraw transaction
isn't listed twice by the exchange. We use the
"uuid" array to remember the hashes of all
purposes, and compare the hashes to find
duplicates. *///
GNUNET_CRYPTO_hash (&withdraw_purpose,
ntohl (withdraw_purpose.purpose.size),
signatures, and compare the hashes to find
duplicates. */
GNUNET_CRYPTO_hash (&sig,
sizeof (sig),
&uuid[uuid_off]);
for (unsigned int i = 0; i<uuid_off; i++)
{

View File

@ -103,14 +103,9 @@ csr_ok (struct TALER_EXCHANGE_CsRHandle *csrh,
json_t *av = json_array_get (arr,
i);
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed (
"r_pub_0",
&alg_values[i].details.cs_values.r_pub_pair.r_pub[0],
sizeof (struct GNUNET_CRYPTO_CsRPublic)),
GNUNET_JSON_spec_fixed (
"r_pub_1",
&alg_values[i].details.cs_values.r_pub_pair.r_pub[1],
sizeof (struct GNUNET_CRYPTO_CsRPublic)),
TALER_JSON_spec_exchange_withdraw_values (
"ewv",
&alg_values[i]),
GNUNET_JSON_spec_end ()
};

View File

@ -461,6 +461,7 @@ handle_deposit_finished (void *cls,
* @param amount the amount to be deposited
* @param h_wire hash of the merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param ech hash over contract extensions
* @param coin_pub coins public key
* @param denom_sig exchanges unblinded signature of the coin
* @param denom_pub denomination key with which the coin is signed

View File

@ -258,19 +258,6 @@ handle_deposit_wtid_finished (void *cls,
}
/**
* Obtain wire transfer details about an existing deposit operation.
*
* @param exchange the exchange to query
* @param merchant_priv the merchant's private key
* @param h_wire hash of merchant's wire transfer details
* @param h_contract_terms hash of the proposal data from the contract
* between merchant and customer
* @param coin_pub public key of the coin
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to abort request
*/
struct TALER_EXCHANGE_DepositGetHandle *
TALER_EXCHANGE_deposits_get (
struct TALER_EXCHANGE_Handle *exchange,
@ -387,12 +374,6 @@ TALER_EXCHANGE_deposits_get (
}
/**
* Cancel /deposits/$WTID request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param dwh the wire deposits request handle
*/
void
TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh)
{

View File

@ -161,19 +161,11 @@ TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *exchange,
struct GNUNET_CURL_Context *ctx;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_ReserveSignatureP reserve_sig;
struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
.size = htonl (sizeof (purpose)),
.purpose = htonl (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP)
};
/* FIXME: move to util/wallet-signatures.c! */
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&reserve_pub.eddsa_pub);
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign_ (&reserve_priv->eddsa_priv,
&purpose,
&reserve_sig.eddsa_signature));
TALER_wallet_account_setup_sign (reserve_priv,
&reserve_sig);
req = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("reserve_pub",
&reserve_pub),

View File

@ -77,9 +77,7 @@ struct TALER_EXCHANGE_LinkHandle
* @param json json reply with the data for one coin
* @param coin_num number of the coin
* @param trans_pub our transfer public key
* @param[out] coin_priv where to return private coin key
* @param[out] sig where to return private coin signature
* @param[out] pub where to return the public key for the coin
* @param[out] lci where to return coin details
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static enum GNUNET_GenericReturnValue
@ -87,28 +85,39 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
const json_t *json,
uint32_t coin_num,
const struct TALER_TransferPublicKeyP *trans_pub,
struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_DenominationSignature *sig,
struct TALER_DenominationPublicKey *pub)
struct TALER_EXCHANGE_LinkedCoinInfo *lci)
{
struct TALER_BlindedDenominationSignature bsig;
struct TALER_DenominationPublicKey rpub;
struct TALER_CoinSpendSignatureP link_sig;
union TALER_DenominationBlindingKeyP bks;
struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_CsNonce nonce;
uint32_t coin_idx;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_denom_pub ("denom_pub",
&rpub),
TALER_JSON_spec_blinded_denom_sig ("ev_sig",
&bsig),
TALER_JSON_spec_exchange_withdraw_values ("ewv",
&alg_values),
GNUNET_JSON_spec_fixed_auto ("link_sig",
&link_sig),
GNUNET_JSON_spec_uint32 ("coin_idx",
&coin_idx),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
&nonce)),
GNUNET_JSON_spec_end ()
};
struct TALER_TransferSecretP secret;
struct TALER_PlanchetSecretsP ps;
struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash;
/* parse reply */
memset (&nonce,
0,
sizeof (nonce));
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
@ -122,21 +131,42 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
&secret);
TALER_transfer_secret_to_planchet_secret (&secret,
coin_num,
&ps);
// TODO: implement cipher handling
alg_values.cipher = TALER_DENOMINATION_RSA;
TALER_planchet_setup_coin_priv (&ps,
&lci->ps);
TALER_planchet_setup_coin_priv (&lci->ps,
&alg_values,
coin_priv);
TALER_planchet_blinding_secret_create (&ps,
&lci->coin_priv);
TALER_planchet_blinding_secret_create (&lci->ps,
&alg_values,
&bks);
if (GNUNET_OK !=
TALER_planchet_prepare (&rpub,
&alg_values,
&bks,
&lci->coin_priv,
&c_hash,
&pd))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
if (TALER_DENOMINATION_CS == alg_values.cipher)
{
if (GNUNET_is_zero (&nonce))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
pd.blinded_planchet.details.cs_blinded_planchet.nonce = nonce;
}
/* extract coin and signature */
if (GNUNET_OK !=
TALER_denom_sig_unblind (sig,
TALER_denom_sig_unblind (&lci->sig,
&bsig,
&bks,
&c_hash,
&alg_values,
&rpub))
{
GNUNET_break_op (0);
@ -144,28 +174,11 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
}
/* verify link_sig */
{
struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash;
struct TALER_CoinSpendPublicKeyP old_coin_pub;
struct TALER_BlindedCoinHash coin_envelope_hash;
GNUNET_CRYPTO_eddsa_key_get_public (&lh->coin_priv.eddsa_priv,
&old_coin_pub.eddsa_pub);
// TODO: implement cipher handling
alg_values.cipher = TALER_DENOMINATION_RSA;
if (GNUNET_OK !=
TALER_planchet_prepare (&rpub,
&alg_values,
&bks,
coin_priv,
&c_hash,
&pd))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
TALER_coin_ev_hash (&pd.blinded_planchet,
&pd.denom_pub_hash,
&coin_envelope_hash);
@ -185,7 +198,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
}
/* clean up */
TALER_denom_pub_deep_copy (pub,
TALER_denom_pub_deep_copy (&lci->pub,
&rpub);
GNUNET_JSON_parse_free (spec);
return GNUNET_OK;
@ -207,9 +220,9 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
unsigned int session;
unsigned int num_coins;
int ret;
struct TALER_EXCHANGE_HttpResponse hr = {
.reply = json,
.http_status = MHD_HTTP_OK
struct TALER_EXCHANGE_LinkResult lr = {
.hr.reply = json,
.hr.http_status = MHD_HTTP_OK
};
if (! json_is_array (json))
@ -262,12 +275,9 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
{
unsigned int off_coin; /* index into 1d array */
unsigned int i;
struct TALER_CoinSpendPrivateKeyP coin_privs[GNUNET_NZL (num_coins)];
struct TALER_DenominationSignature sigs[GNUNET_NZL (num_coins)];
struct TALER_DenominationPublicKey pubs[GNUNET_NZL (num_coins)];
struct TALER_EXCHANGE_LinkedCoinInfo lcis[GNUNET_NZL (num_coins)];
memset (sigs, 0, sizeof (sigs));
memset (pubs, 0, sizeof (pubs));
memset (lcis, 0, sizeof (lcis));
off_coin = 0;
for (session = 0; session<json_array_size (json); session++)
{
@ -300,6 +310,9 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
/* decode all coins */
for (i = 0; i<json_array_size (jsona); i++)
{
struct TALER_EXCHANGE_LinkedCoinInfo *lci;
lci = &lcis[i + off_coin];
GNUNET_assert (i + off_coin < num_coins);
if (GNUNET_OK !=
parse_link_coin (lh,
@ -307,9 +320,7 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
i),
i,
&trans_pub,
&coin_privs[i + off_coin],
&sigs[i + off_coin],
&pubs[i + off_coin]))
lci))
{
GNUNET_break_op (0);
break;
@ -329,12 +340,10 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
if (off_coin == num_coins)
{
lr.details.success.num_coins = num_coins;
lr.details.success.coins = lcis;
lh->link_cb (lh->link_cb_cls,
&hr,
num_coins,
coin_privs,
sigs,
pubs);
&lr);
lh->link_cb = NULL;
ret = GNUNET_OK;
}
@ -348,8 +357,8 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
GNUNET_assert (off_coin <= num_coins);
for (i = 0; i<off_coin; i++)
{
TALER_denom_sig_free (&sigs[i]);
TALER_denom_pub_free (&pubs[i]);
TALER_denom_sig_free (&lcis[i].sig);
TALER_denom_pub_free (&lcis[i].pub);
}
}
return ret;
@ -371,16 +380,16 @@ handle_link_finished (void *cls,
{
struct TALER_EXCHANGE_LinkHandle *lh = cls;
const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = {
.reply = j,
.http_status = (unsigned int) response_code
struct TALER_EXCHANGE_LinkResult lr = {
.hr.reply = j,
.hr.http_status = (unsigned int) response_code
};
lh->job = NULL;
switch (response_code)
{
case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
@ -388,49 +397,45 @@ handle_link_finished (void *cls,
j))
{
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
lr.hr.http_status = 0;
lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
GNUNET_assert (NULL == lh->link_cb);
TALER_EXCHANGE_link_cancel (lh);
return;
case MHD_HTTP_BAD_REQUEST:
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
lr.hr.ec = TALER_JSON_get_error_code (j);
lr.hr.hint = TALER_JSON_get_error_hint (j);
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
lr.hr.ec = TALER_JSON_get_error_code (j);
lr.hr.hint = TALER_JSON_get_error_hint (j);
/* Nothing really to verify, exchange says this coin was not melted; we
should pass the JSON reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
lr.hr.ec = TALER_JSON_get_error_code (j);
lr.hr.hint = TALER_JSON_get_error_hint (j);
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
lr.hr.ec = TALER_JSON_get_error_code (j);
lr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange link\n",
(unsigned int) response_code,
(int) hr.ec);
(int) lr.hr.ec);
break;
}
if (NULL != lh->link_cb)
lh->link_cb (lh->link_cb_cls,
&hr,
0,
NULL,
NULL,
NULL);
&lr);
TALER_EXCHANGE_link_cancel (lh);
}

View File

@ -78,7 +78,7 @@ struct TALER_EXCHANGE_MeltHandle
/**
* The secret the entire melt operation is seeded from.
*/
const struct TALER_PlanchetSecretsP *ps;
struct TALER_RefreshMasterSecretP rms;
/**
* Details about the characteristics of the requested melt operation.
@ -86,16 +86,10 @@ struct TALER_EXCHANGE_MeltHandle
const struct TALER_EXCHANGE_RefreshData *rd;
/**
* Array of `num_fresh_coins` contributory values of
* the exchange to the melt operation.
* Array of `num_fresh_coins` per-coin values
* returned from melt operation.
*/
struct TALER_ExchangeWithdrawValues *alg_values;
/**
* Array of `num_fresh_coins` blinding secrets
* used for blinding the coins.
*/
union TALER_DenominationBlindingKeyP *bks;
struct TALER_EXCHANGE_MeltBlindingDetail *mbds;
/**
* Handle for the preflight request, or NULL.
@ -111,6 +105,12 @@ struct TALER_EXCHANGE_MeltHandle
* @brief Public information about the coin's denomination key
*/
const struct TALER_EXCHANGE_DenomPublicKey *dki;
/**
* Gamma value chosen by the exchange during melt.
*/
uint32_t noreveal_index;
};
@ -118,17 +118,15 @@ struct TALER_EXCHANGE_MeltHandle
* Verify that the signature on the "200 OK" response
* from the exchange is valid.
*
* @param mh melt handle
* @param[in,out] mh melt handle
* @param json json reply with the signature
* @param[out] exchange_pub public key of the exchange used for the signature
* @param[out] noreveal_index set to the noreveal index selected by the exchange
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/
static enum GNUNET_GenericReturnValue
verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
const json_t *json,
struct TALER_ExchangePublicKeyP *exchange_pub,
uint32_t *noreveal_index)
struct TALER_ExchangePublicKeyP *exchange_pub)
{
struct TALER_ExchangeSignatureP exchange_sig;
const struct TALER_EXCHANGE_Keys *key_state;
@ -138,7 +136,7 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
exchange_pub),
GNUNET_JSON_spec_uint32 ("noreveal_index",
noreveal_index),
&mh->noreveal_index),
GNUNET_JSON_spec_end ()
};
@ -161,30 +159,21 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
}
/* check that noreveal index is in permitted range */
if (TALER_CNC_KAPPA <= *noreveal_index)
if (TALER_CNC_KAPPA <= mh->noreveal_index)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
/* verify signature by exchange -- FIXME: move to util! */
if (GNUNET_OK !=
TALER_exchange_melt_confirmation_verify (
&mh->md.rc,
mh->noreveal_index,
exchange_pub,
&exchange_sig))
{
struct TALER_RefreshMeltConfirmationPS confirm = {
.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT),
.purpose.size = htonl (sizeof (confirm)),
.rc = mh->md.rc,
.noreveal_index = htonl (*noreveal_index)
};
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
&confirm,
&exchange_sig.eddsa_signature,
&exchange_pub->eddsa_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
@ -341,60 +330,45 @@ handle_melt_finished (void *cls,
const void *response)
{
struct TALER_EXCHANGE_MeltHandle *mh = cls;
uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
struct TALER_ExchangePublicKeyP exchange_pub;
const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = {
.reply = j,
.http_status = (unsigned int) response_code
struct TALER_EXCHANGE_MeltResponse mr = {
.hr.reply = j,
.hr.http_status = (unsigned int) response_code
};
mh->job = NULL;
switch (response_code)
{
case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
mr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
verify_melt_signature_ok (mh,
j,
&exchange_pub,
&noreveal_index))
&mr.details.success.sign_key))
{
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
}
if (NULL != mh->melt_cb)
{
mh->melt_cb (mh->melt_cb_cls,
&hr,
(0 == hr.http_status)
? 0
: mh->rd->fresh_pks_len,
(0 == hr.http_status)
? NULL
: mh->alg_values,
(0 == hr.http_status)
? NULL
: mh->bks,
noreveal_index,
(0 == hr.http_status)
? NULL
: &exchange_pub);
mh->melt_cb = NULL;
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
break;
}
mr.details.success.noreveal_index = mh->noreveal_index;
mr.details.success.num_mbds = mh->rd->fresh_pks_len;
mr.details.success.mbds = mh->mbds;
mh->melt_cb (mh->melt_cb_cls,
&mr);
mh->melt_cb = NULL;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.ec = TALER_JSON_get_error_code (j);
mr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
hr.ec = TALER_JSON_get_error_code (j);
switch (hr.ec)
mr.hr.ec = TALER_JSON_get_error_code (j);
switch (mr.hr.ec)
{
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
/* Double spending; check signatures on transaction history */
@ -403,9 +377,9 @@ handle_melt_finished (void *cls,
j))
{
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
mr.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
@ -414,16 +388,16 @@ handle_melt_finished (void *cls,
j))
{
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
mr.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
default:
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
mr.hr.hint = TALER_JSON_get_error_hint (j);
break;
}
break;
@ -431,44 +405,46 @@ handle_melt_finished (void *cls,
/* Nothing really to verify, exchange says one of the signatures is
invalid; assuming we checked them, this should never happen, we
should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.ec = TALER_JSON_get_error_code (j);
mr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.ec = TALER_JSON_get_error_code (j);
mr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.ec = TALER_JSON_get_error_code (j);
mr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
mr.hr.ec = TALER_JSON_get_error_code (j);
mr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange melt\n",
(unsigned int) response_code,
hr.ec);
mr.hr.ec);
GNUNET_break_op (0);
break;
}
if (NULL != mh->melt_cb)
mh->melt_cb (mh->melt_cb_cls,
&hr,
0,
NULL,
NULL,
UINT32_MAX,
NULL);
&mr);
TALER_EXCHANGE_melt_cancel (mh);
}
/**
* Start the actual melt operation, now that we have
* the exchange's input values.
*
* @param[in,out] mh melt operation to run
* @return #GNUNET_OK if we could start the operation
*/
static enum GNUNET_GenericReturnValue
start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
{
@ -479,11 +455,14 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
struct TALER_CoinSpendSignatureP confirm_sig;
char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
struct TALER_DenominationHash h_denom_pub;
struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len];
for (unsigned int i = 0; i<mh->rd->fresh_pks_len; i++)
alg_values[i] = mh->mbds[i].alg_value;
if (GNUNET_OK !=
TALER_EXCHANGE_get_melt_data_ (mh->ps,
TALER_EXCHANGE_get_melt_data_ (&mh->rms,
mh->rd,
mh->alg_values,
alg_values,
&mh->md))
{
GNUNET_break (0);
@ -563,18 +542,22 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
}
/**
* The melt request @a mh failed, return an error to
* the application and cancel the operation.
*
* @param[in] mh melt request that failed
*/
static void
fail_mh (struct TALER_EXCHANGE_MeltHandle *mh)
fail_mh (struct TALER_EXCHANGE_MeltHandle *mh,
enum TALER_ErrorCode ec)
{
// FIXME: do return more than NULLs if
// the /csr failed!
struct TALER_EXCHANGE_MeltResponse mr = {
.hr.ec = ec
};
mh->melt_cb (mh->melt_cb_cls,
NULL,
0,
NULL,
NULL,
UINT32_MAX,
NULL);
&mr);
TALER_EXCHANGE_melt_cancel (mh);
}
@ -593,17 +576,31 @@ csr_cb (void *cls,
struct TALER_EXCHANGE_MeltHandle *mh = cls;
unsigned int nks_off = 0;
mh->csr = NULL;
if (MHD_HTTP_OK != csrr->hr.http_status)
{
struct TALER_EXCHANGE_MeltResponse mr = {
.hr = csrr->hr
};
mr.hr.hint = "/csr failed";
mh->melt_cb (mh->melt_cb_cls,
&mr);
TALER_EXCHANGE_melt_cancel (mh);
return;
}
for (unsigned int i = 0; i<mh->rd->fresh_pks_len; i++)
{
const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk =
&mh->rd->fresh_pks[i];
struct TALER_ExchangeWithdrawValues *wv = &mh->alg_values[i];
struct TALER_ExchangeWithdrawValues *wv = &mh->mbds[i].alg_value;
switch (fresh_pk->key.cipher)
{
case TALER_DENOMINATION_INVALID:
GNUNET_break (0);
fail_mh (mh);
fail_mh (mh,
TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR);
return;
case TALER_DENOMINATION_RSA:
GNUNET_assert (TALER_DENOMINATION_RSA == wv->cipher);
@ -619,7 +616,8 @@ csr_cb (void *cls,
start_melt (mh))
{
GNUNET_break (0);
fail_mh (mh);
fail_mh (mh,
TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR);
return;
}
}
@ -627,7 +625,7 @@ csr_cb (void *cls,
struct TALER_EXCHANGE_MeltHandle *
TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_EXCHANGE_RefreshData *rd,
TALER_EXCHANGE_MeltCallback melt_cb,
void *melt_cb_cls)
@ -644,26 +642,24 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange));
mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */
mh->exchange = exchange;
mh->rd = rd;
mh->ps = ps;
mh->rms = *rms;
mh->melt_cb = melt_cb;
mh->melt_cb_cls = melt_cb_cls;
mh->alg_values = GNUNET_new_array (rd->fresh_pks_len,
struct TALER_ExchangeWithdrawValues);
mh->bks = GNUNET_new_array (rd->fresh_pks_len,
union TALER_DenominationBlindingKeyP);
mh->mbds = GNUNET_new_array (rd->fresh_pks_len,
struct TALER_EXCHANGE_MeltBlindingDetail);
for (unsigned int i = 0; i<rd->fresh_pks_len; i++)
{
const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = &rd->fresh_pks[i];
struct TALER_ExchangeWithdrawValues *wv = &mh->alg_values[i];
struct TALER_ExchangeWithdrawValues *wv = &mh->mbds[i].alg_value;
switch (fresh_pk->key.cipher)
{
case TALER_DENOMINATION_INVALID:
GNUNET_break (0);
GNUNET_free (mh->alg_values);
GNUNET_free (mh->bks);
GNUNET_free (mh->mbds);
GNUNET_free (mh);
return NULL;
case TALER_DENOMINATION_RSA:
@ -672,7 +668,7 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
case TALER_DENOMINATION_CS:
wv->cipher = TALER_DENOMINATION_CS;
nks[nks_off].pk = fresh_pk;
TALER_cs_refresh_nonce_derive (ps,
TALER_cs_refresh_nonce_derive (rms,
i,
&nks[nks_off].nonce);
nks_off++;
@ -719,8 +715,7 @@ TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh)
mh->csr = NULL;
}
TALER_EXCHANGE_free_melt_data_ (&mh->md); /* does not free 'md' itself */
GNUNET_free (mh->alg_values);
GNUNET_free (mh->bks);
GNUNET_free (mh->mbds);
GNUNET_free (mh->url);
TALER_curl_easy_post_finished (&mh->ctx);
GNUNET_free (mh);

View File

@ -206,7 +206,7 @@ handle_recoup_finished (void *cls,
exchange is here. We should look at the key
structure of ph->exchange, and find the smallest
_currently withdrawable_ denomination and check
if the value remaining would suffice... *///
if the value remaining would suffice... */
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
@ -285,7 +285,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_DenominationSignature *denom_sig,
const struct TALER_ExchangeWithdrawValues *exchange_vals,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
TALER_EXCHANGE_RecoupResultCallback recoup_cb,
void *recoup_cb_cls)
{
@ -322,10 +322,30 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
&h_denom_pub),
TALER_JSON_pack_denom_sig ("denom_sig",
denom_sig),
TALER_JSON_pack_exchange_withdraw_values ("ewv",
exchange_vals),
GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks));
if (TALER_DENOMINATION_CS == denom_sig->cipher)
{
struct TALER_CsNonce nonce;
// FIXME: add this to the spec!
/* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash()
it is not strictly clear that the nonce is needed. Best case would be
to find a way to include it more 'naturally' somehow, for example with
the variant union version of bks! */
TALER_cs_withdraw_nonce_derive (ps,
&nonce);
GNUNET_assert (
0 ==
json_object_set_new (recoup_obj,
"cs_nonce",
GNUNET_JSON_from_data_auto (
&nonce)));
}
{
char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2017-2021 Taler Systems SA
Copyright (C) 2017-2022 Taler Systems SA
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
@ -165,6 +165,19 @@ handle_recoup_refresh_finished (void *cls,
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
{
/* Insufficient funds, proof attached */
@ -238,19 +251,6 @@ handle_recoup_refresh_finished (void *cls,
TALER_EXCHANGE_recoup_refresh_cancel (ph);
return;
}
case MHD_HTTP_FORBIDDEN:
/* Nothing really to verify, exchange says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_GONE:
/* Kind of normal: the money was already sent to the merchant
(it was too late for the refund). */
@ -287,7 +287,9 @@ TALER_EXCHANGE_recoup_refresh (
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_DenominationSignature *denom_sig,
const struct TALER_ExchangeWithdrawValues *exchange_vals,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_PlanchetMasterSecretP *ps,
unsigned int idx,
TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb,
void *recoup_cb_cls)
{
@ -302,6 +304,7 @@ TALER_EXCHANGE_recoup_refresh (
struct TALER_CoinSpendPrivateKeyP coin_priv;
union TALER_DenominationBlindingKeyP bks;
GNUNET_assert (NULL != recoup_cb);
GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange));
TALER_planchet_setup_coin_priv (ps,
@ -323,11 +326,33 @@ TALER_EXCHANGE_recoup_refresh (
&h_denom_pub),
TALER_JSON_pack_denom_sig ("denom_sig",
denom_sig),
TALER_JSON_pack_exchange_withdraw_values ("ewv",
exchange_vals),
GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks));
if (TALER_DENOMINATION_CS == denom_sig->cipher)
{
struct TALER_CsNonce nonce;
// FIXME: add this to the spec!
/* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash()
it is not strictly clear that the nonce is needed. Best case would be
to find a way to include it more 'naturally' somehow, for example with
the variant union version of bks! */
TALER_cs_refresh_nonce_derive (rms,
idx,
&nonce);
GNUNET_assert (
0 ==
json_object_set_new (recoup_obj,
"cs_nonce",
GNUNET_JSON_from_data_auto (
&nonce)));
}
{
char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
char *end;

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015-2021 Taler Systems SA
Copyright (C) 2015-2022 Taler Systems SA
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
@ -26,16 +26,28 @@
void
TALER_EXCHANGE_free_melt_data_ (struct MeltData *md)
{
for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
{
struct TALER_RefreshCoinData *rcds = md->rcd[i];
if (NULL == rcds)
continue;
for (unsigned int j = 0; j < md->num_fresh_coins; j++)
TALER_blinded_planchet_free (&rcds[j].blinded_planchet);
GNUNET_free (rcds);
}
TALER_denom_pub_free (&md->melted_coin.pub_key);
TALER_denom_sig_free (&md->melted_coin.sig);
if (NULL != md->fresh_pks)
if (NULL != md->fcds)
{
for (unsigned int i = 0; i<md->num_fresh_coins; i++)
TALER_denom_pub_free (&md->fresh_pks[i]);
GNUNET_free (md->fresh_pks);
for (unsigned int j = 0; j<md->num_fresh_coins; j++)
{
struct FreshCoinData *fcd = &md->fcds[j];
TALER_denom_pub_free (&fcd->fresh_pk);
}
GNUNET_free (md->fcds);
}
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
GNUNET_free (md->fresh_coins[i]);
/* Finally, clean up a bit... */
GNUNET_CRYPTO_zero_keys (md,
sizeof (struct MeltData));
@ -44,15 +56,14 @@ TALER_EXCHANGE_free_melt_data_ (struct MeltData *md)
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_get_melt_data_ (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_EXCHANGE_RefreshData *rd,
const struct TALER_ExchangeWithdrawValues *alg_values,
struct MeltData *md)
{
struct TALER_Amount total;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
struct TALER_CsNonce nonces[rd->fresh_pks_len];
GNUNET_CRYPTO_eddsa_key_get_public (&rd->melt_priv.eddsa_priv,
&coin_pub.eddsa_pub);
@ -73,29 +84,42 @@ TALER_EXCHANGE_get_melt_data_ (
&rd->melt_pk.key);
TALER_denom_sig_deep_copy (&md->melted_coin.sig,
&rd->melt_sig);
md->fresh_pks = GNUNET_new_array (rd->fresh_pks_len,
struct TALER_DenominationPublicKey);
for (unsigned int i = 0; i<rd->fresh_pks_len; i++)
md->fcds = GNUNET_new_array (md->num_fresh_coins,
struct FreshCoinData);
for (unsigned int j = 0; j<rd->fresh_pks_len; j++)
{
TALER_denom_pub_deep_copy (&md->fresh_pks[i],
&rd->fresh_pks[i].key);
if ( (0 >
TALER_amount_add (&total,
&total,
&rd->fresh_pks[i].value)) ||
(0 >
TALER_amount_add (&total,
&total,
&rd->fresh_pks[i].fee_withdraw)) )
struct FreshCoinData *fcd = &md->fcds[j];
if (alg_values[j].cipher != rd->fresh_pks[j].key.cipher)
{
GNUNET_break (0);
TALER_EXCHANGE_free_melt_data_ (md);
return GNUNET_SYSERR;
}
if (TALER_DENOMINATION_CS == alg_values[j].cipher)
{
TALER_cs_refresh_nonce_derive (
rms,
j,
&nonces[j]);
}
TALER_denom_pub_deep_copy (&fcd->fresh_pk,
&rd->fresh_pks[j].key);
if ( (0 >
TALER_amount_add (&total,
&total,
&rd->fresh_pks[j].value)) ||
(0 >
TALER_amount_add (&total,
&total,
&rd->fresh_pks[j].fee_withdraw)) )
{
GNUNET_break (0);
TALER_EXCHANGE_free_melt_data_ (md);
memset (md,
0,
sizeof (*md));
return GNUNET_SYSERR;
}
}
/* verify that melt_amount is above total cost */
if (1 ==
TALER_amount_cmp (&total,
@ -105,79 +129,79 @@ TALER_EXCHANGE_get_melt_data_ (
@a melt_amount. This is not OK. */
GNUNET_break (0);
TALER_EXCHANGE_free_melt_data_ (md);
memset (md,
0,
sizeof (*md));
return GNUNET_SYSERR;
}
/* build up coins */
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
{
struct TALER_TransferSecretP trans_sec;
TALER_planchet_secret_to_transfer_priv (
ps,
rms,
i,
&md->melted_coin.transfer_priv[i]);
&md->transfer_priv[i]);
GNUNET_CRYPTO_ecdhe_key_get_public (
&md->melted_coin.transfer_priv[i].ecdhe_priv,
&rce[i].transfer_pub.ecdhe_pub);
&md->transfer_priv[i].ecdhe_priv,
&md->transfer_pub[i].ecdhe_pub);
TALER_link_derive_transfer_secret (&rd->melt_priv,
&md->melted_coin.transfer_priv[i],
&trans_sec[i]);
md->fresh_coins[i] = GNUNET_new_array (rd->fresh_pks_len,
struct TALER_PlanchetSecretsP);
rce[i].new_coins = GNUNET_new_array (rd->fresh_pks_len,
struct TALER_RefreshCoinData);
&md->transfer_priv[i],
&trans_sec);
md->rcd[i] = GNUNET_new_array (rd->fresh_pks_len,
struct TALER_RefreshCoinData);
for (unsigned int j = 0; j<rd->fresh_pks_len; j++)
{
struct TALER_PlanchetSecretsP *fc = &md->fresh_coins[i][j];
struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
struct FreshCoinData *fcd = &md->fcds[j];
struct TALER_CoinSpendPrivateKeyP *coin_priv = &fcd->coin_priv;
struct TALER_PlanchetMasterSecretP *ps = &fcd->ps[i];
struct TALER_RefreshCoinData *rcd = &md->rcd[i][j];
union TALER_DenominationBlindingKeyP *bks = &fcd->bks[i];
struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash;
struct TALER_CoinSpendPrivateKeyP coin_priv;
union TALER_DenominationBlindingKeyP bks;
TALER_transfer_secret_to_planchet_secret (&trans_sec[i],
TALER_transfer_secret_to_planchet_secret (&trans_sec,
j,
fc);
TALER_planchet_setup_coin_priv (fc,
ps);
TALER_planchet_setup_coin_priv (ps,
&alg_values[j],
&coin_priv);
TALER_planchet_blinding_secret_create (fc,
coin_priv);
TALER_planchet_blinding_secret_create (ps,
&alg_values[j],
&bks);
bks);
if (TALER_DENOMINATION_CS == alg_values[j].cipher)
pd.blinded_planchet.details.cs_blinded_planchet.nonce = nonces[j];
if (GNUNET_OK !=
TALER_planchet_prepare (&md->fresh_pks[j],
TALER_planchet_prepare (&fcd->fresh_pk,
&alg_values[j],
&bks,
&coin_priv,
bks,
coin_priv,
&c_hash,
&pd))
{
GNUNET_break_op (0);
TALER_EXCHANGE_free_melt_data_ (md);
memset (md,
0,
sizeof (*md));
return GNUNET_SYSERR;
}
rcd->dk = &md->fresh_pks[j];
rcd->blinded_planchet = pd.blinded_planchet;
rcd->dk = &fcd->fresh_pk;
}
}
/* Compute refresh commitment */
TALER_refresh_get_commitment (&md->rc,
TALER_CNC_KAPPA,
rd->fresh_pks_len,
rce,
&coin_pub,
&rd->melt_amount);
for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
/* Finally, compute refresh commitment */
{
for (unsigned int j = 0; j < rd->fresh_pks_len; j++)
TALER_blinded_planchet_free (&rce[i].new_coins[j].blinded_planchet);
GNUNET_free (rce[i].new_coins);
struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
{
rce[i].transfer_pub = md->transfer_pub[i];
rce[i].new_coins = md->rcd[i];
}
TALER_refresh_get_commitment (&md->rc,
TALER_CNC_KAPPA,
rd->fresh_pks_len,
rce,
&coin_pub,
&rd->melt_amount);
}
return GNUNET_OK;
}

View File

@ -52,11 +52,6 @@ struct MeltedCoin
*/
struct TALER_Amount original_value;
/**
* Transfer private keys for each cut-and-choose dimension.
*/
struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
/**
* Timestamp indicating when coins of this denomination become invalid.
*/
@ -75,6 +70,37 @@ struct MeltedCoin
};
/**
* Data we keep for each fresh coin created in the
* melt process.
*/
struct FreshCoinData
{
/**
* Denomination public key of the coin.
*/
struct TALER_DenominationPublicKey fresh_pk;
/**
* Array of planchet secrets for the coins, depending
* on the cut-and-choose.
*/
struct TALER_PlanchetMasterSecretP ps[TALER_CNC_KAPPA];
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Blinding key secrets for the coins, depending on the
* cut-and-choose.
*/
union TALER_DenominationBlindingKeyP bks[TALER_CNC_KAPPA];
};
/**
* Melt data in non-serialized format for convenient processing.
*/
@ -86,47 +112,62 @@ struct MeltData
*/
struct TALER_RefreshCommitmentP rc;
/**
* Number of coins we are creating
*/
uint16_t num_fresh_coins;
/**
* Information about the melted coin.
*/
struct MeltedCoin melted_coin;
/**
* Array of @e num_fresh_coins denomination keys for the coins to be
* freshly exchangeed.
* Array of length @e num_fresh_coins with information
* about each fresh coin.
*/
struct TALER_DenominationPublicKey *fresh_pks;
struct FreshCoinData *fcds;
/**
* Array of @e num_fresh_coins with exchange contributions
* made during the refresh.
* Transfer secrets, one per cut and choose.
*/
struct TALER_ExchangeWithdrawValues *exchange_vals;
struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
/**
* Arrays of @e num_fresh_coins with information about the fresh
* coins to be created, for each cut-and-choose dimension.
* Transfer private keys for each cut-and-choose dimension.
*/
struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
/**
* Transfer public key of this commitment.
*/
struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
/**
* Transfer secrets, one per cut and choose.
*/
struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
/**
* Blinded planchets and denominations of the fresh coins, depending on the cut-and-choose. Array of length
* @e num_fresh_coins.
*/
struct TALER_RefreshCoinData *rcd[TALER_CNC_KAPPA];
/**
* Number of coins we are creating
*/
uint16_t num_fresh_coins;
};
/**
* Compute the melt data from the refresh data and secret.
*
* @param ps secret internals of the refresh-reveal operation
* @param rms secret internals of the refresh-reveal operation
* @param rd refresh data with the characteristics of the operation
* @param alg_values contributions from the exchange into the melt
* @param[out] rd where to write the derived melt data
* @param[out] md where to write the derived melt data
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_get_melt_data_ (
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_EXCHANGE_RefreshData *rd,
const struct TALER_ExchangeWithdrawValues *alg_values,
struct MeltData *md);
@ -136,7 +177,7 @@ TALER_EXCHANGE_get_melt_data_ (
* Free all information associated with a melting session. Note
* that we allow the melting session to be only partially initialized,
* as we use this function also when freeing melt data that was not
* fully initialized (i.e. due to failures in #TALER_EXCHANGE_deserialize_melt_data_()).
* fully initialized.
*
* @param[in] md melting data to release, the pointer itself is NOT
* freed (as it is typically not allocated by itself)

View File

@ -60,6 +60,11 @@ struct TALER_EXCHANGE_RefreshesRevealHandle
*/
struct GNUNET_CURL_Job *job;
/**
* Exchange-contributed values to the operation.
*/
struct TALER_ExchangeWithdrawValues *alg_values;
/**
* Function to call with the result.
*/
@ -84,26 +89,23 @@ struct TALER_EXCHANGE_RefreshesRevealHandle
/**
* We got a 200 OK response for the /refreshes/$RCH/reveal operation.
* Extract the coin signatures and return them to the caller.
* The signatures we get from the exchange is for the blinded value.
* Thus, we first must unblind them and then should verify their
* validity.
* We got a 200 OK response for the /refreshes/$RCH/reveal operation. Extract
* the coin signatures and return them to the caller. The signatures we get
* from the exchange is for the blinded value. Thus, we first must unblind
* them and then should verify their validity.
*
* If everything checks out, we return the unblinded signatures
* to the application via the callback.
*
* @param rrh operation handle
* @param json reply from the exchange
* @param[out] sigs array of length `num_fresh_coins`, initialized to contain the coin private keys
* @param[out] sigs array of length `num_fresh_coins`, initialized to contain signatures
* @param[out] rcis array of length `num_fresh_coins`, initialized to contain the coin data
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
static enum GNUNET_GenericReturnValue
refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
const json_t *json,
struct TALER_CoinSpendPrivateKeyP *coin_privs,
struct TALER_DenominationSignature *sigs)
struct TALER_EXCHANGE_RevealedCoinInfo *rcis)
{
json_t *jsona;
struct GNUNET_JSON_Specification outer_spec[] = {
@ -136,11 +138,12 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
}
for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++)
{
const struct TALER_PlanchetSecretsP *fc;
struct TALER_DenominationPublicKey *pk;
struct TALER_EXCHANGE_RevealedCoinInfo *rci =
&rcis[i];
const struct FreshCoinData *fcd = &rrh->md.fcds[i];
const struct TALER_DenominationPublicKey *pk;
json_t *jsonai;
struct TALER_BlindedDenominationSignature blind_sig;
struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_CoinPubHash coin_hash;
struct GNUNET_JSON_Specification spec[] = {
@ -151,8 +154,9 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
struct TALER_FreshCoin coin;
union TALER_DenominationBlindingKeyP bks;
fc = &rrh->md.fresh_coins[rrh->noreveal_index][i];
pk = &rrh->md.fresh_pks[i];
rci->ps = fcd->ps[rrh->noreveal_index];
rci->bks = fcd->bks[rrh->noreveal_index];
pk = &fcd->fresh_pk;
jsonai = json_array_get (jsona, i);
GNUNET_assert (NULL != jsonai);
@ -166,17 +170,15 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
return GNUNET_SYSERR;
}
// TODO: implement cipher handling
alg_values.cipher = TALER_DENOMINATION_RSA;
TALER_planchet_setup_coin_priv (fc,
&alg_values,
&coin_privs[i]);
TALER_planchet_blinding_secret_create (fc,
&alg_values,
TALER_planchet_setup_coin_priv (&rci->ps,
&rrh->alg_values[i],
&rci->coin_priv);
TALER_planchet_blinding_secret_create (&rci->ps,
&rrh->alg_values[i],
&bks);
/* needed to verify the signature, and we didn't store it earlier,
hence recomputing it here... */
GNUNET_CRYPTO_eddsa_key_get_public (&coin_privs[i].eddsa_priv,
GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
/* FIXME-Oec: Age commitment hash. */
TALER_coin_pub_hash (&coin_pub,
@ -186,9 +188,9 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
TALER_planchet_to_coin (pk,
&blind_sig,
&bks,
&coin_privs[i],
&rci->coin_priv,
&coin_hash,
&alg_values,
&rrh->alg_values[i],
&coin))
{
GNUNET_break_op (0);
@ -197,7 +199,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
return GNUNET_SYSERR;
}
GNUNET_JSON_parse_free (spec);
sigs[i] = coin.sig;
rci->sig = coin.sig;
}
GNUNET_JSON_parse_free (outer_spec);
return GNUNET_OK;
@ -219,90 +221,86 @@ handle_refresh_reveal_finished (void *cls,
{
struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls;
const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = {
.reply = j,
.http_status = (unsigned int) response_code
struct TALER_EXCHANGE_RevealResult rr = {
.hr.reply = j,
.hr.http_status = (unsigned int) response_code
};
rrh->job = NULL;
switch (response_code)
{
case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
struct TALER_DenominationSignature sigs[rrh->md.num_fresh_coins];
struct TALER_CoinSpendPrivateKeyP coin_privs[rrh->md.num_fresh_coins];
struct TALER_EXCHANGE_RevealedCoinInfo rcis[rrh->md.num_fresh_coins];
enum GNUNET_GenericReturnValue ret;
memset (sigs,
memset (rcis,
0,
sizeof (sigs));
sizeof (rcis));
ret = refresh_reveal_ok (rrh,
j,
coin_privs,
sigs);
rcis);
if (GNUNET_OK != ret)
{
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
rr.hr.http_status = 0;
rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
else
{
GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA);
rr.details.success.num_coins = rrh->md.num_fresh_coins;
rr.details.success.coins = rcis;
rrh->reveal_cb (rrh->reveal_cb_cls,
&hr,
rrh->md.num_fresh_coins,
coin_privs,
sigs);
&rr);
rrh->reveal_cb = NULL;
}
for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++)
TALER_denom_sig_free (&sigs[i]);
TALER_denom_sig_free (&rcis[i].sig);
TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
return;
}
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
rr.hr.ec = TALER_JSON_get_error_code (j);
rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
/* Nothing really to verify, exchange says our reveal is inconsistent
with our commitment, so either side is buggy; we
should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
rr.hr.ec = TALER_JSON_get_error_code (j);
rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_GONE:
/* Server claims key expired or has been revoked */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
rr.hr.ec = TALER_JSON_get_error_code (j);
rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
rr.hr.ec = TALER_JSON_get_error_code (j);
rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
rr.hr.ec = TALER_JSON_get_error_code (j);
rr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange refreshes reveal\n",
(unsigned int) response_code,
(int) hr.ec);
(int) rr.hr.ec);
break;
}
if (NULL != rrh->reveal_cb)
rrh->reveal_cb (rrh->reveal_cb_cls,
&hr,
0,
NULL,
NULL);
&rr);
TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
}
@ -310,7 +308,7 @@ handle_refresh_reveal_finished (void *cls,
struct TALER_EXCHANGE_RefreshesRevealHandle *
TALER_EXCHANGE_refreshes_reveal (
struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_RefreshMasterSecretP *rms,
const struct TALER_EXCHANGE_RefreshData *rd,
unsigned int num_coins,
const struct TALER_ExchangeWithdrawValues *alg_values,
@ -327,9 +325,7 @@ TALER_EXCHANGE_refreshes_reveal (
CURL *eh;
struct GNUNET_CURL_Context *ctx;
struct MeltData md;
struct TALER_TransferPublicKeyP transfer_pub;
char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32];
struct TALER_TransferSecretP ts;
GNUNET_assert (num_coins == rd->fresh_pks_len);
if (noreveal_index >= TALER_CNC_KAPPA)
@ -348,7 +344,7 @@ TALER_EXCHANGE_refreshes_reveal (
return NULL;
}
if (GNUNET_OK !=
TALER_EXCHANGE_get_melt_data_ (ps,
TALER_EXCHANGE_get_melt_data_ (rms,
rd,
alg_values,
&md))
@ -357,75 +353,38 @@ TALER_EXCHANGE_refreshes_reveal (
return NULL;
}
/* now transfer_pub */
GNUNET_CRYPTO_ecdhe_key_get_public (
&md.melted_coin.transfer_priv[noreveal_index].ecdhe_priv,
&transfer_pub.ecdhe_pub);
TALER_link_recover_transfer_secret (&transfer_pub,
&rd->melt_priv,
&ts);
/* now new_denoms */
GNUNET_assert (NULL != (new_denoms_h = json_array ()));
GNUNET_assert (NULL != (coin_evs = json_array ()));
GNUNET_assert (NULL != (link_sigs = json_array ()));
for (unsigned int i = 0; i<md.num_fresh_coins; i++)
{
const struct TALER_RefreshCoinData *rcd = &md.rcd[noreveal_index][i];
struct TALER_DenominationHash denom_hash;
struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash;
struct TALER_PlanchetSecretsP ps;
union TALER_DenominationBlindingKeyP bks;
struct TALER_CoinSpendPrivateKeyP coin_priv;
TALER_denom_pub_hash (&md.fresh_pks[i],
TALER_denom_pub_hash (&md.fcds[i].fresh_pk,
&denom_hash);
GNUNET_assert (0 ==
json_array_append_new (new_denoms_h,
GNUNET_JSON_from_data_auto (
&denom_hash)));
TALER_transfer_secret_to_planchet_secret (&ts,
i,
&ps);
TALER_planchet_setup_coin_priv (&ps,
&alg_values[i],
&coin_priv);
TALER_planchet_blinding_secret_create (&ps,
&alg_values[i],
&bks);
if (GNUNET_OK !=
TALER_planchet_prepare (&md.fresh_pks[i],
&alg_values[i],
&bks,
&coin_priv,
&c_hash,
&pd))
{
/* This should have been noticed during the preparation stage. */
GNUNET_break (0);
json_decref (new_denoms_h);
json_decref (coin_evs);
TALER_EXCHANGE_free_melt_data_ (&md);
return NULL;
}
GNUNET_assert (
0 ==
json_array_append_new (
coin_evs,
GNUNET_JSON_PACK (
TALER_JSON_pack_blinded_planchet (
NULL,
&pd.blinded_planchet))));
GNUNET_assert (0 ==
json_array_append_new (
coin_evs,
GNUNET_JSON_PACK (
TALER_JSON_pack_blinded_planchet (
NULL,
&rcd->blinded_planchet))));
{
struct TALER_CoinSpendSignatureP link_sig;
struct TALER_BlindedCoinHash bch;
TALER_coin_ev_hash (&pd.blinded_planchet,
TALER_coin_ev_hash (&rcd->blinded_planchet,
&denom_hash,
&bch);
TALER_wallet_link_sign (
&denom_hash,
&transfer_pub,
&md.transfer_pub[noreveal_index],
&bch,
&md.melted_coin.coin_priv,
&link_sig);
@ -434,7 +393,6 @@ TALER_EXCHANGE_refreshes_reveal (
link_sigs,
GNUNET_JSON_from_data_auto (&link_sig)));
}
TALER_blinded_planchet_free (&pd.blinded_planchet);
}
/* build array of transfer private keys */
@ -443,20 +401,19 @@ TALER_EXCHANGE_refreshes_reveal (
{
if (j == noreveal_index)
{
/* This is crucial: exclude the transfer key for the
noreval index! */
/* This is crucial: exclude the transfer key for the noreval index! */
continue;
}
GNUNET_assert (0 ==
json_array_append_new (transfer_privs,
GNUNET_JSON_from_data_auto (
&md.melted_coin.transfer_priv[j])));
&md.transfer_priv[j])));
}
/* build main JSON request */
reveal_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("transfer_pub",
&transfer_pub),
&md.transfer_pub[noreveal_index]),
GNUNET_JSON_pack_array_steal ("transfer_privs",
transfer_privs),
GNUNET_JSON_pack_array_steal ("link_sigs",
@ -486,12 +443,17 @@ TALER_EXCHANGE_refreshes_reveal (
rrh->reveal_cb = reveal_cb;
rrh->reveal_cb_cls = reveal_cb_cls;
rrh->md = md;
rrh->alg_values = GNUNET_memdup (alg_values,
md.num_fresh_coins
* sizeof (struct
TALER_ExchangeWithdrawValues));
rrh->url = TEAH_path_to_url (rrh->exchange,
arg_str);
if (NULL == rrh->url)
{
json_decref (reveal_obj);
TALER_EXCHANGE_free_melt_data_ (&md);
GNUNET_free (rrh->alg_values);
GNUNET_free (rrh);
return NULL;
}
@ -507,6 +469,7 @@ TALER_EXCHANGE_refreshes_reveal (
curl_easy_cleanup (eh);
json_decref (reveal_obj);
TALER_EXCHANGE_free_melt_data_ (&md);
GNUNET_free (rrh->alg_values);
GNUNET_free (rrh->url);
GNUNET_free (rrh);
return NULL;
@ -531,6 +494,7 @@ TALER_EXCHANGE_refreshes_reveal_cancel (
GNUNET_CURL_job_cancel (rrh->job);
rrh->job = NULL;
}
GNUNET_free (rrh->alg_values);
GNUNET_free (rrh->url);
TALER_curl_easy_post_finished (&rrh->ctx);
TALER_EXCHANGE_free_melt_data_ (&rrh->md);

View File

@ -66,7 +66,7 @@ struct TALER_EXCHANGE_WithdrawHandle
/**
* Seed of the planchet.
*/
struct TALER_PlanchetSecretsP ps;
struct TALER_PlanchetMasterSecretP ps;
/**
* blinding secret
@ -185,7 +185,8 @@ handle_reserve_withdraw_finished (
/**
* Function called when stage 1 of CS withdraw is finished (request r_pub's)
*
* @param cls
* @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
* @param csrr replies from the /csr request
*/
static void
withdraw_cs_stage_two_callback (void *cls,
@ -214,6 +215,8 @@ withdraw_cs_stage_two_callback (void *cls,
TALER_planchet_blinding_secret_create (&wh->ps,
&wh->alg_values,
&wh->bks);
/* This initializes the 2nd half of the
wh->pd.blinded_planchet! */
if (GNUNET_OK !=
TALER_planchet_prepare (&wh->pk.key,
&wh->alg_values,
@ -245,7 +248,7 @@ TALER_EXCHANGE_withdraw (
struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_PlanchetSecretsP *ps,
const struct TALER_PlanchetMasterSecretP *ps,
TALER_EXCHANGE_WithdrawCallback res_cb,
void *res_cb_cls)
{
@ -297,9 +300,13 @@ TALER_EXCHANGE_withdraw (
.pk = pk,
};
wh->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
TALER_cs_withdraw_nonce_derive (ps,
&nk.nonce);
/* Note that we only initialize the first half
of the blinded_planchet here; the other part
will be done after the /csr request! */
wh->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
wh->pd.blinded_planchet.details.cs_blinded_planchet.nonce = nk.nonce;
wh->csrh = TALER_EXCHANGE_csr (exchange,
1, /* "array" length */
&nk,
@ -312,7 +319,6 @@ TALER_EXCHANGE_withdraw (
GNUNET_free (wh);
return NULL;
}
TALER_blinded_planchet_free (&wh->pd.blinded_planchet);
return wh;
}
@ -320,6 +326,7 @@ TALER_EXCHANGE_withdraw (
void
TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
{
TALER_blinded_planchet_free (&wh->pd.blinded_planchet);
if (NULL != wh->csrh)
{
TALER_EXCHANGE_csr_cancel (wh->csrh);

View File

@ -380,6 +380,7 @@ TALER_EXCHANGE_withdraw2 (
const struct TALER_EXCHANGE_DenomPublicKey *dk;
struct TALER_ReserveSignatureP reserve_sig;
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
struct TALER_BlindedCoinHash bch;
keys = TALER_EXCHANGE_get_keys (exchange);
if (NULL == keys)
@ -428,29 +429,22 @@ TALER_EXCHANGE_withdraw2 (
"/reserves/%s/withdraw",
pub_str);
}
{
struct TALER_WithdrawRequestPS req = {
.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW),
.reserve_pub = wh->reserve_pub,
.h_denomination_pub = pd->denom_pub_hash
};
TALER_amount_hton (&req.amount_with_fee,
&wh->requested_amount);
if (GNUNET_OK != TALER_coin_ev_hash (&pd->blinded_planchet,
&pd->denom_pub_hash,
&req.h_coin_envelope))
{
GNUNET_break (0);
GNUNET_free (wh);
return NULL;
}
GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
&req,
&reserve_sig.eddsa_signature);
if (GNUNET_OK !=
TALER_coin_ev_hash (&pd->blinded_planchet,
&pd->denom_pub_hash,
&bch))
{
GNUNET_break (0);
GNUNET_free (wh);
return NULL;
}
TALER_wallet_withdraw_sign (&pd->denom_pub_hash,
&wh->requested_amount,
&bch,
reserve_priv,
&reserve_sig);
{
json_t *withdraw_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("denom_pub_hash",

View File

@ -27,29 +27,6 @@
#include "taler_mhd_lib.h"
/**
* Process a POST request containing a JSON object. This function
* realizes an MHD POST processor that will (incrementally) process
* JSON data uploaded to the HTTP server. It will store the required
* state in the @a con_cls, which must be cleaned up using
* #TALER_MHD_parse_post_cleanup_callback().
*
* @param connection the MHD connection
* @param con_cls the closure (points to a `struct Buffer *`)
* @param upload_data the POST data
* @param upload_data_size number of bytes in @a upload_data
* @param json the JSON object for a completed request
* @return
* #GNUNET_YES if json object was parsed or at least
* may be parsed in the future (call again);
* `*json` will be NULL if we need to be called again,
* and non-NULL if we are done.
* #GNUNET_NO is request incomplete or invalid
* (error message was generated)
* #GNUNET_SYSERR on internal error
* (we could not even queue an error message,
* close HTTP session with MHD_NO)
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_post_json (struct MHD_Connection *connection,
void **con_cls,
@ -102,13 +79,6 @@ TALER_MHD_parse_post_json (struct MHD_Connection *connection,
}
/**
* Function called whenever we are done with a request
* to clean up our state.
*
* @param con_cls value as it was left by
* #TALER_MHD_parse_post_json(), to be cleaned up
*/
void
TALER_MHD_parse_post_cleanup_callback (void *con_cls)
{
@ -116,22 +86,7 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls)
}
/**
* Extract base32crockford encoded data from request.
*
* Queues an error response to the connection if the parameter is
* missing or invalid.
*
* @param connection the MHD connection
* @param param_name the name of the parameter with the key
* @param[out] out_data pointer to store the result
* @param out_size expected size of data
* @return
* #GNUNET_YES if the the argument is present
* #GNUNET_NO if the argument is absent or malformed
* #GNUNET_SYSERR on internal error (error response could not be sent)
*/
int
enum GNUNET_GenericReturnValue
TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
const char *param_name,
void *out_data,
@ -166,20 +121,6 @@ TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
}
/**
* Parse JSON object into components based on the given field
* specification. Generates error response on parse errors.
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param[in,out] spec field specification for the parser
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* GNUNET_JSON_parse_free() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_json_data (struct MHD_Connection *connection,
const json_t *root,
@ -217,24 +158,6 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection,
}
/**
* Parse JSON object that we (the server!) generated into components based on
* the given field specification. The difference to
* #TALER_MHD_parse_json_data() is that this function will fail
* with an HTTP failure of 500 (internal server error) in case
* parsing fails, instead of blaming it on the client with a
* 400 (#MHD_HTTP_BAD_REQUEST).
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param spec field specification for the parser
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* GNUNET_JSON_parse_free() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
const json_t *root,
@ -272,21 +195,6 @@ TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
}
/**
* Parse JSON array into components based on the given field
* specification. Generates error response on parse errors.
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param[in,out] spec field specification for the parser
* @param ... -1-terminated list of array offsets of type 'int'
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* GNUNET_JSON_parse_free() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_json_array (struct MHD_Connection *connection,
const json_t *root,

View File

@ -527,6 +527,94 @@ TALER_PQ_query_param_blinded_planchet (
}
/**
* Function called to convert input argument into SQL parameters.
*
* @param cls closure
* @param data pointer to input argument
* @param data_len number of bytes in @a data (if applicable)
* @param[out] param_values SQL data to set
* @param[out] param_lengths SQL length data to set
* @param[out] param_formats SQL format data to set
* @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
* @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
* @param scratch_length number of entries left in @a scratch
* @return -1 on error, number of offsets used in @a scratch otherwise
*/
static int
qconv_exchange_withdraw_values (void *cls,
const void *data,
size_t data_len,
void *param_values[],
int param_lengths[],
int param_formats[],
unsigned int param_length,
void *scratch[],
unsigned int scratch_length)
{
const struct TALER_ExchangeWithdrawValues *alg_values = data;
size_t tlen;
size_t len;
uint32_t be[2];
char *buf;
(void) cls;
(void) data_len;
GNUNET_assert (1 == param_length);
GNUNET_assert (scratch_length > 0);
GNUNET_break (NULL == cls);
be[0] = htonl ((uint32_t) alg_values->cipher);
be[1] = htonl (0x010000); /* magic marker: EWV */
switch (alg_values->cipher)
{
case TALER_DENOMINATION_RSA:
tlen = 0;
break;
case TALER_DENOMINATION_CS:
tlen = sizeof (struct TALER_DenominationCSPublicRPairP);
break;
default:
GNUNET_assert (0);
}
len = tlen + sizeof (be);
buf = GNUNET_malloc (len);
memcpy (buf,
&be,
sizeof (be));
switch (alg_values->cipher)
{
case TALER_DENOMINATION_RSA:
break;
case TALER_DENOMINATION_CS:
memcpy (&buf[sizeof (be)],
&alg_values->details.cs_values,
tlen);
break;
default:
GNUNET_assert (0);
}
scratch[0] = buf;
param_values[0] = (void *) buf;
param_lengths[0] = len;
param_formats[0] = 1;
return 1;
}
struct GNUNET_PQ_QueryParam
TALER_PQ_query_param_exchange_withdraw_values (
const struct TALER_ExchangeWithdrawValues *alg_values)
{
struct GNUNET_PQ_QueryParam res = {
.conv = &qconv_exchange_withdraw_values,
.data = alg_values,
.num_params = 1
};
return res;
}
/**
* Function called to convert input argument into SQL parameters.
*

Some files were not shown because too many files have changed in this diff Show More