gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated (0a459aeb -> a6778607)


From: gnunet
Subject: [taler-exchange] branch master updated (0a459aeb -> a6778607)
Date: Fri, 04 Feb 2022 16:53:30 +0100

This is an automated email from the git hooks/post-receive script.

gian-demarmels pushed a change to branch master
in repository exchange.

    from 0a459aeb fix hyphenation
     new f3fb7c29 added CS data structures, implemented CS keypair
     new 385eb51e CS planchet create and withdraw create
     new a02ab8f8 added CS get R functionality and planchet setup
     new f1ec1e70 implemented planchet_prepare for CS
     new 5d2157a8 sign_blinded implementation
     new db9b8497 add sign and verify implementation
     new 3225566c implement exchange_api_csr
     new ca247f6f fixed CS signatures and cleanup/refactoring
     new 4bcbd704 utility functions
     new cf4fd36c remove varargs in cs crypto implementation
     new 75eff152 clean up cs implementation
     new fbb6d03f fix const due to changes in TALER_planchet_prepare
     new f239b01b secmod cs signatures implementation
     new 18db69be initial cs_secmod implementation
     new 9d9d4413 setup_key for cs secmod helper
     new d1fd3a48 revocation
     new 875a8b39 implement secmod cs derive R
     new 106664ed implement TALER_CRYPTO_helper_cs_r_derive and related tests
     new 36f551ff set planchet detail cipher, add cipher checks
     new 82405b0c implement CS key handling and csr endpoint
     new 2d70c8c6 secmod CS sign implementation
     new 4c7aa097 cleanup
     new 9074e66e implement withdraw (nonce reuse check missing)
     new 9c2aefaa removed varargs
     new daa7fdcf implement spend
     new 5b7e8f9a refactoring
     new 74ce114b change TEH_keys_denomination_sign message parameter
     new ea97729b -scope needed
     new 8d85c8b5 implement feedback
     new 3510f953 -make picky gcc happy
     new bcc159de introduce new type for security module pubkeys
     new ae5f082c repair nonce check
     new be50c084 fixed nonce check, renamed WithdrawNonce
     new 22130128 include denom_pub into coin_ev_hash
     new 086cf057 refactor TALER_coin_ev_hash
     new 8674f32a denomination CIPHER field per denom
     new a6778607 resolves merge conflicts

The 37 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 contrib/gana                                       |   2 +-
 src/auditor/generate-auditor-basedb.conf           |  98 ++++
 src/bank-lib/fakebank.c                            |  52 +-
 src/benchmark/bank-benchmark.conf                  |  60 +++
 src/benchmark/benchmark.conf                       |  60 +++
 src/benchmark/taler-aggregator-benchmark.c         |  20 +-
 src/exchange-tools/taler-exchange-offline.c        | 137 ++++--
 src/exchange/Makefile.am                           |   1 +
 src/exchange/taler-exchange-httpd.c                |   8 +
 src/exchange/taler-exchange-httpd_csr.c            | 153 ++++++
 ...nge-httpd_link.h => taler-exchange-httpd_csr.h} |  28 +-
 src/exchange/taler-exchange-httpd_deposit.c        |   9 +
 src/exchange/taler-exchange-httpd_keys.c           | 249 +++++++++-
 src/exchange/taler-exchange-httpd_keys.h           |  56 ++-
 src/exchange/taler-exchange-httpd_recoup-refresh.c |  13 +-
 src/exchange/taler-exchange-httpd_recoup.c         |  22 +-
 .../taler-exchange-httpd_refreshes_reveal.c        |  18 +-
 src/exchange/taler-exchange-httpd_responses.c      |  47 ++
 src/exchange/taler-exchange-httpd_responses.h      |  13 +
 src/exchange/taler-exchange-httpd_withdraw.c       |  70 ++-
 src/exchange/test_taler_exchange_httpd.conf        |  36 ++
 src/exchange/test_taler_exchange_unix.conf         |  36 ++
 src/exchangedb/test_exchangedb.c                   | 256 +++++++++-
 src/include/taler_crypto_lib.h                     | 535 +++++++++++++++++++--
 src/include/taler_exchange_service.h               | 100 +++-
 src/include/taler_json_lib.h                       |  27 ++
 src/include/taler_signatures.h                     |   7 +-
 src/include/taler_testing_lib.h                    |  46 +-
 src/include/taler_util.h                           |   2 +-
 src/json/json_helper.c                             | 188 +++++++-
 src/json/json_pack.c                               |  92 +++-
 src/lib/Makefile.am                                |   1 +
 src/lib/exchange_api_csr.c                         | 292 +++++++++++
 src/lib/exchange_api_link.c                        |  15 +-
 src/lib/exchange_api_management_get_keys.c         |  22 +
 src/lib/exchange_api_refresh_common.c              |  10 +-
 src/lib/exchange_api_refreshes_reveal.c            |  25 +-
 src/lib/exchange_api_withdraw.c                    | 143 +++++-
 src/lib/exchange_api_withdraw2.c                   |  20 +-
 src/pq/pq_query_helper.c                           |  30 +-
 src/pq/pq_result_helper.c                          |  33 +-
 src/testing/.gitignore                             |   5 +
 .../test-taler-exchange-aggregator-postgres.conf   |  12 +
 .../test-taler-exchange-wirewatch-postgres.conf    |  12 +
 src/testing/test_auditor_api.conf                  |  64 +++
 src/testing/test_exchange_api.c                    | 147 +++++-
 src/testing/test_exchange_api.conf                 |  55 +++
 .../test_exchange_api_keys_cherry_picking.conf     |  20 +
 src/testing/test_kyc_api.conf                      |  60 +++
 src/testing/testing_api_cmd_insert_deposit.c       |  20 +-
 src/testing/testing_api_cmd_refresh.c              |   6 +-
 src/testing/testing_api_cmd_withdraw.c             |  84 +++-
 src/testing/testing_api_helpers_exchange.c         |  54 ++-
 src/util/.gitignore                                |   3 +
 src/util/Makefile.am                               |  27 +-
 src/util/crypto.c                                  | 290 +++++++++--
 .../{crypto_helper_rsa.c => crypto_helper_cs.c}    | 362 ++++++++++----
 src/util/denom.c                                   | 214 +++++++--
 src/util/secmod_signatures.c                       |  59 ++-
 ...nge-secmod-rsa.c => taler-exchange-secmod-cs.c} | 443 +++++++++--------
 ...cmod-rsa.conf => taler-exchange-secmod-cs.conf} |  10 +-
 src/util/taler-exchange-secmod-cs.h                | 270 +++++++++++
 src/util/taler-exchange-secmod-rsa.c               |  21 +
 src/util/test_crypto.c                             | 104 +++-
 src/util/{test_helper_rsa.c => test_helper_cs.c}   | 329 ++++++++++---
 .../{test_helper_rsa.conf => test_helper_cs.conf}  |   6 +-
 src/util/test_helper_rsa.c                         |  38 +-
 src/util/test_helper_rsa.conf                      |   1 +
 68 files changed, 5012 insertions(+), 736 deletions(-)
 create mode 100644 src/exchange/taler-exchange-httpd_csr.c
 copy src/exchange/{taler-exchange-httpd_link.h => taler-exchange-httpd_csr.h} 
(59%)
 create mode 100644 src/lib/exchange_api_csr.c
 copy src/util/{crypto_helper_rsa.c => crypto_helper_cs.c} (60%)
 copy src/util/{taler-exchange-secmod-rsa.c => taler-exchange-secmod-cs.c} (80%)
 copy src/util/{taler-exchange-secmod-rsa.conf => 
taler-exchange-secmod-cs.conf} (66%)
 create mode 100644 src/util/taler-exchange-secmod-cs.h
 copy src/util/{test_helper_rsa.c => test_helper_cs.c} (61%)
 copy src/util/{test_helper_rsa.conf => test_helper_cs.conf} (61%)

diff --git a/contrib/gana b/contrib/gana
index c12314df..6b74d0fa 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit c12314df0f82e192c6829a9c6cf3e9663b586da1
+Subproject commit 6b74d0faa173bbb220cdd82dcf3915dadd241e1e
diff --git a/src/auditor/generate-auditor-basedb.conf 
b/src/auditor/generate-auditor-basedb.conf
index e5de0b59..205a04a2 100644
--- a/src/auditor/generate-auditor-basedb.conf
+++ b/src/auditor/generate-auditor-basedb.conf
@@ -106,6 +106,7 @@ fee_withdraw = TESTKUDOS:0.01
 fee_deposit = TESTKUDOS:0.01
 fee_refresh = TESTKUDOS:0.01
 fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_ct_10]
@@ -117,6 +118,7 @@ fee_withdraw = TESTKUDOS:0.01
 fee_deposit = TESTKUDOS:0.01
 fee_refresh = TESTKUDOS:0.03
 fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_1]
@@ -128,6 +130,7 @@ fee_withdraw = TESTKUDOS:0.02
 fee_deposit = TESTKUDOS:0.02
 fee_refresh = TESTKUDOS:0.03
 fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_2]
@@ -139,6 +142,7 @@ fee_withdraw = TESTKUDOS:0.03
 fee_deposit = TESTKUDOS:0.03
 fee_refresh = TESTKUDOS:0.04
 fee_refund = TESTKUDOS:0.02
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_4]
@@ -150,6 +154,7 @@ fee_withdraw = TESTKUDOS:0.03
 fee_deposit = TESTKUDOS:0.03
 fee_refresh = TESTKUDOS:0.04
 fee_refund = TESTKUDOS:0.02
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_5]
@@ -161,6 +166,7 @@ fee_withdraw = TESTKUDOS:0.01
 fee_deposit = TESTKUDOS:0.01
 fee_refresh = TESTKUDOS:0.03
 fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_8]
@@ -172,6 +178,7 @@ fee_withdraw = TESTKUDOS:0.05
 fee_deposit = TESTKUDOS:0.02
 fee_refresh = TESTKUDOS:0.03
 fee_refund = TESTKUDOS:0.04
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_kudos_10]
@@ -183,8 +190,99 @@ fee_withdraw = TESTKUDOS:0.01
 fee_deposit = TESTKUDOS:0.01
 fee_refresh = TESTKUDOS:0.03
 fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_kudos_ct_1]
+value = TESTKUDOS:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.01
+fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
+rsa_keysize = 1024
+
+[coin_kudos_ct_10]
+value = TESTKUDOS:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+CIPHER = RSA
+rsa_keysize = 1024
+
+[coin_kudos_12]
+value = TESTKUDOS:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.02
+fee_deposit = TESTKUDOS:0.02
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+CIPHER = CS
+
+[coin_kudos_21]
+value = TESTKUDOS:2
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.03
+fee_deposit = TESTKUDOS:0.03
+fee_refresh = TESTKUDOS:0.04
+fee_refund = TESTKUDOS:0.02
+CIPHER = CS
+
+[coin_kudos_41]
+value = TESTKUDOS:4
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.03
+fee_deposit = TESTKUDOS:0.03
+fee_refresh = TESTKUDOS:0.04
+fee_refund = TESTKUDOS:0.02
+CIPHER = CS
+
+[coin_kudos_51]
+value = TESTKUDOS:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+CIPHER = CS
+
+[coin_kudos_81]
+value = TESTKUDOS:8
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.05
+fee_deposit = TESTKUDOS:0.02
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.04
+CIPHER = CS
+
+[coin_kudos_111]
+value = TESTKUDOS:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+CIPHER = CS
+
 [benchmark]
 BANK_DETAILS = bank_details.json
 MERCHANT_DETAILS = merchant_details.json
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index 11993e55..ab1d73b4 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -1386,19 +1386,19 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle 
*h,
                                 &json);
   switch (pr)
   {
-    case GNUNET_JSON_PR_OUT_OF_MEMORY:
-      GNUNET_break (0);
-      return MHD_NO;
-    case GNUNET_JSON_PR_CONTINUE:
-      return MHD_YES;
-    case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
-      GNUNET_break (0);
-      return MHD_NO;
-    case GNUNET_JSON_PR_JSON_INVALID:
-      GNUNET_break (0);
-      return MHD_NO;
-    case GNUNET_JSON_PR_SUCCESS:
-      break;
+  case GNUNET_JSON_PR_OUT_OF_MEMORY:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_CONTINUE:
+    return MHD_YES;
+  case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_JSON_INVALID:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_SUCCESS:
+    break;
   }
   {
     const char *debit_account;
@@ -1510,19 +1510,19 @@ handle_transfer (struct TALER_FAKEBANK_Handle *h,
                                 &json);
   switch (pr)
   {
-    case GNUNET_JSON_PR_OUT_OF_MEMORY:
-      GNUNET_break (0);
-      return MHD_NO;
-    case GNUNET_JSON_PR_CONTINUE:
-      return MHD_YES;
-    case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
-      GNUNET_break (0);
-      return MHD_NO;
-    case GNUNET_JSON_PR_JSON_INVALID:
-      GNUNET_break (0);
-      return MHD_NO;
-    case GNUNET_JSON_PR_SUCCESS:
-      break;
+  case GNUNET_JSON_PR_OUT_OF_MEMORY:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_CONTINUE:
+    return MHD_YES;
+  case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_JSON_INVALID:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_SUCCESS:
+    break;
   }
   {
     struct GNUNET_HashCode uuid;
diff --git a/src/benchmark/bank-benchmark.conf 
b/src/benchmark/bank-benchmark.conf
index 1942d551..c98b1374 100644
--- a/src/benchmark/bank-benchmark.conf
+++ b/src/benchmark/bank-benchmark.conf
@@ -81,6 +81,7 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_ct_10]
@@ -92,6 +93,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_1]
@@ -103,6 +105,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_5]
@@ -114,6 +117,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_10]
@@ -125,4 +129,60 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
+
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_6]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = RSA
+
+[coin_eur_11]
+value = EUR:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
diff --git a/src/benchmark/benchmark.conf b/src/benchmark/benchmark.conf
index 5199ee87..375665a0 100644
--- a/src/benchmark/benchmark.conf
+++ b/src/benchmark/benchmark.conf
@@ -79,6 +79,7 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_ct_10]
@@ -90,6 +91,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_1]
@@ -101,6 +103,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_5]
@@ -112,6 +115,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
 
 [coin_eur_10]
@@ -123,4 +127,60 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 2048
+
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_6]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = RSA
+
+[coin_eur_11]
+value = EUR:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
\ No newline at end of file
diff --git a/src/benchmark/taler-aggregator-benchmark.c 
b/src/benchmark/taler-aggregator-benchmark.c
index 41192100..062cb1da 100644
--- a/src/benchmark/taler-aggregator-benchmark.c
+++ b/src/benchmark/taler-aggregator-benchmark.c
@@ -490,7 +490,8 @@ run (void *cls,
     struct TALER_CoinPubHash c_hash;
     struct TALER_PlanchetDetail pd;
     struct TALER_BlindedDenominationSignature bds;
-    union TALER_DenominationBlindingKeyP bks;
+    struct TALER_PlanchetSecretsP ps;
+    struct TALER_ExchangeWithdrawValues alg_values;
     struct TALER_CoinSpendPublicKeyP coin_pub;
 
     RANDOMIZE (&coin_pub);
@@ -518,25 +519,26 @@ run (void *cls,
       return;
     }
 
-    TALER_blinding_secret_create (&bks);
+
+    TALER_planchet_blinding_secret_create (&ps,
+                                           &alg_values);
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_blind (&denom_pub,
-                                      &bks,
+                                      &ps.blinding_key,
                                       NULL, /* FIXME-oec */
                                       &coin_pub,
+                                      &alg_values,
                                       &c_hash,
-                                      &pd.coin_ev,
-                                      &pd.coin_ev_size));
+                                      &pd.blinded_planchet));
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_sign_blinded (&bds,
                                              &pk,
-                                             pd.coin_ev,
-                                             pd.coin_ev_size));
-    GNUNET_free (pd.coin_ev);
+                                             &pd.blinded_planchet));
+    TALER_blinded_planchet_free (&pd.blinded_planchet);
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_sig_unblind (&denom_sig,
                                             &bds,
-                                            &bks,
+                                            &ps.blinding_key,
                                             &denom_pub));
     TALER_blinded_denom_sig_free (&bds);
     TALER_denom_pub_free (&denom_pub);
diff --git a/src/exchange-tools/taler-exchange-offline.c 
b/src/exchange-tools/taler-exchange-offline.c
index 6ad345eb..3b6280c7 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -2531,10 +2531,10 @@ do_download (char *const *args)
  *         #GNUNET_SYSERR if keys changed from what we remember or other error
  */
 static int
-tofu_check (const struct TALER_SecurityModulePublicKeyP secm[2])
+tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
 {
   char *fn;
-  struct TALER_SecurityModulePublicKeyP old[2];
+  struct TALER_SecurityModulePublicKeySetP oldset;
   ssize_t ret;
 
   if (GNUNET_OK !=
@@ -2552,11 +2552,11 @@ tofu_check (const struct TALER_SecurityModulePublicKeyP 
secm[2])
       GNUNET_DISK_file_test (fn))
   {
     ret = GNUNET_DISK_fn_read (fn,
-                               &old,
-                               sizeof (old));
+                               &oldset,
+                               sizeof (oldset));
     if (GNUNET_SYSERR != ret)
     {
-      if (ret != sizeof (old))
+      if (ret != sizeof (oldset))
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "File `%s' corrupt\n",
@@ -2565,9 +2565,9 @@ tofu_check (const struct TALER_SecurityModulePublicKeyP 
secm[2])
         return GNUNET_SYSERR;
       }
       /* TOFU check */
-      if (0 != memcmp (old,
-                       secm,
-                       sizeof (old)))
+      if (0 != memcmp (&oldset,
+                       secmset,
+                       sizeof (*secmset)))
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "Fatal: security module keys changed (file `%s')!\n",
@@ -2608,7 +2608,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeyP 
secm[2])
       GNUNET_free (key);
       if (0 !=
           GNUNET_memcmp (&k,
-                         &secm[1]))
+                         &secmset->eddsa))
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "ESIGN security module key does not match 
SECM_ESIGN_PUBKEY in configuration\n");
@@ -2639,13 +2639,44 @@ tofu_check (const struct TALER_SecurityModulePublicKeyP 
secm[2])
       GNUNET_free (key);
       if (0 !=
           GNUNET_memcmp (&k,
-                         &secm[0]))
+                         &secmset->rsa))
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "DENOM security module key does not match 
SECM_DENOM_PUBKEY in configuration\n");
         return GNUNET_SYSERR;
       }
     }
+    if (GNUNET_OK ==
+        GNUNET_CONFIGURATION_get_value_string (kcfg,
+                                               "exchange-offline",
+                                               "SECM_DENOM_CS_PUBKEY",
+                                               &key))
+    {
+      struct TALER_SecurityModulePublicKeyP k;
+
+      if (GNUNET_OK !=
+          GNUNET_STRINGS_string_to_data (key,
+                                         strlen (key),
+                                         &k,
+                                         sizeof (k)))
+      {
+        GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                   "exchange-offline",
+                                   "SECM_DENOM_CS_PUBKEY",
+                                   "key malformed");
+        GNUNET_free (key);
+        return GNUNET_SYSERR;
+      }
+      GNUNET_free (key);
+      if (0 !=
+          GNUNET_memcmp (&k,
+                         &secmset->cs))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "DENOM security module key does not match 
SECM_DENOM_CS_PUBKEY in configuration\n");
+        return GNUNET_SYSERR;
+      }
+    }
   }
   if (GNUNET_OK !=
       GNUNET_DISK_directory_create_for_file (fn))
@@ -2659,8 +2690,8 @@ tofu_check (const struct TALER_SecurityModulePublicKeyP 
secm[2])
   /* persist keys for future runs */
   if (GNUNET_OK !=
       GNUNET_DISK_fn_write (fn,
-                            secm,
-                            sizeof (old),
+                            secmset,
+                            sizeof (oldset),
                             GNUNET_DISK_PERM_USER_READ))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -2766,11 +2797,14 @@ 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 denomkeys keys to output
  * @return #GNUNET_OK on success
  */
 static int
-show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
+show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
+                const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
                 const json_t *denomkeys)
 {
   size_t index;
@@ -2863,10 +2897,24 @@ show_denomkeys (const struct 
TALER_SecurityModulePublicKeyP *secm_pub,
                                                section_name,
                                                stamp_start,
                                                duration,
-                                               secm_pub,
+                                               secm_pub_rsa,
                                                &secm_sig);
       }
       break;
+    case TALER_DENOMINATION_CS:
+      {
+        struct TALER_CsPubHashP h_cs;
+
+        TALER_cs_pub_hash (&denom_pub.details.cs_public_key,
+                           &h_cs);
+        ok = TALER_exchange_secmod_cs_verify (&h_cs,
+                                              section_name,
+                                              stamp_start,
+                                              duration,
+                                              secm_pub_cs,
+                                              &secm_sig);
+      }
+      break;
     default:
       GNUNET_break (0);
       ok = GNUNET_SYSERR;
@@ -3018,7 +3066,7 @@ do_show (char *const *args)
   json_t *denomkeys;
   json_t *signkeys;
   struct TALER_MasterPublicKeyP mpub;
-  struct TALER_SecurityModulePublicKeyP secm[2];
+  struct TALER_SecurityModulePublicKeySetP secmset;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("future_denoms",
                            &denomkeys),
@@ -3027,9 +3075,11 @@ do_show (char *const *args)
     GNUNET_JSON_spec_fixed_auto ("master_pub",
                                  &mpub),
     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
-                                 &secm[0]),
+                                 &secmset.rsa),
+    GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
+                                 &secmset.cs),
     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
-                                 &secm[1]),
+                                 &secmset.eddsa),
     GNUNET_JSON_spec_end ()
   };
 
@@ -3070,7 +3120,7 @@ do_show (char *const *args)
     return;
   }
   if (GNUNET_SYSERR ==
-      tofu_check (secm))
+      tofu_check (&secmset))
   {
     global_ret = EXIT_FAILURE;
     test_shutdown ();
@@ -3079,10 +3129,11 @@ do_show (char *const *args)
     return;
   }
   if ( (GNUNET_OK !=
-        show_signkeys (&secm[1],
+        show_signkeys (&secmset.eddsa,
                        signkeys)) ||
        (GNUNET_OK !=
-        show_denomkeys (&secm[0],
+        show_denomkeys (&secmset.rsa,
+                        &secmset.cs,
                         denomkeys)) )
   {
     global_ret = EXIT_FAILURE;
@@ -3200,12 +3251,15 @@ 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 denomkeys keys to output
  * @param[in,out] result array where to output the signatures
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
+sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
+                const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
                 const json_t *denomkeys,
                 json_t *result)
 {
@@ -3300,7 +3354,7 @@ sign_denomkeys (const struct 
TALER_SecurityModulePublicKeyP *secm_pub,
                                               section_name,
                                               stamp_start,
                                               duration,
-                                              secm_pub,
+                                              secm_pub_rsa,
                                               &secm_sig))
         {
           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -3313,6 +3367,30 @@ sign_denomkeys (const struct 
TALER_SecurityModulePublicKeyP *secm_pub,
         }
       }
       break;
+    case TALER_DENOMINATION_CS:
+      {
+        struct TALER_CsPubHashP h_cs;
+
+        TALER_cs_pub_hash (&denom_pub.details.cs_public_key,
+                           &h_cs);
+        if (GNUNET_OK !=
+            TALER_exchange_secmod_cs_verify (&h_cs,
+                                             section_name,
+                                             stamp_start,
+                                             duration,
+                                             secm_pub_cs,
+                                             &secm_sig))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Invalid security module signature for denomination key 
%s (aborting)\n",
+                      GNUNET_h2s (&h_denom_pub.hash));
+          global_ret = EXIT_FAILURE;
+          test_shutdown ();
+          GNUNET_JSON_parse_free (spec);
+          return GNUNET_SYSERR;
+        }
+      }
+      break;
     default:
       global_ret = EXIT_FAILURE;
       test_shutdown ();
@@ -3364,7 +3442,7 @@ do_sign (char *const *args)
   json_t *denomkeys;
   json_t *signkeys;
   struct TALER_MasterPublicKeyP mpub;
-  struct TALER_SecurityModulePublicKeyP secm[2];
+  struct TALER_SecurityModulePublicKeySetP secmset;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("future_denoms",
                            &denomkeys),
@@ -3373,9 +3451,11 @@ do_sign (char *const *args)
     GNUNET_JSON_spec_fixed_auto ("master_pub",
                                  &mpub),
     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
-                                 &secm[0]),
+                                 &secmset.rsa),
+    GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
+                                 &secmset.cs),
     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
-                                 &secm[1]),
+                                 &secmset.eddsa),
     GNUNET_JSON_spec_end ()
   };
 
@@ -3419,7 +3499,7 @@ do_sign (char *const *args)
     return;
   }
   if (GNUNET_SYSERR ==
-      tofu_check (secm))
+      tofu_check (&secmset))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Fatal: security module keys changed!\n");
@@ -3436,11 +3516,12 @@ do_sign (char *const *args)
     GNUNET_assert (NULL != signkey_sig_array);
     GNUNET_assert (NULL != denomkey_sig_array);
     if ( (GNUNET_OK !=
-          sign_signkeys (&secm[1],
+          sign_signkeys (&secmset.eddsa,
                          signkeys,
                          signkey_sig_array)) ||
          (GNUNET_OK !=
-          sign_denomkeys (&secm[0],
+          sign_denomkeys (&secmset.rsa,
+                          &secmset.cs,
                           denomkeys,
                           denomkey_sig_array)) )
     {
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 44487a3a..e4526f1c 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -79,6 +79,7 @@ taler_exchange_transfer_LDADD = \
 taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd.c taler-exchange-httpd.h \
   taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \
+  taler-exchange-httpd_csr.c taler-exchange-httpd_csr \
   taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
   taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
   taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index ae5847d1..c357813b 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -30,6 +30,7 @@
 #include <limits.h>
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_auditors.h"
+#include "taler-exchange-httpd_csr.h"
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_deposits_get.h"
 #include "taler-exchange-httpd_extensions.h"
@@ -910,6 +911,13 @@ handle_mhd_request (void *cls,
       .method = MHD_HTTP_METHOD_GET,
       .handler.get = &TEH_handler_wire
     },
+    /* request R, used in clause schnorr withdraw and refresh */
+    {
+      .url = "csr",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler.post = &TEH_handler_csr,
+      .nargs = 0
+    },
     /* Withdrawing coins / interaction with reserves */
     {
       .url = "reserves",
diff --git a/src/exchange/taler-exchange-httpd_csr.c 
b/src/exchange/taler-exchange-httpd_csr.c
new file mode 100644
index 00000000..fbad543c
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_csr.c
@@ -0,0 +1,153 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2021 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 Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty
+  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General
+  Public License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_csr.c
+ * @brief Handle /csr requests
+ * @author Lucien Heuzeveldt
+ * @author Gian Demarmles
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_csr.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keys.h"
+
+
+MHD_RESULT
+TEH_handler_csr (struct TEH_RequestContext *rc,
+                 const json_t *root,
+                 const char *const args[])
+{
+  struct TALER_CsNonce nonce;
+  struct TALER_DenominationHash denom_pub_hash;
+  struct TALER_DenominationCsPublicR r_pub;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed ("nonce",
+                            &nonce,
+                            sizeof (struct TALER_CsNonce)),
+    GNUNET_JSON_spec_fixed ("denom_pub_hash",
+                            &denom_pub_hash,
+                            sizeof (struct TALER_DenominationHash)),
+    GNUNET_JSON_spec_end ()
+  };
+  enum TALER_ErrorCode ec;
+  struct TEH_DenominationKey *dk;
+
+  (void) args;
+
+  // parse input
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    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;
+  }
+
+  // check denomination referenced by denom_pub_hash
+  {
+    struct TEH_KeyStateHandle *ksh;
+
+    ksh = TEH_keys_get_state ();
+    if (NULL == ksh)
+    {
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+                                         NULL);
+    }
+    dk = TEH_keys_denomination_by_hash2 (ksh,
+                                         &denom_pub_hash,
+                                         NULL,
+                                         NULL);
+    if (NULL == dk)
+    {
+      return TEH_RESPONSE_reply_unknown_denom_pub_hash (
+        rc->connection,
+        &denom_pub_hash);
+    }
+    if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
+    {
+      /* This denomination is past the expiration time for 
withdraws/refreshes*/
+      return TEH_RESPONSE_reply_expired_denom_pub_hash (
+        rc->connection,
+        &denom_pub_hash,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        "CSR");
+    }
+    if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
+    {
+      /* This denomination is not yet valid, no need to check
+         for idempotency! */
+      return TEH_RESPONSE_reply_expired_denom_pub_hash (
+        rc->connection,
+        &denom_pub_hash,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
+        "CSR");
+    }
+    if (dk->recoup_possible)
+    {
+      /* This denomination has been revoked */
+      return TEH_RESPONSE_reply_expired_denom_pub_hash (
+        rc->connection,
+        &denom_pub_hash,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
+        "CSR");
+    }
+    if (TALER_DENOMINATION_CS != dk->denom_pub.cipher)
+    {
+      // denomination is valid but not CS
+      return TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
+        rc->connection,
+        &denom_pub_hash);
+    }
+  }
+
+  // derive r_pub
+  ec = TEH_keys_denomination_cs_r_pub (&denom_pub_hash,
+                                       &nonce,
+                                       &r_pub);
+  if (TALER_EC_NONE != ec)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_ec (rc->connection,
+                                    ec,
+                                    NULL);
+  }
+
+  // send response
+  return TALER_MHD_REPLY_JSON_PACK (
+    rc->connection,
+    MHD_HTTP_OK,
+    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)));
+}
+
+
+/* end of taler-exchange-httpd_csr.c */
diff --git a/src/exchange/taler-exchange-httpd_link.h 
b/src/exchange/taler-exchange-httpd_csr.h
similarity index 59%
copy from src/exchange/taler-exchange-httpd_link.h
copy to src/exchange/taler-exchange-httpd_csr.h
index 01679e87..3bd98742 100644
--- a/src/exchange/taler-exchange-httpd_link.h
+++ b/src/exchange/taler-exchange-httpd_csr.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
+  Copyright (C) 2014-2021 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
@@ -14,30 +14,30 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_link.h
- * @brief Handle /coins/$COIN_PUB/link requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
+ * @file taler-exchange-httpd_csr.h
+ * @brief Handle /csr requests
+ * @author Lucien Heuzeveldt
+ * @author Gian Demarmles
  */
-#ifndef TALER_EXCHANGE_HTTPD_LINK_H
-#define TALER_EXCHANGE_HTTPD_LINK_H
+#ifndef TALER_EXCHANGE_HTTPD_CSR_H
+#define TALER_EXCHANGE_HTTPD_CSR_H
 
-#include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
 #include "taler-exchange-httpd.h"
 
 
 /**
- * Handle a "/coins/$COIN_PUB/link" request.
+ * Handle a "/csr" request.  Parses the "nonce"  and
+ * the "denom_pub_hash" (identifying a denomination) used to derive the r_pub.
  *
  * @param rc request context
- * @param args array of additional options (length: 2, first is the coin_pub, 
second must be "link")
+ * @param root uploaded JSON data
+ * @param args empty array
  * @return MHD result code
   */
 MHD_RESULT
-TEH_handler_link (struct TEH_RequestContext *rc,
-                  const char *const args[2]);
-
+TEH_handler_csr (struct TEH_RequestContext *rc,
+                 const json_t *root,
+                 const char *const args[]);
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index 84741b5c..50ddba60 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -356,6 +356,15 @@ TEH_handler_deposit (struct MHD_Connection *connection,
         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
         "DEPOSIT");
     }
+    if (dk->denom_pub.cipher != deposit.coin.denom_sig.cipher)
+    {
+      /* denomination cipher and denomination signature cipher not the same */
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
+                                         NULL);
+    }
 
     deposit.deposit_fee = dk->meta.fee_deposit;
     /* check coin signature */
diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index de9b81cd..2e1d7182 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -103,6 +103,11 @@ struct HelperDenomination
      */
     struct TALER_RsaPubHashP h_rsa;
 
+    /**
+     * Hash of the CS key.
+     */
+    struct TALER_CsPubHashP h_cs;
+
   } h_details;
 
   /**
@@ -188,7 +193,12 @@ struct HelperState
   /**
    * Handle for the denom/RSA helper.
    */
-  struct TALER_CRYPTO_RsaDenominationHelper *dh;
+  struct TALER_CRYPTO_RsaDenominationHelper *rsadh;
+
+  /**
+   * Handle for the denom/CS helper.
+   */
+  struct TALER_CRYPTO_CsDenominationHelper *csdh;
 
   /**
    * Map from H(denom_pub) to `struct HelperDenomination` entries.
@@ -200,6 +210,11 @@ struct HelperState
    */
   struct GNUNET_CONTAINER_MultiHashMap *rsa_keys;
 
+  /**
+   * Map from H(cs_pub) to `struct HelperDenomination` entries.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *cs_keys;
+
   /**
    * Map from `struct TALER_ExchangePublicKey` to `struct HelperSignkey`
    * entries.  Based on the fact that a `struct GNUNET_PeerIdentity` is also
@@ -424,7 +439,12 @@ static struct GNUNET_TIME_Relative signkey_legal_duration;
 /**
  * RSA security module public key, all zero if not known.
  */
-static struct TALER_SecurityModulePublicKeyP denom_sm_pub;
+static struct TALER_SecurityModulePublicKeyP denom_rsa_sm_pub;
+
+/**
+ * CS security module public key, all zero if not known.
+ */
+static struct TALER_SecurityModulePublicKeyP denom_cs_sm_pub;
 
 /**
  * EdDSA security module public key, all zero if not known.
@@ -541,6 +561,7 @@ check_dk (void *cls,
   if (TALER_DENOMINATION_RSA == dk->denom_pub.cipher)
     GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check (
                      dk->denom_pub.details.rsa_public_key));
+  // nothing to do for TALER_DENOMINATION_CS
   return GNUNET_OK;
 }
 
@@ -609,19 +630,43 @@ clear_response_cache (struct TEH_KeyStateHandle *ksh)
  * @param sm_pub RSA security module public key to check
  */
 static void
-check_denom_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
+check_denom_rsa_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
 {
   if (0 !=
       GNUNET_memcmp (sm_pub,
-                     &denom_sm_pub))
+                     &denom_rsa_sm_pub))
   {
-    if (! GNUNET_is_zero (&denom_sm_pub))
+    if (! GNUNET_is_zero (&denom_rsa_sm_pub))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Our RSA security module changed its key. This must not 
happen.\n");
       GNUNET_assert (0);
     }
-    denom_sm_pub = *sm_pub; /* TOFU ;-) */
+    denom_rsa_sm_pub = *sm_pub; /* TOFU ;-) */
+  }
+}
+
+
+/**
+ * Check that the given CS security module's public key is the one
+ * we have pinned.  If it does not match, we die hard.
+ *
+ * @param sm_pub RSA security module public key to check
+ */
+static void
+check_denom_cs_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
+{
+  if (0 !=
+      GNUNET_memcmp (sm_pub,
+                     &denom_cs_sm_pub))
+  {
+    if (! GNUNET_is_zero (&denom_cs_sm_pub))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Our CS security module changed its key. This must not 
happen.\n");
+      GNUNET_assert (0);
+    }
+    denom_cs_sm_pub = *sm_pub; /* TOFU ;-) */
   }
 }
 
@@ -712,6 +757,8 @@ destroy_key_helpers (struct HelperState *hs)
                                          hs);
   GNUNET_CONTAINER_multihashmap_destroy (hs->rsa_keys);
   hs->rsa_keys = NULL;
+  GNUNET_CONTAINER_multihashmap_destroy (hs->cs_keys);
+  hs->cs_keys = NULL;
   GNUNET_CONTAINER_multihashmap_destroy (hs->denom_keys);
   hs->denom_keys = NULL;
   GNUNET_CONTAINER_multipeermap_iterate (hs->esign_keys,
@@ -719,10 +766,15 @@ destroy_key_helpers (struct HelperState *hs)
                                          hs);
   GNUNET_CONTAINER_multipeermap_destroy (hs->esign_keys);
   hs->esign_keys = NULL;
-  if (NULL != hs->dh)
+  if (NULL != hs->rsadh)
+  {
+    TALER_CRYPTO_helper_rsa_disconnect (hs->rsadh);
+    hs->rsadh = NULL;
+  }
+  if (NULL != hs->csdh)
   {
-    TALER_CRYPTO_helper_rsa_disconnect (hs->dh);
-    hs->dh = NULL;
+    TALER_CRYPTO_helper_cs_disconnect (hs->csdh);
+    hs->csdh = NULL;
   }
   if (NULL != hs->esh)
   {
@@ -795,7 +847,7 @@ load_age_mask (const char*section_name)
  *                 zero if the key has been revoked or purged
  * @param validity_duration how long does the key remain available for signing;
  *                 zero if the key has been revoked or purged
- * @param h_denom_pub hash of the @a denom_pub that is available (or was 
purged)
+ * @param h_rsa hash of the @a denom_pub that is available (or was purged)
  * @param denom_pub the public key itself, NULL if the key was revoked or 
purged
  * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
  * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
@@ -832,7 +884,7 @@ helper_rsa_cb (
     return;
   }
   GNUNET_assert (NULL != sm_pub);
-  check_denom_sm_pub (sm_pub);
+  check_denom_rsa_sm_pub (sm_pub);
   hd = GNUNET_new (struct HelperDenomination);
   hd->start_time = start_time;
   hd->validity_duration = validity_duration;
@@ -864,6 +916,86 @@ helper_rsa_cb (
 }
 
 
+/**
+ * Function called with information about available CS keys for signing. 
Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure with the `struct HelperState *`
+ * @param section_name name of the denomination type in the configuration;
+ *                 NULL if the key has been revoked or purged
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param h_cs hash of the @a denom_pub that is available (or was purged)
+ * @param denom_pub the public key itself, NULL if the key was revoked or 
purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+static void
+helper_cs_cb (
+  void *cls,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct TALER_CsPubHashP *h_cs,
+  const struct TALER_DenominationPublicKey *denom_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig)
+{
+  struct HelperState *hs = cls;
+  struct HelperDenomination *hd;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "CS helper announces key %s for denomination type %s with 
validity %s\n",
+              GNUNET_h2s (&h_cs->hash),
+              section_name,
+              GNUNET_STRINGS_relative_time_to_string (validity_duration,
+                                                      GNUNET_NO));
+  key_generation++;
+  TEH_resume_keys_requests (false);
+  hd = GNUNET_CONTAINER_multihashmap_get (hs->cs_keys,
+                                          &h_cs->hash);
+  if (NULL != hd)
+  {
+    /* should be just an update (revocation!), so update existing entry */
+    hd->validity_duration = validity_duration;
+    return;
+  }
+  GNUNET_assert (NULL != sm_pub);
+  check_denom_cs_sm_pub (sm_pub);
+  hd = GNUNET_new (struct HelperDenomination);
+  hd->start_time = start_time;
+  hd->validity_duration = validity_duration;
+  hd->h_details.h_cs = *h_cs;
+  hd->sm_sig = *sm_sig;
+  GNUNET_assert (TALER_DENOMINATION_CS == denom_pub->cipher);
+  TALER_denom_pub_deep_copy (&hd->denom_pub,
+                             denom_pub);
+  /* load the age mask for the denomination, if applicable */
+  hd->denom_pub.age_mask = load_age_mask (section_name);
+  TALER_denom_pub_hash (&hd->denom_pub,
+                        &hd->h_denom_pub);
+  hd->section_name = GNUNET_strdup (section_name);
+  GNUNET_assert (
+    GNUNET_OK ==
+    GNUNET_CONTAINER_multihashmap_put (
+      hs->denom_keys,
+      &hd->h_denom_pub.hash,
+      hd,
+      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+  GNUNET_assert (
+    GNUNET_OK ==
+    GNUNET_CONTAINER_multihashmap_put (
+      hs->cs_keys,
+      &hd->h_details.h_cs.hash,
+      hd,
+      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
 /**
  * Function called with information about available keys for signing.  Usually
  * only called once per key upon connect. Also called again in case a key is
@@ -940,13 +1072,24 @@ setup_key_helpers (struct HelperState *hs)
   hs->rsa_keys
     = GNUNET_CONTAINER_multihashmap_create (1024,
                                             GNUNET_YES);
+  hs->cs_keys
+    = GNUNET_CONTAINER_multihashmap_create (1024,
+                                            GNUNET_YES);
   hs->esign_keys
     = GNUNET_CONTAINER_multipeermap_create (32,
                                             GNUNET_NO /* MUST BE NO! */);
-  hs->dh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg,
-                                            &helper_rsa_cb,
-                                            hs);
-  if (NULL == hs->dh)
+  hs->rsadh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg,
+                                               &helper_rsa_cb,
+                                               hs);
+  if (NULL == hs->rsadh)
+  {
+    destroy_key_helpers (hs);
+    return GNUNET_SYSERR;
+  }
+  hs->csdh = TALER_CRYPTO_helper_cs_connect (TEH_cfg,
+                                             &helper_cs_cb,
+                                             hs);
+  if (NULL == hs->csdh)
   {
     destroy_key_helpers (hs);
     return GNUNET_SYSERR;
@@ -971,7 +1114,8 @@ setup_key_helpers (struct HelperState *hs)
 static void
 sync_key_helpers (struct HelperState *hs)
 {
-  TALER_CRYPTO_helper_rsa_poll (hs->dh);
+  TALER_CRYPTO_helper_rsa_poll (hs->rsadh);
+  TALER_CRYPTO_helper_cs_poll (hs->csdh);
   TALER_CRYPTO_helper_esign_poll (hs->esh);
 }
 
@@ -2265,8 +2409,7 @@ TEH_keys_denomination_by_hash2 (
 
 struct TALER_BlindedDenominationSignature
 TEH_keys_denomination_sign (const struct TALER_DenominationHash *h_denom_pub,
-                            const void *msg,
-                            size_t msg_size,
+                            const struct TEH_SignDetails *msg,
                             enum TALER_ErrorCode *ec)
 {
   struct TEH_KeyStateHandle *ksh;
@@ -2289,14 +2432,24 @@ TEH_keys_denomination_sign (const struct 
TALER_DenominationHash *h_denom_pub,
     *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;
+  }
   switch (hd->denom_pub.cipher)
   {
   case TALER_DENOMINATION_RSA:
-    return TALER_CRYPTO_helper_rsa_sign (ksh->helpers->dh,
+    return TALER_CRYPTO_helper_rsa_sign (ksh->helpers->rsadh,
                                          &hd->h_details.h_rsa,
-                                         msg,
-                                         msg_size,
+                                         msg->details.rsa_message.msg,
+                                         msg->details.rsa_message.msg_size,
                                          ec);
+  case TALER_DENOMINATION_CS:
+    return TALER_CRYPTO_helper_cs_sign (ksh->helpers->csdh,
+                                        &hd->h_details.h_cs,
+                                        &msg->details.cs_message,
+                                        ec);
   default:
     *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     return none;
@@ -2304,6 +2457,40 @@ TEH_keys_denomination_sign (const struct 
TALER_DenominationHash *h_denom_pub,
 }
 
 
+enum TALER_ErrorCode
+TEH_keys_denomination_cs_r_pub (const struct
+                                TALER_DenominationHash *h_denom_pub,
+                                const struct TALER_CsNonce *nonce,
+                                struct TALER_DenominationCsPublicR *r_pub)
+{
+  struct TEH_KeyStateHandle *ksh;
+  struct HelperDenomination *hd;
+  enum TALER_ErrorCode r_derive_ec;
+
+  ksh = TEH_keys_get_state ();
+  if (NULL == ksh)
+  {
+    return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
+  }
+  hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+                                          &h_denom_pub->hash);
+  if (NULL == hd)
+  {
+    return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
+  }
+  if (TALER_DENOMINATION_CS != hd->denom_pub.cipher)
+  {
+    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;
+}
+
+
 void
 TEH_keys_denomination_revoke (const struct TALER_DenominationHash *h_denom_pub)
 {
@@ -2326,10 +2513,15 @@ TEH_keys_denomination_revoke (const struct 
TALER_DenominationHash *h_denom_pub)
   switch (hd->denom_pub.cipher)
   {
   case TALER_DENOMINATION_RSA:
-    TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->dh,
+    TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->rsadh,
                                     &hd->h_details.h_rsa);
     TEH_keys_update_states ();
     return;
+  case TALER_DENOMINATION_CS:
+    TALER_CRYPTO_helper_cs_revoke (ksh->helpers->csdh,
+                                   &hd->h_details.h_cs);
+    TEH_keys_update_states ();
+    return;
   default:
     GNUNET_break (0);
     return;
@@ -2923,7 +3115,14 @@ TEH_keys_management_get_keys_handler (const struct 
TEH_RequestHandler *rh,
       .signkeys = json_array ()
     };
 
-    if (GNUNET_is_zero (&denom_sm_pub))
+    if (GNUNET_is_zero (&denom_rsa_sm_pub))
+    {
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_GATEWAY,
+                                         
TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE,
+                                         NULL);
+    }
+    if (GNUNET_is_zero (&denom_cs_sm_pub))
     {
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_GATEWAY,
@@ -2954,7 +3153,9 @@ TEH_keys_management_get_keys_handler (const struct 
TEH_RequestHandler *rh,
       GNUNET_JSON_pack_data_auto ("master_pub",
                                   &TEH_master_public_key),
       GNUNET_JSON_pack_data_auto ("denom_secmod_public_key",
-                                  &denom_sm_pub),
+                                  &denom_rsa_sm_pub),
+      GNUNET_JSON_pack_data_auto ("denom_secmod_cs_public_key",
+                                  &denom_cs_sm_pub),
       GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key",
                                   &esign_sm_pub));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
diff --git a/src/exchange/taler-exchange-httpd_keys.h 
b/src/exchange/taler-exchange-httpd_keys.h
index ce9068ec..57011ed2 100644
--- a/src/exchange/taler-exchange-httpd_keys.h
+++ b/src/exchange/taler-exchange-httpd_keys.h
@@ -82,6 +82,42 @@ 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
@@ -179,11 +215,27 @@ TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle 
*ksh,
  */
 struct TALER_BlindedDenominationSignature
 TEH_keys_denomination_sign (const struct TALER_DenominationHash *h_denom_pub,
-                            const void *msg,
-                            size_t msg_size,
+                            const struct TEH_SignDetails *msg,
                             enum TALER_ErrorCode *ec);
 
 
+/**
+ * Request to derive CS 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
+ */
+enum TALER_ErrorCode
+TEH_keys_denomination_cs_r_pub (const struct
+                                TALER_DenominationHash *h_denom_pub,
+                                const struct TALER_CsNonce *nonce,
+                                struct TALER_DenominationCsPublicR *r_pub);
+
+
 /**
  * Revoke the public key associated with @param h_denom_pub .
  * This function should be called AFTER the database was
diff --git a/src/exchange/taler-exchange-httpd_recoup-refresh.c 
b/src/exchange/taler-exchange-httpd_recoup-refresh.c
index 78a454c8..acaea64f 100644
--- a/src/exchange/taler-exchange-httpd_recoup-refresh.c
+++ b/src/exchange/taler-exchange-httpd_recoup-refresh.c
@@ -241,18 +241,17 @@ verify_and_execute_recoup_refresh (
   }
 
   {
-    void *coin_ev;
-    size_t coin_ev_size;
     struct TALER_CoinPubHash c_hash;
+    struct TALER_BlindedPlanchet blinded_planchet;
 
     if (GNUNET_OK !=
         TALER_denom_blind (&dk->denom_pub,
                            coin_bks,
                            NULL, /* FIXME-Oec: TALER_AgeHash * */
                            &coin->coin_pub,
+                           NULL, /* FIXME: Implement CS */
                            &c_hash,
-                           &coin_ev,
-                           &coin_ev_size))
+                           &blinded_planchet))
     {
       GNUNET_break (0);
       return TALER_MHD_reply_with_error (
@@ -261,10 +260,10 @@ verify_and_execute_recoup_refresh (
         TALER_EC_EXCHANGE_RECOUP_REFRESH_BLINDING_FAILED,
         NULL);
     }
-    TALER_coin_ev_hash (coin_ev,
-                        coin_ev_size,
+    TALER_coin_ev_hash (&blinded_planchet,
+                        &coin->denom_pub_hash,
                         &h_blind);
-    GNUNET_free (coin_ev);
+    TALER_blinded_planchet_free (&blinded_planchet);
   }
 
   pc.coin_sig = coin_sig;
diff --git a/src/exchange/taler-exchange-httpd_recoup.c 
b/src/exchange/taler-exchange-httpd_recoup.c
index 0deaa8bb..416eaf69 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -243,18 +243,17 @@ verify_and_execute_recoup (
   }
 
   {
-    void *coin_ev;
-    size_t coin_ev_size;
     struct TALER_CoinPubHash c_hash;
+    struct TALER_BlindedPlanchet blinded_planchet;
 
     if (GNUNET_OK !=
         TALER_denom_blind (&dk->denom_pub,
                            coin_bks,
                            NULL, /* FIXME-Oec: TALER_AgeHash * */
                            &coin->coin_pub,
+                           NULL, /* FIXME: handle CS */
                            &c_hash,
-                           &coin_ev,
-                           &coin_ev_size))
+                           &blinded_planchet))
     {
       GNUNET_break (0);
       return TALER_MHD_reply_with_error (
@@ -263,10 +262,17 @@ verify_and_execute_recoup (
         TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
         NULL);
     }
-    TALER_coin_ev_hash (coin_ev,
-                        coin_ev_size,
-                        &pc.h_blind);
-    GNUNET_free (coin_ev);
+    if (GNUNET_OK != TALER_coin_ev_hash (&blinded_planchet,
+                                         &coin->denom_pub_hash,
+                                         &pc.h_blind))
+    {
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                                         NULL);
+    }
+    TALER_blinded_planchet_free (&blinded_planchet);
   }
 
   pc.coin_sig = coin_sig;
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 30a7294c..95ec55b2 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -181,6 +181,7 @@ check_commitment (struct RevealContext *rctx,
         {
           struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
           struct TALER_PlanchetSecretsP ps;
+          struct TALER_ExchangeWithdrawValues alg_values;
           struct TALER_PlanchetDetail pd;
           struct TALER_CoinPubHash c_hash;
 
@@ -188,13 +189,18 @@ check_commitment (struct RevealContext *rctx,
           TALER_planchet_setup_refresh (&ts,
                                         j,
                                         &ps);
+          // TODO: implement cipher handling
+          alg_values.cipher = TALER_DENOMINATION_RSA;
           GNUNET_assert (GNUNET_OK ==
                          TALER_planchet_prepare (rcd->dk,
+                                                 &alg_values,
                                                  &ps,
                                                  &c_hash,
                                                  &pd));
-          rcd->coin_ev = pd.coin_ev;
-          rcd->coin_ev_size = pd.coin_ev_size;
+          rcd->coin_ev =
+            pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg;
+          rcd->coin_ev_size =
+            pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg_size;
         }
       }
     }
@@ -504,12 +510,16 @@ resolve_refreshes_reveal_denominations (struct 
MHD_Connection *connection,
   for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
   {
     enum TALER_ErrorCode ec = TALER_EC_NONE;
+    struct TEH_SignDetails sign_details;
 
+    // FIXME: implement cipher handling
+    sign_details.cipher = TALER_DENOMINATION_RSA;
+    sign_details.details.rsa_message.msg = rcds[i].coin_ev;
+    sign_details.details.rsa_message.msg_size = rcds[i].coin_ev_size;
     rrcs[i].coin_sig
       = TEH_keys_denomination_sign (
           &rrcs[i].h_denom_pub,
-          rcds[i].coin_ev,
-          rcds[i].coin_ev_size,
+          &sign_details,
           &ec);
     if (TALER_EC_NONE != ec)
     {
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 66a3b0af..8aa54712 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -491,6 +491,53 @@ TEH_RESPONSE_reply_expired_denom_pub_hash (
 }
 
 
+MHD_RESULT
+TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
+  struct MHD_Connection *connection,
+  const struct TALER_DenominationHash *dph)
+{
+  struct TALER_ExchangePublicKeyP epub;
+  struct TALER_ExchangeSignatureP esig;
+  struct GNUNET_TIME_Timestamp now;
+  enum TALER_ErrorCode ec;
+
+  now = GNUNET_TIME_timestamp_get ();
+  {
+    struct TALER_DenominationUnknownAffirmationPS dua = {
+      .purpose.size = htonl (sizeof (dua)),
+      .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN),
+      .timestamp = GNUNET_TIME_timestamp_hton (now),
+      .h_denom_pub = *dph,
+    };
+
+    ec = TEH_keys_exchange_sign (&dua,
+                                 &epub,
+                                 &esig);
+  }
+  if (TALER_EC_NONE != ec)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       ec,
+                                       NULL);
+  }
+  return TALER_MHD_REPLY_JSON_PACK (
+    connection,
+    MHD_HTTP_NOT_FOUND,
+    TALER_JSON_pack_ec (
+      TALER_EC_EXCHANGE_GENERIC_INVALID_DENOMINATION_CIPHER_FOR_OPERATION),
+    GNUNET_JSON_pack_timestamp ("timestamp",
+                                now),
+    GNUNET_JSON_pack_data_auto ("exchange_pub",
+                                &epub),
+    GNUNET_JSON_pack_data_auto ("exchange_sig",
+                                &esig),
+    GNUNET_JSON_pack_data_auto ("h_denom_pub",
+                                dph));
+}
+
+
 /**
  * Send proof that a request is invalid to client because of
  * insufficient funds.  This function will create a message with all
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index db2286ff..48309a41 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -79,6 +79,19 @@ TEH_RESPONSE_reply_expired_denom_pub_hash (
   const char *oper);
 
 
+/**
+ * Send assertion that the given denomination cannot be used for this 
operation.
+ *
+ * @param connection connection to the client
+ * @param dph denomination public key hash
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
+  struct MHD_Connection *connection,
+  const struct TALER_DenominationHash *dph);
+
+
 /**
  * Send proof that a request is invalid to client because of
  * insufficient funds.  This function will create a message with all
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
index 53ba270b..5cae883e 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -98,12 +98,7 @@ struct WithdrawContext
   /**
    * Blinded planchet.
    */
-  void *blinded_msg;
-
-  /**
-   * Number of bytes in @e blinded_msg.
-   */
-  size_t blinded_msg_len;
+  struct TALER_BlindedPlanchet blinded_planchet;
 
   /**
    * Set to the resulting signed coin data to be returned to the client.
@@ -324,13 +319,12 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
 {
   struct WithdrawContext wc;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_varsize ("coin_ev",
-                              &wc.blinded_msg,
-                              &wc.blinded_msg_len),
     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
                                  &wc.collectable.reserve_sig),
     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
                                  &wc.collectable.denom_pub_hash),
+    TALER_JSON_spec_blinded_planchet ("coin_ev",
+                                      &wc.blinded_planchet),
     GNUNET_JSON_spec_end ()
   };
   enum TALER_ErrorCode ec;
@@ -444,6 +438,15 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
       GNUNET_JSON_parse_free (spec);
       return mret;
     }
+    if (dk->denom_pub.cipher != wc.blinded_planchet.cipher)
+    {
+      /* denomination cipher and blinded planchet cipher not the same */
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
+                                         NULL);
+    }
   }
 
   if (0 >
@@ -468,9 +471,17 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
     = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
   wc.wsrd.h_denomination_pub
     = wc.collectable.denom_pub_hash;
-  TALER_coin_ev_hash (wc.blinded_msg,
-                      wc.blinded_msg_len,
-                      &wc.wsrd.h_coin_envelope);
+  if (GNUNET_OK != TALER_coin_ev_hash (&wc.blinded_planchet,
+                                       &wc.collectable.denom_pub_hash,
+                                       &wc.wsrd.h_coin_envelope))
+  {
+    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);
+  }
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_verify (
         TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
@@ -487,13 +498,38 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
                                        NULL);
   }
 
+  // TODO: if CS: check nonce for reuse
+
   /* Sign before transaction! */
   ec = TALER_EC_NONE;
-  wc.collectable.sig
-    = TEH_keys_denomination_sign (&wc.collectable.denom_pub_hash,
-                                  wc.blinded_msg,
-                                  wc.blinded_msg_len,
-                                  &ec);
+  {
+    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);
+  }
   if (TALER_EC_NONE != ec)
   {
     GNUNET_break (0);
diff --git a/src/exchange/test_taler_exchange_httpd.conf 
b/src/exchange/test_taler_exchange_httpd.conf
index 2adee505..25938679 100644
--- a/src/exchange/test_taler_exchange_httpd.conf
+++ b/src/exchange/test_taler_exchange_httpd.conf
@@ -79,6 +79,7 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_eur_ct_10]
@@ -90,6 +91,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_eur_1]
@@ -101,4 +103,38 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
diff --git a/src/exchange/test_taler_exchange_unix.conf 
b/src/exchange/test_taler_exchange_unix.conf
index b9387f60..24e1a0fa 100644
--- a/src/exchange/test_taler_exchange_unix.conf
+++ b/src/exchange/test_taler_exchange_unix.conf
@@ -79,6 +79,7 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_eur_ct_10]
@@ -90,6 +91,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_eur_1]
@@ -101,4 +103,38 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
\ No newline at end of file
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index cca7c3f4..7895aaca 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -531,8 +531,228 @@ handle_link_data_cb (void *cls,
         break;
       }
     }
-    GNUNET_assert (found);
+    // FIXME:
+    GNUNET_assert (GNUNET_NO != found);
+  }
+}
+
+
+/**
+ * Function to test melting of coins as part of a refresh session
+ *
+ * @return #GNUNET_OK if everything went well; #GNUNET_SYSERR if not
+ */
+static enum GNUNET_GenericReturnValue
+test_melting (void)
+{
+  struct TALER_EXCHANGEDB_Refresh refresh_session;
+  struct TALER_EXCHANGEDB_Melt ret_refresh_session;
+  struct DenomKeyPair *dkp;
+  struct TALER_DenominationPublicKey *new_denom_pubs;
+  enum GNUNET_GenericReturnValue ret;
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_TIME_Timestamp now;
+
+  ret = GNUNET_SYSERR;
+  RND_BLK (&refresh_session);
+  dkp = NULL;
+  new_dkp = NULL;
+  new_denom_pubs = NULL;
+  /* create and test a refresh session */
+  refresh_session.noreveal_index = MELT_NOREVEAL_INDEX;
+  /* create a denomination (value: 1; fraction: 100) */
+  now = GNUNET_TIME_timestamp_get ();
+  dkp = create_denom_key_pair (512,
+                               now,
+                               &value,
+                               &fee_withdraw,
+                               &fee_deposit,
+                               &fee_refresh,
+                               &fee_refund);
+  GNUNET_assert (NULL != dkp);
+  /* initialize refresh session melt data */
+  {
+    struct TALER_CoinPubHash c_hash;
+    struct TALER_PlanchetDetail pd;
+    struct TALER_BlindedDenominationSignature bds;
+    struct TALER_PlanchetSecretsP ps;
+    struct TALER_ExchangeWithdrawValues alg_values;
+
+    RND_BLK (&refresh_session.coin.coin_pub);
+    alg_values.cipher = TALER_DENOMINATION_RSA;
+    TALER_planchet_blinding_secret_create (&ps,
+                                           &alg_values);
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_denom_blind (&dkp->pub,
+                                      &ps.blinding_key,
+                                      NULL, /* FIXME-Oec */
+                                      &refresh_session.coin.coin_pub,
+                                      &alg_values,
+                                      &c_hash,
+                                      &pd.blinded_planchet));
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_denom_sign_blinded (&bds,
+                                             &dkp->priv,
+                                             &pd.blinded_planchet));
+    TALER_blinded_planchet_free (&pd.blinded_planchet);
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_denom_sig_unblind (&refresh_session.coin.denom_sig,
+                                            &bds,
+                                            &ps.blinding_key,
+                                            &dkp->pub));
+    TALER_blinded_denom_sig_free (&bds);
+    TALER_denom_pub_hash (&dkp->pub,
+                          &refresh_session.coin.denom_pub_hash);
+    refresh_session.amount_with_fee = amount_with_fee;
+  }
+
+  /* test insert_melt & get_melt */
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+          plugin->get_melt (plugin->cls,
+                            &refresh_session.rc,
+                            &ret_refresh_session));
+  FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
+          plugin->ensure_coin_known (plugin->cls,
+                                     &refresh_session.coin));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          plugin->insert_melt (plugin->cls,
+                               &refresh_session));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          plugin->get_melt (plugin->cls,
+                            &refresh_session.rc,
+                            &ret_refresh_session));
+  FAILIF (refresh_session.noreveal_index !=
+          ret_refresh_session.session.noreveal_index);
+  FAILIF (0 !=
+          TALER_amount_cmp (&refresh_session.amount_with_fee,
+                            &ret_refresh_session.session.amount_with_fee));
+  FAILIF (0 !=
+          TALER_amount_cmp (&fee_refresh,
+                            &ret_refresh_session.melt_fee));
+  FAILIF (0 !=
+          GNUNET_memcmp (&refresh_session.rc,
+                         &ret_refresh_session.session.rc));
+  FAILIF (0 != GNUNET_memcmp (&refresh_session.coin_sig,
+                              &ret_refresh_session.session.coin_sig));
+  FAILIF (0 != memcmp (&refresh_session.coin.coin_pub,
+                       &ret_refresh_session.session.coin.coin_pub,
+                       sizeof (refresh_session.coin.coin_pub)));
+  FAILIF (0 !=
+          GNUNET_memcmp (&refresh_session.coin.denom_pub_hash,
+                         &ret_refresh_session.session.coin.denom_pub_hash));
+
+  /* test 'select_refreshes_above_serial_id' */
+  auditor_row_cnt = 0;
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          plugin->select_refreshes_above_serial_id (plugin->cls,
+                                                    0,
+                                                    &audit_refresh_session_cb,
+                                                    NULL));
+  FAILIF (1 != auditor_row_cnt);
+
+  new_dkp = GNUNET_new_array (MELT_NEW_COINS,
+                              struct DenomKeyPair *);
+  new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
+                                     struct TALER_DenominationPublicKey);
+  revealed_coins
+    = GNUNET_new_array (MELT_NEW_COINS,
+                        struct TALER_EXCHANGEDB_RefreshRevealedCoin);
+  for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+  {
+    struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
+    struct GNUNET_TIME_Timestamp now;
+    struct TALER_BlindedPlanchet blinded_planchet;
+    blinded_planchet.cipher = TALER_DENOMINATION_RSA;
+
+    now = GNUNET_TIME_timestamp_get ();
+    new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE,
+                                          now,
+                                          &value,
+                                          &fee_withdraw,
+                                          &fee_deposit,
+                                          &fee_refresh,
+                                          &fee_refund);
+    GNUNET_assert (NULL != new_dkp[cnt]);
+    new_denom_pubs[cnt] = new_dkp[cnt]->pub;
+    ccoin = &revealed_coins[cnt];
+    ccoin->coin_ev_size = (size_t) GNUNET_CRYPTO_random_u64 (
+      GNUNET_CRYPTO_QUALITY_WEAK,
+      (RSA_KEY_SIZE / 8) - 1);
+    ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size);
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                ccoin->coin_ev,
+                                ccoin->coin_ev_size);
+    ccoin->denom_pub = new_dkp[cnt]->pub;
+
+    blinded_planchet.details.rsa_blinded_planchet.blinded_msg = ccoin->coin_ev;
+    blinded_planchet.details.rsa_blinded_planchet.blinded_msg_size =
+      ccoin->coin_ev_size;
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_denom_sign_blinded (&ccoin->coin_sig,
+                                             &new_dkp[cnt]->priv,
+                                             &blinded_planchet));
   }
+  RND_BLK (&tprivs);
+  RND_BLK (&tpub);
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+          plugin->get_refresh_reveal (plugin->cls,
+                                      &refresh_session.rc,
+                                      &never_called_cb,
+                                      NULL));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          plugin->insert_refresh_reveal (plugin->cls,
+                                         &refresh_session.rc,
+                                         MELT_NEW_COINS,
+                                         revealed_coins,
+                                         TALER_CNC_KAPPA - 1,
+                                         tprivs,
+                                         &tpub));
+  FAILIF (0 >=
+          plugin->get_refresh_reveal (plugin->cls,
+                                      &refresh_session.rc,
+                                      &check_refresh_reveal_cb,
+                                      NULL));
+  qs = plugin->get_link_data (plugin->cls,
+                              &refresh_session.coin.coin_pub,
+                              &handle_link_data_cb,
+                              NULL);
+  FAILIF (0 >= qs);
+  {
+    /* Just to test fetching a coin with melt history */
+    struct TALER_EXCHANGEDB_TransactionList *tl;
+    enum GNUNET_DB_QueryStatus qs;
+
+    qs = plugin->get_coin_transactions (plugin->cls,
+                                        &refresh_session.coin.coin_pub,
+                                        GNUNET_YES,
+                                        &tl);
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
+    plugin->free_coin_transaction_list (plugin->cls,
+                                        tl);
+  }
+
+
+  ret = GNUNET_OK;
+drop:
+  if (NULL != revealed_coins)
+  {
+    for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++)
+    {
+      TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig);
+      GNUNET_free (revealed_coins[cnt].coin_ev);
+    }
+    GNUNET_free (revealed_coins);
+    revealed_coins = NULL;
+  }
+  destroy_denom_key_pair (dkp);
+  TALER_denom_sig_free (&refresh_session.coin.denom_sig);
+  GNUNET_free (new_denom_pubs);
+  for (unsigned int cnt = 0;
+       (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]);
+       cnt++)
+    destroy_denom_key_pair (new_dkp[cnt]);
+  GNUNET_free (new_dkp);
+  return ret;
 }
 
 
@@ -1346,7 +1566,6 @@ run (void *cls)
   enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_TIME_Timestamp now;
   struct TALER_WireSalt salt;
-  union TALER_DenominationBlindingKeyP bks;
   struct TALER_CoinPubHash c_hash;
   uint64_t known_coin_id;
   uint64_t rrc_serial;
@@ -1354,6 +1573,8 @@ run (void *cls)
   struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
   uint64_t reserve_out_serial_id;
   uint64_t melt_serial_id;
+  struct TALER_PlanchetSecretsP ps;
+
 
   memset (&deposit,
           0,
@@ -1469,7 +1690,7 @@ run (void *cls)
     struct TALER_CoinSpendPublicKeyP coin_pub;
     struct TALER_AgeHash age_hash;
     struct TALER_AgeHash *p_ah[2] = {NULL, &age_hash};
-
+    // FIXME:
     /* Call TALER_denom_blind()/TALER_denom_sign_blinded() twice, once without
      * age_hash, once with age_hash */
     RND_BLK (&age_hash);
@@ -1495,6 +1716,29 @@ run (void *cls)
                                                pd.coin_ev_size));
       GNUNET_free (pd.coin_ev);
     }
+    struct TALER_ExchangeWithdrawValues alg_values;
+
+    RND_BLK (&coin_pub);
+    alg_values.cipher = TALER_DENOMINATION_RSA;
+    TALER_planchet_blinding_secret_create (&ps,
+                                           &alg_values);
+
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_denom_blind (&dkp->pub,
+                                      &ps.blinding_key,
+                                      NULL, /* FIXME-Oec */
+                                      &coin_pub,
+                                      &alg_values,
+                                      &c_hash,
+                                      &pd.blinded_planchet));
+    GNUNET_assert (GNUNET_OK == TALER_coin_ev_hash (&pd.blinded_planchet,
+                                                    &cbc.denom_pub_hash,
+                                                    &cbc.h_coin_envelope));
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_denom_sign_blinded (&cbc.sig,
+                                             &dkp->priv,
+                                             &pd.blinded_planchet));
+    TALER_blinded_planchet_free (&pd.blinded_planchet);
   }
 
   cbc.reserve_pub = reserve_pub;
@@ -1554,7 +1798,7 @@ run (void *cls)
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_sig_unblind (&ds,
                                             &cbc2.sig,
-                                            &bks,
+                                            &ps.blinding_key,
                                             &dkp->pub));
     FAILIF (GNUNET_OK !=
             TALER_denom_pub_verify (&dkp->pub,
@@ -1571,7 +1815,7 @@ run (void *cls)
   GNUNET_assert (GNUNET_OK ==
                  TALER_denom_sig_unblind (&deposit.coin.denom_sig,
                                           &cbc.sig,
-                                          &bks,
+                                          &ps.blinding_key,
                                           &dkp->pub));
   deadline = GNUNET_TIME_timestamp_get ();
   {
@@ -2150,7 +2394,7 @@ run (void *cls)
   GNUNET_assert (GNUNET_OK ==
                  TALER_denom_sig_unblind (&deposit.coin.denom_sig,
                                           &cbc.sig,
-                                          &bks,
+                                          &ps.blinding_key,
                                           &dkp->pub));
   RND_BLK (&deposit.csig);
   RND_BLK (&deposit.merchant_pub);
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 6a805b64..d9565dd7 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -44,6 +44,26 @@ struct TALER_SecurityModulePublicKeyP
   struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
 };
 
+/**
+ * @brief Set of the public keys of the security modules
+ */
+struct TALER_SecurityModulePublicKeySetP
+{
+  /**
+   * Public key of the RSA security module
+   */
+  struct TALER_SecurityModulePublicKeyP rsa;
+
+  /**
+   * Public key of the CS security module
+   */
+  struct TALER_SecurityModulePublicKeyP cs;
+
+  /**
+   * Public key of the eddsa security module
+   */
+  struct TALER_SecurityModulePublicKeyP eddsa;
+};
 
 /**
  * @brief Type of private keys for Taler security modules (software or 
hardware).
@@ -353,9 +373,15 @@ struct TALER_CoinSpendSignatureP
 
 /**
  * @brief Type of blinding keys for Taler.
+ * must be 32 bytes (DB)
  */
 union TALER_DenominationBlindingKeyP
 {
+  /**
+   * Clause Schnorr Signatures have 2 blinding secrets, each containing two 
unpredictable values. (must be 32 bytes)
+   */
+  struct GNUNET_CRYPTO_CsNonce nonce;
+
   /**
    * Taler uses RSA for blind signatures.
    */
@@ -402,6 +428,20 @@ struct TALER_WireSalt
 };
 
 
+/**
+ * Hash used to represent an CS public key.  Does not include age
+ * restrictions and is ONLY for CS.  Used ONLY for interactions with the CS
+ * security module.
+ */
+struct TALER_CsPubHashP
+{
+  /**
+   * Actual hash value.
+   */
+  struct GNUNET_HashCode hash;
+};
+
+
 /**
  * Hash used to represent an RSA public key.  Does not include age
  * restrictions and is ONLY for RSA.  Used ONLY for interactions with the RSA
@@ -426,6 +466,15 @@ void
 TALER_rsa_pub_hash (const struct GNUNET_CRYPTO_RsaPublicKey *rsa,
                     struct TALER_RsaPubHashP *h_rsa);
 
+/**
+ * Hash @a cs.
+ *
+ * @param cs key to hash
+ * @param[out] h_cs where to write the result
+ */
+void
+TALER_cs_pub_hash (const struct GNUNET_CRYPTO_CsPublicKey *cs,
+                   struct TALER_CsPubHashP *h_cs);
 
 /**
  * Hash used to represent a denomination public key
@@ -575,9 +624,9 @@ enum TALER_DenominationCipher
   TALER_DENOMINATION_RSA = 1,
 
   /**
-   * Clause-Schnorr blind signature.
+   * Clause Blind Schnorr signature.
    */
-  // TALER_DENOMINATION_CS = 2
+  TALER_DENOMINATION_CS = 2
 };
 
 
@@ -597,6 +646,10 @@ struct TALER_DenominationSignature
    */
   union
   {
+    /**
+     * If we use #TALER_DENOMINATION_CS in @a cipher.
+     */
+    struct GNUNET_CRYPTO_CsSignature cs_signature;
 
     /**
      * If we use #TALER_DENOMINATION_RSA in @a cipher.
@@ -607,6 +660,23 @@ struct TALER_DenominationSignature
 
 };
 
+/**
+ * The Sign Answer for Clause Blind Schnorr signature.
+ * The sign operation returns a parameter @param b and the signature
+ * scalar @param s_scalar.
+ */
+struct TALER_BlindedDenominationCsSignAnswer
+{
+  /**
+   * To make ROS problem harder, the signer chooses an unpredictable b and 
only calculates signature of c_b
+   */
+  unsigned int b;
+
+  /**
+   * The blinded s scalar calculated from c_b
+   */
+  struct GNUNET_CRYPTO_CsBlindS s_scalar;
+};
 
 /**
  * @brief Type for *blinded* denomination signatures for Taler.
@@ -625,6 +695,12 @@ struct TALER_BlindedDenominationSignature
    */
   union
   {
+    /**
+     * If we use #TALER_DENOMINATION_CS in @a cipher.
+     * At this point only the blinded s scalar is used.
+     * The final signature consisting of r,s is built after unblinding.
+     */
+    struct TALER_BlindedDenominationCsSignAnswer blinded_cs_answer;
 
     /**
      * If we use #TALER_DENOMINATION_RSA in @a cipher.
@@ -657,6 +733,10 @@ struct TALER_DenominationPublicKey
    */
   union
   {
+    /**
+     * If we use #TALER_DENOMINATION_CS in @a cipher.
+     */
+    struct GNUNET_CRYPTO_CsPublicKey cs_public_key;
 
     /**
      * If we use #TALER_DENOMINATION_RSA in @a cipher.
@@ -683,6 +763,10 @@ struct TALER_DenominationPrivateKey
    */
   union
   {
+    /**
+     * If we use #TALER_DENOMINATION_CS in @a cipher.
+     */
+    struct GNUNET_CRYPTO_CsPrivateKey cs_private_key;
 
     /**
      * If we use #TALER_DENOMINATION_RSA in @a cipher.
@@ -692,6 +776,111 @@ struct TALER_DenominationPrivateKey
   } details;
 };
 
+/**
+ * @brief RSA Parameters to create blinded signature
+ *
+ */
+struct TALER_BlindedRsaPlanchet
+{
+  /**
+   * blinded message to be signed
+   * Note: is malloc()'ed!
+   */
+  void *blinded_msg;
+
+  /**
+   * size of the blinded message to be signed
+   */
+  size_t blinded_msg_size;
+};
+
+
+/**
+ * Withdraw nonce for CS denominations
+ */
+struct TALER_CsNonce
+{
+  /**
+   * 32 bit nonce to include in withdrawals
+   */
+  struct GNUNET_CRYPTO_CsNonce nonce;
+};
+
+
+/**
+ * @brief CS Parameters to create blinded signature
+ *
+ */
+struct TALER_BlindedCsPlanchet
+{
+  /**
+   * The Clause Schnorr c_0 and c_1 containing the blinded message
+   */
+  struct GNUNET_CRYPTO_CsC c[2];
+
+  /**
+   * Public Nonce
+   */
+  struct TALER_CsNonce nonce;
+};
+
+/**
+ * @brief Type including Parameters to create blinded signature
+ *
+ */
+struct TALER_BlindedPlanchet
+{
+  /**
+   * Type of the sign blinded message
+   */
+  enum TALER_DenominationCipher cipher;
+
+  /**
+   * Details, depending on @e cipher.
+   */
+  union
+  {
+    /**
+     * If we use #TALER_DENOMINATION_CS in @a cipher.
+     */
+    struct TALER_BlindedCsPlanchet cs_blinded_planchet;
+
+    /**
+     * If we use #TALER_DENOMINATION_RSA in @a cipher.
+     */
+    struct TALER_BlindedRsaPlanchet rsa_blinded_planchet;
+
+  } details;
+};
+
+/**
+ * Withdraw nonce for CS denominations
+ */
+struct TALER_RefreshNonce
+{
+  /**
+   * 32 bit nonce to include in withdrawals
+   */
+  struct GNUNET_CRYPTO_CsNonce nonce;
+};
+
+/**
+ * Public R for Cs denominations
+ */
+struct TALER_DenominationCsPublicR
+{
+  struct GNUNET_CRYPTO_CsRPublic r_pub[2];
+};
+
+/**
+ * Secret r for Cs denominations
+ */
+
+struct TALER_DenominationCsPrivateR
+{
+  struct GNUNET_CRYPTO_CsRSecret r[2];
+};
+
 
 /**
  * @brief Public information about a coin (including the public key
@@ -753,6 +942,43 @@ struct TALER_TrackTransferDetails
 };
 
 
+/**
+ * @brief Type of CS Values for withdrawal
+ */
+struct TALER_ExchangeWithdrawCsValues
+{
+  /**
+   * (non-blinded) r_pub
+   */
+  struct TALER_DenominationCsPublicR r_pub;
+};
+
+/**
+ * @brief Type of algorithm specific Values for withdrawal
+ */
+struct TALER_ExchangeWithdrawValues
+{
+
+  /**
+   * Type of the signature.
+   */
+  enum TALER_DenominationCipher cipher;
+
+  /**
+   * Details, depending on @e cipher.
+   */
+  union
+  {
+    /**
+     * If we use #TALER_DENOMINATION_CS in @a cipher.
+     */
+    struct TALER_ExchangeWithdrawCsValues cs_values;
+
+  } details;
+
+};
+
+
 /**
  * Free internals of @a denom_pub, but not @a denom_pub itself.
  *
@@ -761,16 +987,26 @@ struct TALER_TrackTransferDetails
 void
 TALER_denom_pub_free (struct TALER_DenominationPublicKey *denom_pub);
 
-
 /**
- * Create a blinding secret @a bs for @a cipher.
+ * @brief Method to derive withdraw nonce
  *
- * @param[out] bs blinding secret to initialize
+ * @param coin_priv private key of the coin
+ * @param nonce withdraw nonce included in the request to generate R_0 and R_1
  */
 void
-TALER_blinding_secret_create (union TALER_DenominationBlindingKeyP *bs);
+TALER_cs_withdraw_nonce_derive (const struct
+                                TALER_CoinSpendPrivateKeyP *coin_priv,
+                                struct TALER_CsNonce *nonce);
 
 
+/**
+ * @brief Method to generate a random withdraw nonce used in refresh protocol
+ *
+ * @param nonce withdraw nonce included in the request to generate R_0 and R_1
+ */
+void
+TALER_cs_withdraw_nonce_generate (struct TALER_CsNonce *nonce);
+
 /**
  * Initialize denomination public-private key pair.
  *
@@ -781,7 +1017,7 @@ TALER_blinding_secret_create (union 
TALER_DenominationBlindingKeyP *bs);
  * @param[out] denom_priv where to write the private key
  * @param[out] deonm_pub where to write the public key
  * @param cipher which type of cipher to use
- * @param ... cipher-specific parameters
+ * @param ... RSA key size (eg. 2048/3072/4096)
  * @return #GNUNET_OK on success, #GNUNET_NO if parameters were invalid
  */
 enum GNUNET_GenericReturnValue
@@ -809,6 +1045,22 @@ 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_DenominationCsPublicR *r_pub);
+
+
 /**
  * Blind coin for blind signing with @a dk using blinding secret @a coin_bks.
  *
@@ -816,6 +1068,7 @@ TALER_denom_sig_free (struct TALER_DenominationSignature 
*denom_sig);
  * @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
@@ -826,9 +1079,9 @@ TALER_denom_blind (const struct 
TALER_DenominationPublicKey *dk,
                    const union TALER_DenominationBlindingKeyP *coin_bks,
                    const struct TALER_AgeHash *age_commitment_hash,
                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                   const struct TALER_ExchangeWithdrawValues *alg_values,
                    struct TALER_CoinPubHash *c_hash,
-                   void **coin_ev,
-                   size_t *coin_ev_size);
+                   struct TALER_BlindedPlanchet *blinded_planchet);
 
 
 /**
@@ -836,15 +1089,13 @@ TALER_denom_blind (const struct 
TALER_DenominationPublicKey *dk,
  *
  * @param[out] denom_sig where to write the signature
  * @param denom_priv private key to use for signing
- * @param blinded_msg message to sign
- * @param blinded_msg_size number of bytes in @a blinded_msg
+ * @param blinded_planchet the planchet already blinded
  * @return #GNUNET_OK on success
  */
 enum GNUNET_GenericReturnValue
 TALER_denom_sign_blinded (struct TALER_BlindedDenominationSignature *denom_sig,
                           const struct TALER_DenominationPrivateKey 
*denom_priv,
-                          void *blinded_msg,
-                          size_t blinded_msg_size);
+                          const struct TALER_BlindedPlanchet 
*blinded_planchet);
 
 
 /**
@@ -854,6 +1105,7 @@ TALER_denom_sign_blinded (struct 
TALER_BlindedDenominationSignature *denom_sig,
  * @param bdenom_sig the blinded signature
  * @param bks blinding secret to use
  * @param denom_pub public key used for signing
+ * @param alg_values algorithm specific values
  * @return #GNUNET_OK on success
  */
 enum GNUNET_GenericReturnValue
@@ -890,7 +1142,7 @@ TALER_denom_pub_hash (const struct 
TALER_DenominationPublicKey *denom_pub,
  * @a denom_dst.
  *
  * @param[out] denom_dst target to copy to
- * @param denom_str public key to copy
+ * @param denom_src public key to copy
  */
 void
 TALER_denom_pub_deep_copy (struct TALER_DenominationPublicKey *denom_dst,
@@ -902,7 +1154,7 @@ TALER_denom_pub_deep_copy (struct 
TALER_DenominationPublicKey *denom_dst,
  * @a denom_dst.
  *
  * @param[out] denom_dst target to copy to
- * @param denom_str public key to copy
+ * @param denom_src public key to copy
  */
 void
 TALER_denom_sig_deep_copy (struct TALER_DenominationSignature *denom_dst,
@@ -914,7 +1166,7 @@ TALER_denom_sig_deep_copy (struct 
TALER_DenominationSignature *denom_dst,
  * @a denom_dst.
  *
  * @param[out] denom_dst target to copy to
- * @param denom_str public key to copy
+ * @param denom_src public key to copy
  */
 void
 TALER_blinded_denom_sig_deep_copy (
@@ -1005,13 +1257,14 @@ TALER_test_coin_valid (const struct 
TALER_CoinPublicInfo *coin_public_info,
 /**
  * Compute the hash of a blinded coin.
  *
- * @param coin_ev blinded coin
- * @param coin_ev_size number of bytes in @a coin_ev
+ * @param blinded_planchet blinded planchet
+ * @param denom_pub denomination publick key
  * @param[out] bch where to write the hash
+ * @return #GNUNET_OK when successful, #GNUNET_SYSERR if an internal error 
occured
  */
-void
-TALER_coin_ev_hash (const void *coin_ev,
-                    size_t coin_ev_size,
+enum GNUNET_GenericReturnValue
+TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
+                    const struct TALER_DenominationHash *denom_hash,
                     struct TALER_BlindedCoinHash *bch);
 
 
@@ -1056,10 +1309,9 @@ struct TALER_PlanchetSecretsP
   struct TALER_CoinSpendPrivateKeyP coin_priv;
 
   /**
-   * The blinding key.
+   * The blinding key. must be 32 byte
    */
   union TALER_DenominationBlindingKeyP blinding_key;
-
 };
 
 
@@ -1080,14 +1332,9 @@ struct TALER_PlanchetDetail
   struct TALER_DenominationHash denom_pub_hash;
 
   /**
-   * Blinded coin (see GNUNET_CRYPTO_rsa_blind()).  Note: is malloc()'ed!
-   */
-  void *coin_ev;
-
-  /**
-   * Number of bytes in @a coin_ev.
+   * The blinded planchet
    */
-  size_t coin_ev_size;
+  struct TALER_BlindedPlanchet blinded_planchet;
 };
 
 
@@ -1222,10 +1469,23 @@ TALER_planchet_setup_refresh (const struct 
TALER_TransferSecretP *secret_seed,
  * Setup information for a fresh coin.
  *
  * @param[out] ps value to initialize
+ * @oaram alg_values WitdrawValues containing cipher
  */
 void
-TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps);
+TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps,
+                             const struct
+                             TALER_ExchangeWithdrawValues *alg_values);
 
+/**
+ * Create a blinding secret @a bs for @a cipher.
+ *
+ * @param[out] ps planchet with blinding secret to initialize
+ * @param alg_values withdraw values containing cipher and additional CS values
+ */
+void
+TALER_planchet_blinding_secret_create (struct TALER_PlanchetSecretsP *ps,
+                                       const struct
+                                       TALER_ExchangeWithdrawValues 
*alg_values);
 
 /**
  * Prepare a planchet for tipping.  Creates and blinds a coin.
@@ -1234,16 +1494,28 @@ TALER_planchet_setup_random (struct 
TALER_PlanchetSecretsP *ps);
  * @param ps secret planchet internals (for #TALER_planchet_to_coin)
  * @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
+ *               other withdraw operations, pd->blinded_planchet.cipher will 
be set
+ *               to cipher from dk
  * @return #GNUNET_OK on success
  */
 enum GNUNET_GenericReturnValue
 TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
-                        const struct TALER_PlanchetSecretsP *ps,
+                        const struct TALER_ExchangeWithdrawValues *alg_values,
+                        struct TALER_PlanchetSecretsP *ps,
                         struct TALER_CoinPubHash *c_hash,
                         struct TALER_PlanchetDetail *pd);
 
 
+/**
+ * Frees blinded message inside blinded planchet depending on 
blinded_planchet->cipher
+ * Does not free the @a blinded_planchet itself!
+ *
+ * @param blinded_planchet blnded planchet
+ */
+void
+TALER_blinded_planchet_free (struct TALER_BlindedPlanchet *blinded_planchet);
+
+
 /**
  * Obtain a coin from the planchet's secrets and the blind signature
  * of the exchange.
@@ -1256,12 +1528,13 @@ TALER_planchet_prepare (const struct 
TALER_DenominationPublicKey *dk,
  * @return #GNUNET_OK on success
  */
 enum GNUNET_GenericReturnValue
-TALER_planchet_to_coin (
-  const struct TALER_DenominationPublicKey *dk,
-  const struct TALER_BlindedDenominationSignature *blind_sig,
-  const struct TALER_PlanchetSecretsP *ps,
-  const struct TALER_CoinPubHash *c_hash,
-  struct TALER_FreshCoin *coin);
+TALER_planchet_to_coin (const struct TALER_DenominationPublicKey *dk,
+                        const struct
+                        TALER_BlindedDenominationSignature *blind_sig,
+                        const struct TALER_PlanchetSecretsP *ps,
+                        const struct TALER_CoinPubHash *c_hash,
+                        const struct TALER_ExchangeWithdrawValues *alg_values,
+                        struct TALER_FreshCoin *coin);
 
 
 /* ****************** Refresh crypto primitives ************* */
@@ -1497,6 +1770,149 @@ void
 TALER_CRYPTO_helper_rsa_disconnect (
   struct TALER_CRYPTO_RsaDenominationHelper *dh);
 
+/* **************** Helper-based CS operations **************** */
+
+/**
+ * Handle for talking to an Denomination key signing helper.
+ */
+struct TALER_CRYPTO_CsDenominationHelper;
+
+/**
+ * Function called with information about available keys for signing.  Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure
+ * @param section_name name of the denomination type in the configuration;
+ *                 NULL if the key has been revoked or purged
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param h_cs hash of the CS @a denom_pub that is available (or was purged)
+ * @param denom_pub the public key itself, NULL if the key was revoked or 
purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+typedef void
+(*TALER_CRYPTO_CsDenominationKeyStatusCallback)(
+  void *cls,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct TALER_CsPubHashP *h_cs,
+  const struct TALER_DenominationPublicKey *denom_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig);
+
+
+/**
+ * Initiate connection to an denomination key helper.
+ *
+ * @param cfg configuration to use
+ * @param dkc function to call with key information
+ * @param dkc_cls closure for @a dkc
+ * @return NULL on error (such as bad @a cfg).
+ */
+struct TALER_CRYPTO_CsDenominationHelper *
+TALER_CRYPTO_helper_cs_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  TALER_CRYPTO_CsDenominationKeyStatusCallback dkc,
+  void *dkc_cls);
+
+
+/**
+ * Function to call to 'poll' for updates to the available key material.
+ * Should be called whenever it is important that the key material status is
+ * current, like when handling a "/keys" request.  This function basically
+ * briefly checks if there are messages from the helper announcing changes to
+ * denomination keys.
+ *
+ * @param dh helper process connection
+ */
+void
+TALER_CRYPTO_helper_cs_poll (struct TALER_CRYPTO_CsDenominationHelper *dh);
+
+
+/**
+ * Request helper @a dh to sign @a msg using the public key corresponding to
+ * @a h_denom_pub.
+ *
+ * This operation will block until the signature has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @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
+ */
+struct TALER_BlindedDenominationSignature
+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);
+
+
+/**
+ * Ask the helper to revoke the public key associated with @param h_cs .
+ * Will cause the helper to tell all clients that the key is now unavailable,
+ * and to create a replacement key.
+ *
+ * This operation will block until the revocation request has been
+ * transmitted.  Should this process receive a signal (that is not ignored)
+ * while the operation is pending, the operation may fail. If the key is
+ * unknown, this function will also appear to have succeeded. To be sure that
+ * the revocation worked, clients must watch the denomination key status
+ * callback.
+ *
+ * @param dh helper to process connection
+ * @param h_cs hash of the CS public key to revoke
+ */
+void
+TALER_CRYPTO_helper_cs_revoke (
+  struct TALER_CRYPTO_CsDenominationHelper *dh,
+  const struct TALER_CsPubHashP *h_cs);
+
+
+/**
+ * Ask the helper to derive R using the @param nonce and denomination key
+ * associated with @param 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
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @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
+ */
+struct TALER_DenominationCsPublicR
+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);
+
+
+/**
+ * Close connection to @a dh.
+ *
+ * @param[in] dh connection to close
+ */
+void
+TALER_CRYPTO_helper_cs_disconnect (
+  struct TALER_CRYPTO_CsDenominationHelper *dh);
 
 /**
  * Handle for talking to an online key signing helper.
@@ -2261,6 +2677,47 @@ TALER_exchange_secmod_rsa_verify (
   const struct TALER_SecurityModuleSignatureP *secm_sig);
 
 
+/**
+ * Create security module denomination signature.
+ *
+ * @param h_cs hash of the CS public key to sign
+ * @param section_name name of the section in the configuration
+ * @param start_sign starting point of validity for signing
+ * @param duration how long will the key be in use
+ * @param secm_priv security module key to sign with
+ * @param[out] secm_sig where to write the signature
+ */
+void
+TALER_exchange_secmod_cs_sign (
+  const struct TALER_CsPubHashP *h_cs,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_sign,
+  struct GNUNET_TIME_Relative duration,
+  const struct TALER_SecurityModulePrivateKeyP *secm_priv,
+  struct TALER_SecurityModuleSignatureP *secm_sig);
+
+
+/**
+ * Verify security module denomination signature.
+ *
+ * @param h_cs hash of the public key to validate
+ * @param section_name name of the section in the configuration
+ * @param start_sign starting point of validity for signing
+ * @param duration how long will the key be in use
+ * @param secm_pub public key to verify against
+ * @param secm_sig the signature the signature
+ * @return #GNUNET_OK if the signature is valid
+ */
+enum GNUNET_GenericReturnValue
+TALER_exchange_secmod_cs_verify (
+  const struct TALER_CsPubHashP *h_cs,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_sign,
+  struct GNUNET_TIME_Relative duration,
+  const struct TALER_SecurityModulePublicKeyP *secm_pub,
+  const struct TALER_SecurityModuleSignatureP *secm_sig);
+
+
 /**
  * Create denomination key validity signature by the auditor.
  *
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index caa61c5f..68c97186 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1033,6 +1033,96 @@ void
 TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund);
 
 
+/* ********************* POST /csr *********************** */
+
+
+/**
+ * @brief A /csr Handle
+ */
+struct TALER_EXCHANGE_CsRHandle;
+
+
+/**
+ * Details about a response for a CS R request.
+ */
+struct TALER_EXCHANGE_CsRResponse
+{
+  /**
+   * HTTP response data.
+   */
+  struct TALER_EXCHANGE_HttpResponse hr;
+
+  /**
+   * Details about the response.
+   */
+  union
+  {
+    /**
+     * Details if the status is #MHD_HTTP_OK.
+     */
+    struct
+    {
+      /**
+       * Signature over the coin.
+       */
+      struct TALER_DenominationCsPublicR r_pubs;
+    } success;
+
+    /**
+     * Details if the status is #MHD_HTTP_GONE.
+     */
+    struct
+    {
+      /* TODO: returning full details is not implemented */
+    } gone;
+
+  } details;
+};
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * CS R request to a exchange.
+ *
+ * @param cls closure
+ * @param csrr response details
+ */
+typedef void
+(*TALER_EXCHANGE_CsRCallback) (void *cls,
+                               const struct TALER_EXCHANGE_CsRResponse *csrr);
+
+
+/**
+ * Get a CS R using a /csr request.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk denomination of coin the R's will be used for
+ * @param nonce public nonce for CS R request
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for the above callback
+ * @return handle for the operation on success, NULL on error, i.e.
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_CsRHandle *
+TALER_EXCHANGE_csr (struct TALER_EXCHANGE_Handle *exchange,
+                    const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                    const struct TALER_CsNonce *nonce,
+                    TALER_EXCHANGE_CsRCallback res_cb,
+                    void *res_cb_cls);
+
+
+/**
+ *
+ * Cancel a CS R request.  This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param csrh the withdraw handle
+ */
+void
+TALER_EXCHANGE_csr_cancel (struct TALER_EXCHANGE_CsRHandle *csrh);
+
+
 /* ********************* GET /reserves/$RESERVE_PUB *********************** */
 
 
@@ -1370,7 +1460,8 @@ 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,
+  struct TALER_PlanchetSecretsP *ps,
+  struct TALER_ExchangeWithdrawValues *alg_values,
   TALER_EXCHANGE_WithdrawCallback res_cb,
   void *res_cb_cls);
 
@@ -2497,10 +2588,15 @@ struct TALER_EXCHANGE_FutureKeys
   struct TALER_SecurityModulePublicKeyP signkey_secmod_public_key;
 
   /**
-   * Public key of the denomination security module.
+   * Public key of the RSA denomination security module.
    */
   struct TALER_SecurityModulePublicKeyP denom_secmod_public_key;
 
+  /**
+   * Public key of the CS denomination security module.
+   */
+  struct TALER_SecurityModulePublicKeyP denom_secmod_cs_public_key;
+
   /**
    * Offline master public key used by this exchange.
    */
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 51ebe6d9..ea692622 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -117,6 +117,20 @@ TALER_JSON_pack_blinded_denom_sig (
   const struct TALER_BlindedDenominationSignature *sig);
 
 
+/**
+ * Generate packer instruction for a JSON field of type
+ * blinded planchet.
+ *
+ * @param name name of the field to add to the object
+ * @param blinded_planchet blinded planchet
+ * @return json pack specification
+ */
+struct GNUNET_JSON_PackSpec
+TALER_JSON_pack_blinded_planchet (
+  const char *name,
+  const struct TALER_BlindedPlanchet *blinded_planchet);
+
+
 /**
  * Generate packer instruction for a JSON field of type
  * amount.
@@ -260,6 +274,19 @@ TALER_JSON_spec_blinded_denom_sig (
   struct TALER_BlindedDenominationSignature *sig);
 
 
+/**
+ * Generate line in parser specification for a
+ * blinded planchet.
+ *
+ * @param field name of the field
+ * @param[out] blinded_planchet the blinded planchet to initialize
+ * @return corresponding field spec
+ */
+struct GNUNET_JSON_Specification
+TALER_JSON_spec_blinded_planchet (const char *field,
+                                  struct TALER_BlindedPlanchet 
*blinded_planchet);
+
+
 /**
  * The expected field stores a possibly internationalized string.
  * Internationalization means that there is another field "$name_i18n"
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index 3ad1121c..3c31a4b6 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -287,6 +287,11 @@
  */
 #define TALER_SIGNATURE_SM_SIGNING_KEY 1251
 
+/**
+ * Signature on a denomination key announcement.
+ */
+#define TALER_SIGNATURE_SM_CS_DENOMINATION_KEY 1252
+
 /*******************/
 /* Test signatures */
 /*******************/
@@ -341,7 +346,7 @@ struct TALER_DenominationKeyAnnouncementPS
   /**
    * Hash of the denomination public key.
    */
-  struct TALER_RsaPubHashP h_rsa;
+  struct TALER_DenominationHash h_denom;
 
   /**
    * Hash of the section name in the configuration of this denomination.
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 20e3145f..c6bebbee 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -66,11 +66,13 @@ TALER_TESTING_make_wire_details (const char *payto);
  *
  * @param keys array of keys to search
  * @param amount coin value to look for
+ * @param cipher denomination cipher
  * @return NULL if no matching key was found
  */
 const struct TALER_EXCHANGE_DenomPublicKey *
 TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
-                       const struct TALER_Amount *amount);
+                       const struct TALER_Amount *amount,
+                       const enum TALER_DenominationCipher cipher);
 
 
 /**
@@ -1288,6 +1290,24 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
                                    unsigned int expected_response_code);
 
 
+/**
+ * Create a withdraw command using a CS denomination, letting the caller 
specify
+ * the desired amount as string.
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw 
from
+ * @param amount how much we withdraw.
+ * @param expected_response_code which HTTP response code
+ *        we expect from the exchange.
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_cs_amount (const char *label,
+                                      const char *reserve_reference,
+                                      const char *amount,
+                                      unsigned int expected_response_code);
+
+
 /**
  * Create a withdraw command, letting the caller specify
  * the desired amount as string and also re-using an existing
@@ -1312,6 +1332,30 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key (
   unsigned int expected_response_code);
 
 
+/**
+ * Create a CS withdraw command, letting the caller specify
+ * the desired amount as string and also re-using an existing
+ * coin private key in the process (violating the specification,
+ * which will result in an error when spending the coin!).
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw 
from
+ * @param amount how much we withdraw.
+ * @param coin_ref reference to (withdraw/reveal) command of a coin
+ *        from which we should re-use the private key
+ * @param expected_response_code which HTTP response code
+ *        we expect from the exchange.
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_cs_amount_reuse_key (
+  const char *label,
+  const char *reserve_reference,
+  const char *amount,
+  const char *coin_ref,
+  unsigned int expected_response_code);
+
+
 /**
  * Create withdraw command, letting the caller specify the
  * amount by a denomination key.
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 26440cb1..f64811a4 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -444,7 +444,7 @@ TALER_yna_to_string (enum TALER_EXCHANGE_YesNoAll yna);
  * @param c the character to search for
  * @return char* the first occurence of `c` in `s`
  */
-char * strchrnul (const char *s, int c);
+char *strchrnul (const char *s, int c);
 
 #endif
 
diff --git a/src/json/json_helper.c b/src/json/json_helper.c
index 1942d09b..4acac506 100644
--- a/src/json/json_helper.c
+++ b/src/json/json_helper.c
@@ -262,6 +262,26 @@ parse_denom_pub (void *cls,
         GNUNET_JSON_spec_end ()
       };
 
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_fixed ("cs_public_key",
+                                &denom_pub->details.cs_public_key,
+                                sizeof (denom_pub->details.cs_public_key)),
+        GNUNET_JSON_spec_end ()
+      };
+
       if (GNUNET_OK !=
           GNUNET_JSON_parse (root,
                              ispec,
@@ -357,6 +377,27 @@ parse_denom_sig (void *cls,
         GNUNET_JSON_spec_end ()
       };
 
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_fixed_auto ("cs_signature_r",
+                                     &denom_sig->details.cs_signature.r_point),
+        GNUNET_JSON_spec_fixed_auto ("cs_signature_s",
+                                     
&denom_sig->details.cs_signature.s_scalar),
+        GNUNET_JSON_spec_end ()
+      };
+
       if (GNUNET_OK !=
           GNUNET_JSON_parse (root,
                              ispec,
@@ -463,6 +504,29 @@ parse_blinded_denom_sig (void *cls,
       }
       return GNUNET_OK;
     }
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_uint32 ("b",
+                                 &denom_sig->details.blinded_cs_answer.b),
+        GNUNET_JSON_spec_fixed_auto ("s",
+                                     &denom_sig->details.blinded_cs_answer.
+                                     s_scalar),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+    break;
   default:
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -503,6 +567,129 @@ TALER_JSON_spec_blinded_denom_sig (
 }
 
 
+/**
+ * Parse given JSON object to blinded planchet.
+ *
+ * @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_blinded_planchet (void *cls,
+                        json_t *root,
+                        struct GNUNET_JSON_Specification *spec)
+{
+  struct TALER_BlindedPlanchet *blinded_planchet = spec->ptr;
+  uint32_t cipher;
+  struct GNUNET_JSON_Specification dspec[] = {
+    GNUNET_JSON_spec_uint32 ("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;
+  }
+  blinded_planchet->cipher = (enum TALER_DenominationCipher) cipher;
+  switch (blinded_planchet->cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_varsize (
+          "rsa_blinded_planchet",
+          &blinded_planchet->details.rsa_blinded_planchet.blinded_msg,
+          &blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_fixed_auto (
+          "cs_nonce",
+          &blinded_planchet->details.cs_blinded_planchet.nonce),
+        GNUNET_JSON_spec_fixed_auto (
+          "cs_blinded_c0",
+          &blinded_planchet->details.cs_blinded_planchet.c[0]),
+        GNUNET_JSON_spec_fixed_auto (
+          "cs_blinded_c1",
+          &blinded_planchet->details.cs_blinded_planchet.c[1]),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+    break;
+  default:
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+/**
+ * Cleanup data left from parsing blinded planchet.
+ *
+ * @param cls closure, NULL
+ * @param[out] spec where to free the data
+ */
+static void
+clean_blinded_planchet (void *cls,
+                        struct GNUNET_JSON_Specification *spec)
+{
+  struct TALER_BlindedPlanchet *blinded_planchet = spec->ptr;
+
+  (void) cls;
+  TALER_blinded_planchet_free (blinded_planchet);
+}
+
+
+struct GNUNET_JSON_Specification
+TALER_JSON_spec_blinded_planchet (const char *field,
+                                  struct TALER_BlindedPlanchet 
*blinded_planchet)
+{
+  struct GNUNET_JSON_Specification ret = {
+    .parser = &parse_blinded_planchet,
+    .cleaner = &clean_blinded_planchet,
+    .field = field,
+    .ptr = blinded_planchet
+  };
+
+  return ret;
+}
+
+
 /**
  * Closure for #parse_i18n_string.
  */
@@ -686,7 +873,6 @@ TALER_JSON_parse_agemask (const json_t *root,
   {
     return GNUNET_SYSERR;
   }
-
   return GNUNET_OK;
 }
 
diff --git a/src/json/json_pack.c b/src/json/json_pack.c
index 6fea72a3..cf6504c0 100644
--- a/src/json/json_pack.c
+++ b/src/json/json_pack.c
@@ -68,6 +68,17 @@ TALER_JSON_pack_denom_pub (
           GNUNET_JSON_pack_rsa_public_key ("rsa_public_key",
                                            pk->details.rsa_public_key));
     break;
+  case TALER_DENOMINATION_CS:
+    ps.object
+      = GNUNET_JSON_PACK (
+          GNUNET_JSON_pack_uint64 ("cipher",
+                                   TALER_DENOMINATION_CS),
+          GNUNET_JSON_pack_uint64 ("age_mask",
+                                   pk->age_mask.mask),
+          GNUNET_JSON_pack_data_varsize ("cs_public_key",
+                                         &pk->details.cs_public_key,
+                                         sizeof (pk->details.cs_public_key)));
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -87,12 +98,20 @@ TALER_JSON_pack_denom_sig (
   switch (sig->cipher)
   {
   case TALER_DENOMINATION_RSA:
-    ps.object
-      = GNUNET_JSON_PACK (
-          GNUNET_JSON_pack_uint64 ("cipher",
-                                   TALER_DENOMINATION_RSA),
-          GNUNET_JSON_pack_rsa_signature ("rsa_signature",
-                                          sig->details.rsa_signature));
+    ps.object = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_uint64 ("cipher",
+                               TALER_DENOMINATION_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_data_auto ("cs_signature_r",
+                                  &sig->details.cs_signature.r_point),
+      GNUNET_JSON_pack_data_auto ("cs_signature_s",
+                                  &sig->details.cs_signature.s_scalar));
     break;
   default:
     GNUNET_assert (0);
@@ -113,12 +132,61 @@ TALER_JSON_pack_blinded_denom_sig (
   switch (sig->cipher)
   {
   case TALER_DENOMINATION_RSA:
-    ps.object
-      = GNUNET_JSON_PACK (
-          GNUNET_JSON_pack_uint64 ("cipher",
-                                   TALER_DENOMINATION_RSA),
-          GNUNET_JSON_pack_rsa_signature ("blinded_rsa_signature",
-                                          sig->details.blinded_rsa_signature));
+    ps.object = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_uint64 ("cipher",
+                               TALER_DENOMINATION_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_uint64 ("b",
+                               sig->details.blinded_cs_answer.b),
+      GNUNET_JSON_pack_data_auto ("s",
+                                  &sig->details.blinded_cs_answer.s_scalar));
+    break;
+  default:
+    GNUNET_assert (0);
+  }
+  return ps;
+}
+
+
+struct GNUNET_JSON_PackSpec
+TALER_JSON_pack_blinded_planchet (
+  const char *name,
+  const struct TALER_BlindedPlanchet *blinded_planchet)
+{
+  struct GNUNET_JSON_PackSpec ps = {
+    .field_name = name,
+  };
+
+  switch (blinded_planchet->cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    ps.object = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_uint64 ("cipher",
+                               TALER_DENOMINATION_RSA),
+      GNUNET_JSON_pack_data_varsize (
+        "rsa_blinded_planchet",
+        blinded_planchet->details.rsa_blinded_planchet.blinded_msg,
+        blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size));
+    break;
+  case TALER_DENOMINATION_CS:
+    ps.object = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_uint64 ("cipher",
+                               TALER_DENOMINATION_CS),
+      GNUNET_JSON_pack_data_auto (
+        "cs_nonce",
+        &blinded_planchet->details.cs_blinded_planchet.nonce),
+      GNUNET_JSON_pack_data_auto (
+        "cs_blinded_c0",
+        &blinded_planchet->details.cs_blinded_planchet.c[0]),
+      GNUNET_JSON_pack_data_auto (
+        "cs_blinded_c1",
+        &blinded_planchet->details.cs_blinded_planchet.c[1]));
     break;
   default:
     GNUNET_assert (0);
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 3398bdf1..fe2a0b6b 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -24,6 +24,7 @@ libtalerexchange_la_SOURCES = \
   exchange_api_auditor_add_denomination.c \
   exchange_api_curl_defaults.c exchange_api_curl_defaults.h \
   exchange_api_common.c \
+  exchange_api_csr.c \
   exchange_api_handle.c exchange_api_handle.h \
   exchange_api_deposit.c \
   exchange_api_deposits_get.c \
diff --git a/src/lib/exchange_api_csr.c b/src/lib/exchange_api_csr.c
new file mode 100644
index 00000000..d99b08ca
--- /dev/null
+++ b/src/lib/exchange_api_csr.c
@@ -0,0 +1,292 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2021 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
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/exchange_api_csr.c
+ * @brief Implementation of /csr requests (get R in exchange used for Clause 
Schnorr withdraw and refresh)
+ * @author Lucien Heuzeveldt
+ * @author Gian Demarmels
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A Clause Schnorr R Handle
+ */
+struct TALER_EXCHANGE_CsRHandle
+{
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_CsRCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Denomination key we are withdrawing.
+   */
+  struct TALER_EXCHANGE_DenomPublicKey pk;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext post_ctx;
+};
+
+
+/**
+ * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
+ * Extract the coin's signature and return it to the caller.  The signature we
+ * get from the exchange is for the blinded value.  Thus, we first must
+ * unblind it and then should verify its validity against our coin's hash.
+ *
+ * If everything checks out, we return the unblinded signature
+ * to the application via the callback.
+ *
+ * @param wh operation handle
+ * @param json reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static enum GNUNET_GenericReturnValue
+csr_ok (const json_t *json,
+        struct TALER_EXCHANGE_CsRResponse *csrr)
+{
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed ("r_pub_0",
+                            &csrr->details.success.r_pubs.r_pub[0],
+                            sizeof (struct GNUNET_CRYPTO_CsRPublic)),
+    GNUNET_JSON_spec_fixed ("r_pub_1",
+                            &csrr->details.success.r_pubs.r_pub[1],
+                            sizeof (struct GNUNET_CRYPTO_CsRPublic)),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_JSON_parse_free (spec);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the HTTP /csr request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_CsRHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_csr_finished (void *cls,
+                     long response_code,
+                     const void *response)
+{
+  struct TALER_EXCHANGE_CsRHandle *csrh = cls;
+  const json_t *j = response;
+  struct TALER_EXCHANGE_HttpResponse hr = {
+    .reply = j,
+    .http_status = (unsigned int) response_code
+  };
+  struct TALER_EXCHANGE_CsRResponse csrr = {
+    .hr = hr
+  };
+
+  csrh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    csrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        csr_ok (j,
+                &csrr))
+    {
+      GNUNET_break_op (0);
+      csrr.hr.http_status = 0;
+      csrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+      break;
+    }
+    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 */
+    csrr.hr.ec = TALER_JSON_get_error_code (j);
+    csrr.hr.hint = TALER_JSON_get_error_hint (j);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, the exchange basically just says
+       that it doesn't know the /csr endpoint or denomination.
+       Can happen if the exchange doesn't support Clause Schnorr.
+       We should simply pass the JSON reply to the application. */
+    csrr.hr.ec = TALER_JSON_get_error_code (j);
+    csrr.hr.hint = TALER_JSON_get_error_hint (j);
+    break;
+  case MHD_HTTP_GONE:
+    /* could happen if denomination was revoked */
+    /* Note: one might want to check /keys for revocation
+       signature here, alas tricky in case our /keys
+       is outdated => left to clients */
+    csrr.hr.ec = TALER_JSON_get_error_code (j);
+    csrr.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 */
+    csrr.hr.ec = TALER_JSON_get_error_code (j);
+    csrr.hr.hint = TALER_JSON_get_error_hint (j);
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_break_op (0);
+    csrr.hr.ec = TALER_JSON_get_error_code (j);
+    csrr.hr.hint = TALER_JSON_get_error_hint (j);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u/%d for CS R request\n",
+                (unsigned int) response_code,
+                (int) hr.ec);
+    break;
+  }
+  csrh->cb (csrh->cb_cls,
+            &csrr);
+  csrh->cb = NULL;
+  TALER_EXCHANGE_csr_cancel (csrh);
+}
+
+
+struct TALER_EXCHANGE_CsRHandle *
+TALER_EXCHANGE_csr (struct TALER_EXCHANGE_Handle *exchange,
+                    const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                    const struct TALER_CsNonce *nonce,
+                    TALER_EXCHANGE_CsRCallback res_cb,
+                    void *res_cb_cls)
+{
+  struct TALER_EXCHANGE_CsRHandle *csrh;
+
+  if (TALER_DENOMINATION_CS != pk->key.cipher)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  csrh = GNUNET_new (struct TALER_EXCHANGE_CsRHandle);
+  csrh->exchange = exchange;
+  csrh->cb = res_cb;
+  csrh->cb_cls = res_cb_cls;
+  csrh->pk = *pk;
+
+  {
+    json_t *csr_obj;
+
+    csr_obj = GNUNET_JSON_PACK (GNUNET_JSON_pack_data_varsize ("nonce",
+                                                               nonce,
+                                                               sizeof(struct
+                                                                      
TALER_CsNonce)),
+                                GNUNET_JSON_pack_data_varsize 
("denom_pub_hash",
+                                                               &pk->h_key,
+                                                               sizeof(struct
+                                                                      
TALER_DenominationHash)));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Attempting to request R with denomination public key %s\n",
+                TALER_B2S (&pk->key.details.cs_public_key));
+    csrh->url = TEAH_path_to_url (exchange,
+                                  "/csr");
+    if (NULL == csrh->url)
+    {
+      json_decref (csr_obj);
+      GNUNET_free (csrh);
+      return NULL;
+    }
+    {
+      CURL *eh;
+      struct GNUNET_CURL_Context *ctx;
+
+      ctx = TEAH_handle_to_context (exchange);
+      eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url);
+      if ( (NULL == eh) ||
+           (GNUNET_OK !=
+            TALER_curl_easy_post (&csrh->post_ctx,
+                                  eh,
+                                  csr_obj)) )
+      {
+        GNUNET_break (0);
+        if (NULL != eh)
+          curl_easy_cleanup (eh);
+        json_decref (csr_obj);
+        GNUNET_free (csrh->url);
+        GNUNET_free (csrh);
+        return NULL;
+      }
+      json_decref (csr_obj);
+      csrh->job = GNUNET_CURL_job_add2 (ctx,
+                                        eh,
+                                        csrh->post_ctx.headers,
+                                        &handle_csr_finished,
+                                        csrh);
+    }
+  }
+
+  return csrh;
+}
+
+
+void
+TALER_EXCHANGE_csr_cancel (struct TALER_EXCHANGE_CsRHandle *csrh)
+{
+  if (NULL != csrh->job)
+  {
+    GNUNET_CURL_job_cancel (csrh->job);
+    csrh->job = NULL;
+  }
+  GNUNET_free (csrh->url);
+  TALER_curl_easy_post_finished (&csrh->post_ctx);
+  GNUNET_free (csrh);
+}
diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c
index ec085b53..e241f543 100644
--- a/src/lib/exchange_api_link.c
+++ b/src/lib/exchange_api_link.c
@@ -135,6 +135,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
   *coin_priv = fc.coin_priv;
   /* verify link_sig */
   {
+    struct TALER_ExchangeWithdrawValues alg_values;
     struct TALER_PlanchetDetail pd;
     struct TALER_CoinPubHash c_hash;
     struct TALER_CoinSpendPublicKeyP old_coin_pub;
@@ -142,8 +143,11 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle 
*lh,
 
     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,
                                 &fc,
                                 &c_hash,
                                 &pd))
@@ -152,10 +156,9 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle 
*lh,
       GNUNET_JSON_parse_free (spec);
       return GNUNET_SYSERR;
     }
-    GNUNET_CRYPTO_hash (pd.coin_ev,
-                        pd.coin_ev_size,
-                        &coin_envelope_hash.hash);
-
+    TALER_coin_ev_hash (&pd.blinded_planchet,
+                        &pd.denom_pub_hash,
+                        &coin_envelope_hash);
     if (GNUNET_OK !=
         TALER_wallet_link_verify (&pd.denom_pub_hash,
                                   trans_pub,
@@ -164,11 +167,11 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle 
*lh,
                                   &link_sig))
     {
       GNUNET_break_op (0);
-      GNUNET_free (pd.coin_ev);
+      TALER_blinded_planchet_free (&pd.blinded_planchet);
       GNUNET_JSON_parse_free (spec);
       return GNUNET_SYSERR;
     }
-    GNUNET_free (pd.coin_ev);
+    TALER_blinded_planchet_free (&pd.blinded_planchet);
   }
 
   /* clean up */
diff --git a/src/lib/exchange_api_management_get_keys.c 
b/src/lib/exchange_api_management_get_keys.c
index e776082d..4d686633 100644
--- a/src/lib/exchange_api_management_get_keys.c
+++ b/src/lib/exchange_api_management_get_keys.c
@@ -92,6 +92,8 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
                                  &fk.master_pub),
     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
                                  &fk.denom_secmod_public_key),
+    GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
+                                 &fk.denom_secmod_cs_public_key),
     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
                                  &fk.signkey_secmod_public_key),
     GNUNET_JSON_spec_end ()
@@ -243,6 +245,26 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle 
*gh,
           }
         }
         break;
+      case TALER_DENOMINATION_CS:
+        {
+          struct TALER_CsPubHashP h_cs;
+
+          TALER_cs_pub_hash (&denom_key->key.details.cs_public_key,
+                             &h_cs);
+          if (GNUNET_OK !=
+              TALER_exchange_secmod_cs_verify (&h_cs,
+                                               section_name,
+                                               denom_key->valid_from,
+                                               duration,
+                                               &fk.denom_secmod_cs_public_key,
+                                               &denom_key->denom_secmod_sig))
+          {
+            GNUNET_break_op (0);
+            ok = false;
+            break;
+          }
+        }
+        break;
       default:
         GNUNET_break_op (0);
         ok = false;
diff --git a/src/lib/exchange_api_refresh_common.c 
b/src/lib/exchange_api_refresh_common.c
index 3e367566..65c7d6ba 100644
--- a/src/lib/exchange_api_refresh_common.c
+++ b/src/lib/exchange_api_refresh_common.c
@@ -424,14 +424,18 @@ TALER_EXCHANGE_refresh_prepare (
     {
       struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
       struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
+      struct TALER_ExchangeWithdrawValues alg_values;
       struct TALER_PlanchetDetail pd;
       struct TALER_CoinPubHash c_hash;
 
       TALER_planchet_setup_refresh (&trans_sec[i],
                                     j,
                                     fc);
+      // TODO: implement cipher handling
+      alg_values.cipher = TALER_DENOMINATION_RSA;
       if (GNUNET_OK !=
           TALER_planchet_prepare (&md.fresh_pks[j],
+                                  &alg_values,
                                   fc,
                                   &c_hash,
                                   &pd))
@@ -441,8 +445,10 @@ TALER_EXCHANGE_refresh_prepare (
         return NULL;
       }
       rcd->dk = &md.fresh_pks[j];
-      rcd->coin_ev = pd.coin_ev;
-      rcd->coin_ev_size = pd.coin_ev_size;
+      rcd->coin_ev =
+        pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg;
+      rcd->coin_ev_size =
+        pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg_size;
     }
   }
 
diff --git a/src/lib/exchange_api_refreshes_reveal.c 
b/src/lib/exchange_api_refreshes_reveal.c
index 2b7fcf8c..82f92322 100644
--- a/src/lib/exchange_api_refreshes_reveal.c
+++ b/src/lib/exchange_api_refreshes_reveal.c
@@ -138,6 +138,7 @@ refresh_reveal_ok (struct 
TALER_EXCHANGE_RefreshesRevealHandle *rrh,
     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[] = {
@@ -170,11 +171,14 @@ refresh_reveal_ok (struct 
TALER_EXCHANGE_RefreshesRevealHandle *rrh,
     TALER_coin_pub_hash (&coin_pub,
                          NULL, /* FIXME-Oec */
                          &coin_hash);
+    // TODO: implement cipher handling
+    alg_values.cipher = TALER_DENOMINATION_RSA;
     if (GNUNET_OK !=
         TALER_planchet_to_coin (pk,
                                 &blind_sig,
                                 fc,
                                 &coin_hash,
+                                &alg_values,
                                 &coin))
     {
       GNUNET_break_op (0);
@@ -346,6 +350,7 @@ TALER_EXCHANGE_refreshes_reveal (
   for (unsigned int i = 0; i<md->num_fresh_coins; i++)
   {
     struct TALER_DenominationHash denom_hash;
+    struct TALER_ExchangeWithdrawValues alg_values;
     struct TALER_PlanchetDetail pd;
     struct TALER_CoinPubHash c_hash;
 
@@ -356,8 +361,11 @@ TALER_EXCHANGE_refreshes_reveal (
                                           GNUNET_JSON_from_data_auto (
                                             &denom_hash)));
 
+    // TODO: implement cipher handling
+    alg_values.cipher = TALER_DENOMINATION_RSA;
     if (GNUNET_OK !=
         TALER_planchet_prepare (&md->fresh_pks[i],
+                                &alg_values,
                                 &md->fresh_coins[noreveal_index][i],
                                 &c_hash,
                                 &pd))
@@ -370,15 +378,22 @@ TALER_EXCHANGE_refreshes_reveal (
     }
     GNUNET_assert (0 ==
                    json_array_append_new (coin_evs,
-                                          GNUNET_JSON_from_data (pd.coin_ev,
-                                                                 
pd.coin_ev_size)));
+                                          GNUNET_JSON_from_data (
+                                            pd.blinded_planchet.details.
+                                            rsa_blinded_planchet.blinded_msg,
+                                            pd.
+                                            blinded_planchet.details.
+                                            rsa_blinded_planchet.
+                                            blinded_msg_size)));
     {
       struct TALER_CoinSpendSignatureP link_sig;
 
       TALER_wallet_link_sign (&denom_hash,
                               &transfer_pub,
-                              pd.coin_ev,
-                              pd.coin_ev_size,
+                              pd.blinded_planchet.details.rsa_blinded_planchet.
+                              blinded_msg,
+                              pd.blinded_planchet.details.rsa_blinded_planchet.
+                              blinded_msg_size,
                               &md->melted_coin.coin_priv,
                               &link_sig);
       GNUNET_assert (0 ==
@@ -386,7 +401,7 @@ TALER_EXCHANGE_refreshes_reveal (
                        link_sigs,
                        GNUNET_JSON_from_data_auto (&link_sig)));
     }
-    GNUNET_free (pd.coin_ev);
+    TALER_blinded_planchet_free (&pd.blinded_planchet);
   }
 
   /* build array of transfer private keys */
diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c
index 5e823ee6..a5a88676 100644
--- a/src/lib/exchange_api_withdraw.c
+++ b/src/lib/exchange_api_withdraw.c
@@ -58,11 +58,26 @@ struct TALER_EXCHANGE_WithdrawHandle
    */
   void *cb_cls;
 
+  /**
+   * Reserve private key.
+   */
+  const struct TALER_ReservePrivateKeyP *reserve_priv;
+
   /**
    * Secrets of the planchet.
    */
   struct TALER_PlanchetSecretsP ps;
 
+  /**
+   * Details of the planchet.
+   */
+  struct TALER_PlanchetDetail pd;
+
+  /**
+   * Values of the @cipher selected
+   */
+  struct TALER_ExchangeWithdrawValues alg_values;
+
   /**
    * Denomination key we are withdrawing.
    */
@@ -73,6 +88,11 @@ struct TALER_EXCHANGE_WithdrawHandle
    */
   struct TALER_CoinPubHash c_hash;
 
+  /**
+   * Handler for the CS R request (only used for TALER_DENOMINATION_CS 
denominations)
+   */
+  struct TALER_EXCHANGE_CsRHandle *csrh;
+
 };
 
 
@@ -107,6 +127,7 @@ handle_reserve_withdraw_finished (
                                   blind_sig,
                                   &wh->ps,
                                   &wh->c_hash,
+                                  &wh->alg_values,
                                   &fc))
       {
         wr.hr.http_status = 0;
@@ -147,6 +168,58 @@ handle_reserve_withdraw_finished (
 }
 
 
+/**
+ * Function called when stage 1 of CS withdraw is finished (request r_pub's)
+ *
+ * @param cls
+ */
+static void
+withdraw_cs_stage_two_callback (void *cls,
+                                const struct TALER_EXCHANGE_CsRResponse *csrr)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
+
+  wh->csrh = NULL;
+
+  GNUNET_assert (TALER_DENOMINATION_CS == wh->pk.key.cipher);
+
+  switch (csrr->hr.http_status)
+  {
+  case MHD_HTTP_OK:
+    wh->alg_values.details.cs_values.r_pub = csrr->details.success.r_pubs;
+    TALER_planchet_blinding_secret_create (&wh->ps,
+                                           &wh->alg_values);
+    if (GNUNET_OK !=
+        TALER_planchet_prepare (&wh->pk.key,
+                                &wh->alg_values,
+                                &wh->ps,
+                                &wh->c_hash,
+                                &wh->pd))
+    {
+      GNUNET_break (0);
+      GNUNET_free (wh);
+    }
+    wh->wh2 = TALER_EXCHANGE_withdraw2 (wh->exchange,
+                                        &wh->pd,
+                                        wh->reserve_priv,
+                                        &handle_reserve_withdraw_finished,
+                                        wh);
+    break;
+  default:
+    {
+      // the CSR request went wrong -> serve response to the callback
+      struct TALER_EXCHANGE_WithdrawResponse wr = {
+        .hr = csrr->hr
+      };
+      wh->cb (wh->cb_cls,
+              &wr);
+      TALER_EXCHANGE_withdraw_cancel (wh);
+      break;
+    }
+  }
+}
+
+
 /**
  * Withdraw a coin from the exchange using a /reserve/withdraw request.  Note
  * that to ensure that no money is lost in case of hardware failures,
@@ -170,37 +243,70 @@ 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,
+  struct TALER_PlanchetSecretsP *ps,
+  struct TALER_ExchangeWithdrawValues *alg_values,
   TALER_EXCHANGE_WithdrawCallback res_cb,
   void *res_cb_cls)
 {
-  struct TALER_PlanchetDetail pd;
   struct TALER_EXCHANGE_WithdrawHandle *wh;
 
   wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
   wh->exchange = exchange;
   wh->cb = res_cb;
   wh->cb_cls = res_cb_cls;
-  wh->pk = *pk;
+  wh->reserve_priv = reserve_priv;
   wh->ps = *ps;
-  if (GNUNET_OK !=
-      TALER_planchet_prepare (&pk->key,
-                              ps,
-                              &wh->c_hash,
-                              &pd))
+  wh->alg_values = *alg_values,
+  wh->pk = *pk;
+  wh->csrh = NULL;
+
+  TALER_denom_pub_deep_copy (&wh->pk.key,
+                             &pk->key);
+  switch (pk->key.cipher)
   {
+  case TALER_DENOMINATION_RSA:
+    if (GNUNET_OK !=
+        TALER_planchet_prepare (&pk->key,
+                                &wh->alg_values,
+                                ps,
+                                &wh->c_hash,
+                                &wh->pd))
+    {
+      GNUNET_break (0);
+      GNUNET_free (wh);
+      return NULL;
+    }
+    wh->wh2 = TALER_EXCHANGE_withdraw2 (exchange,
+                                        &wh->pd,
+                                        wh->reserve_priv,
+                                        &handle_reserve_withdraw_finished,
+                                        wh);
+    break;
+  case TALER_DENOMINATION_CS:
+    wh->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
+
+    /**
+     * This part is a bit hacky..
+     * due to the reason that Withdraw tests use the same private key coin to 
sign,
+     * the same Withdraw nonce will be derived.
+     * In a normal withdrawal TALER_cs_withdraw_nonce_derive is used.
+     * As a hacky solution, we generate the nonce here randomly.
+     */
+    TALER_cs_withdraw_nonce_generate (&wh->pd.blinded_planchet.details.
+                                      cs_blinded_planchet.nonce);
+    wh->csrh = TALER_EXCHANGE_csr (exchange,
+                                   pk,
+                                   &wh->pd.blinded_planchet.details.
+                                   cs_blinded_planchet.nonce,
+                                   &withdraw_cs_stage_two_callback,
+                                   wh);
+    break;
+  default:
     GNUNET_break (0);
     GNUNET_free (wh);
     return NULL;
   }
-  TALER_denom_pub_deep_copy (&wh->pk.key,
-                             &pk->key);
-  wh->wh2 = TALER_EXCHANGE_withdraw2 (exchange,
-                                      &pd,
-                                      reserve_priv,
-                                      &handle_reserve_withdraw_finished,
-                                      wh);
-  GNUNET_free (pd.coin_ev);
+  TALER_blinded_planchet_free (&wh->pd.blinded_planchet);
   return wh;
 }
 
@@ -208,6 +314,11 @@ TALER_EXCHANGE_withdraw (
 void
 TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
 {
+  if (NULL != wh->csrh)
+  {
+    TALER_EXCHANGE_csr_cancel (wh->csrh);
+    wh->csrh = NULL;
+  }
   if (NULL != wh->wh2)
   {
     TALER_EXCHANGE_withdraw2_cancel (wh->wh2);
diff --git a/src/lib/exchange_api_withdraw2.c b/src/lib/exchange_api_withdraw2.c
index d50892e5..c5a3a66a 100644
--- a/src/lib/exchange_api_withdraw2.c
+++ b/src/lib/exchange_api_withdraw2.c
@@ -437,23 +437,25 @@ TALER_EXCHANGE_withdraw2 (
 
     TALER_amount_hton (&req.amount_with_fee,
                        &wh->requested_amount);
-    TALER_coin_ev_hash (pd->coin_ev,
-                        pd->coin_ev_size,
-                        &req.h_coin_envelope);
+    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);
   }
 
   {
-    json_t *withdraw_obj;
-
-    withdraw_obj = GNUNET_JSON_PACK (
+    json_t *withdraw_obj = GNUNET_JSON_PACK (
       GNUNET_JSON_pack_data_auto ("denom_pub_hash",
                                   &pd->denom_pub_hash),
-      GNUNET_JSON_pack_data_varsize ("coin_ev",
-                                     pd->coin_ev,
-                                     pd->coin_ev_size),
+      TALER_JSON_pack_blinded_planchet ("coin_ev",
+                                        &pd->blinded_planchet),
       GNUNET_JSON_pack_data_auto ("reserve_sig",
                                   &reserve_sig));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c
index 37d7bf5b..ca1e94ef 100644
--- a/src/pq/pq_query_helper.c
+++ b/src/pq/pq_query_helper.c
@@ -194,7 +194,9 @@ qconv_denom_pub (void *cls,
       denom_pub->details.rsa_public_key,
       &tbuf);
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    tlen = sizeof (denom_pub->details.cs_public_key);
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -211,7 +213,11 @@ qconv_denom_pub (void *cls,
             tlen);
     GNUNET_free (tbuf);
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    memcpy (&buf[sizeof (be)],
+            &denom_pub->details.cs_public_key,
+            tlen);
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -284,7 +290,9 @@ qconv_denom_sig (void *cls,
       denom_sig->details.rsa_signature,
       &tbuf);
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    tlen = sizeof (denom_sig->details.cs_signature);
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -301,7 +309,11 @@ qconv_denom_sig (void *cls,
             tlen);
     GNUNET_free (tbuf);
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    memcpy (&buf[sizeof (be)],
+            &denom_sig->details.cs_signature,
+            tlen);
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -374,7 +386,9 @@ qconv_blinded_denom_sig (void *cls,
       denom_sig->details.blinded_rsa_signature,
       &tbuf);
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    tlen = sizeof (denom_sig->details.blinded_cs_answer);
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -391,7 +405,11 @@ qconv_blinded_denom_sig (void *cls,
             tlen);
     GNUNET_free (tbuf);
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    memcpy (&buf[sizeof (be)],
+            &denom_sig->details.blinded_cs_answer,
+            tlen);
+    break;
   default:
     GNUNET_assert (0);
   }
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c
index a6bc9409..02733f29 100644
--- a/src/pq/pq_result_helper.c
+++ b/src/pq/pq_result_helper.c
@@ -425,7 +425,16 @@ extract_denom_pub (void *cls,
       return GNUNET_SYSERR;
     }
     return GNUNET_OK;
-  // FIXME: add CS case!
+  case TALER_DENOMINATION_CS:
+    if (sizeof (pk->details.cs_public_key) != len)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    memcpy (&pk->details.cs_public_key,
+            res,
+            len);
+    return GNUNET_OK;
   default:
     GNUNET_break (0);
   }
@@ -543,7 +552,16 @@ extract_denom_sig (void *cls,
       return GNUNET_SYSERR;
     }
     return GNUNET_OK;
-  // FIXME: add CS case!
+  case TALER_DENOMINATION_CS:
+    if (sizeof (sig->details.cs_signature) != len)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    memcpy (&sig->details.cs_signature,
+            res,
+            len);
+    return GNUNET_OK;
   default:
     GNUNET_break (0);
   }
@@ -661,7 +679,16 @@ extract_blinded_denom_sig (void *cls,
       return GNUNET_SYSERR;
     }
     return GNUNET_OK;
-  // FIXME: add CS case!
+  case TALER_DENOMINATION_CS:
+    if (sizeof (sig->details.blinded_cs_answer) != len)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    memcpy (&sig->details.blinded_cs_answer,
+            res,
+            len);
+    return GNUNET_OK;
   default:
     GNUNET_break (0);
   }
diff --git a/src/testing/.gitignore b/src/testing/.gitignore
index 8c19b94c..61e3a4c0 100644
--- a/src/testing/.gitignore
+++ b/src/testing/.gitignore
@@ -24,12 +24,17 @@ 
test_taler_exchange_httpd_home/.local/share/taler/taler-exchange-secmod-eddsa/
 test_taler_exchange_httpd_home/.local/share/taler/taler-exchange-secmod-rsa/
 test_exchange_api_keys_cherry_picking_home/.local/share/taler/crypto-rsa/
 test_exchange_api_home/.local/share/taler/exchange-offline/secm_tofus.pub
+test_exchange_api_home/.local/share/taler/exchange-secmod-cs/
 test_exchange_api_home/.local/share/taler/exchange-secmod-eddsa/
 test_exchange_api_home/.local/share/taler/exchange-secmod-rsa/
 
test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-offline/secm_tofus.pub
+test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-secmod-cs/
 
test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-secmod-eddsa/
 
test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-secmod-rsa/
 
test_taler_exchange_httpd_home/.local/share/taler/exchange-offline/secm_tofus.pub
+test_taler_exchange_httpd_home/.local/share/taler/exchange-secmod-cs/
 test_taler_exchange_httpd_home/.local/share/taler/exchange-secmod-eddsa/
 test_taler_exchange_httpd_home/.local/share/taler/exchange-secmod-rsa/
 test_kyc_api
+test_helper_cs_home/
+test_helper_rsa_home/
\ No newline at end of file
diff --git a/src/testing/test-taler-exchange-aggregator-postgres.conf 
b/src/testing/test-taler-exchange-aggregator-postgres.conf
index 7f277629..965f05b0 100644
--- a/src/testing/test-taler-exchange-aggregator-postgres.conf
+++ b/src/testing/test-taler-exchange-aggregator-postgres.conf
@@ -92,4 +92,16 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
\ No newline at end of file
diff --git a/src/testing/test-taler-exchange-wirewatch-postgres.conf 
b/src/testing/test-taler-exchange-wirewatch-postgres.conf
index d42f9d44..60d973c1 100644
--- a/src/testing/test-taler-exchange-wirewatch-postgres.conf
+++ b/src/testing/test-taler-exchange-wirewatch-postgres.conf
@@ -81,4 +81,16 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_ct_11]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
\ No newline at end of file
diff --git a/src/testing/test_auditor_api.conf 
b/src/testing/test_auditor_api.conf
index 03a5e245..8e3cd28d 100644
--- a/src/testing/test_auditor_api.conf
+++ b/src/testing/test_auditor_api.conf
@@ -10,6 +10,10 @@ TALER_RUNTIME_DIR = 
${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/
 # Reduce from 1 year to speed up test
 LOOKAHEAD_SIGN = 24 days
 
+[taler-exchange-secmod-cs]
+# Reduce from 1 year to speed up test
+LOOKAHEAD_SIGN = 24 days
+
 [taler-exchange-secmod-eddsa]
 # Reduce from 1 year to speed up test
 LOOKAHEAD_SIGN = 24 days
@@ -93,8 +97,20 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_ct_10]
 value = EUR:0.10
 duration_withdraw = 7 days
@@ -104,8 +120,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_1]
 value = EUR:1
 duration_withdraw = 7 days
@@ -115,8 +143,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_5]
 value = EUR:5
 duration_withdraw = 7 days
@@ -126,8 +166,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_6]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_10]
 value = EUR:10
 duration_withdraw = 7 days
@@ -137,4 +189,16 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_11]
+value = EUR:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
\ No newline at end of file
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index 59c2cb06..ac5dfdc0 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -406,7 +406,6 @@ run (void *cls,
     TALER_TESTING_cmd_end ()
   };
 
-
   /**
    * This block checks whether a wire deadline
    * very far in the future does NOT get aggregated now.
@@ -892,6 +891,145 @@ run (void *cls,
     TALER_TESTING_cmd_end ()
   };
 
+  /**
+   * Test CS withdrawal plus spending.
+   */
+  struct TALER_TESTING_Command withdraw_cs[] = {
+    /**
+     * Move money to the exchange's bank account.
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-cs-1",
+                              "EUR:6.02"),
+    TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-cs-1",
+                                                 "EUR:6.02",
+                                                 bc.user42_payto,
+                                                 bc.exchange_payto,
+                                                 "create-reserve-cs-1"),
+    /**
+     * Make a reserve exist, according to the previous
+     * transfer.
+     */
+    CMD_EXEC_WIREWATCH ("wirewatch-cs-1"),
+    /**
+     * Withdraw EUR:5.
+     */
+    TALER_TESTING_cmd_withdraw_cs_amount ("withdraw-cs-coin-1",
+                                          "create-reserve-cs-1",
+                                          "EUR:5",
+                                          MHD_HTTP_OK),
+    /**
+     * Withdraw EUR:1 using the SAME private coin key as for the previous coin
+     * (in violation of the specification, to be detected on spending!).
+     */
+    TALER_TESTING_cmd_withdraw_cs_amount_reuse_key ("withdraw-cs-coin-1x",
+                                                    "create-reserve-cs-1",
+                                                    "EUR:1",
+                                                    "withdraw-cs-coin-1",
+                                                    MHD_HTTP_OK),
+    /**
+     * Check the reserve is depleted.
+     */
+    TALER_TESTING_cmd_status ("status-cs-1",
+                              "create-reserve-cs-1",
+                              "EUR:0",
+                              MHD_HTTP_OK),
+    /*
+     * Try to overdraw.
+     */
+    TALER_TESTING_cmd_withdraw_cs_amount ("withdraw-cs-coin-2",
+                                          "create-reserve-cs-1",
+                                          "EUR:5",
+                                          MHD_HTTP_CONFLICT),
+    // TODO: add test for nonce reuse
+    TALER_TESTING_cmd_end ()
+  };
+
+  struct TALER_TESTING_Command spend_cs[] = {
+    /**
+     * Spend the coin.
+     */
+    TALER_TESTING_cmd_deposit ("deposit-cs-simple",
+                               "withdraw-cs-coin-1",
+                               0,
+                               bc.user42_payto,
+                               "{\"items\":[{\"name\":\"ice 
cream\",\"value\":1}]}",
+                               GNUNET_TIME_UNIT_ZERO,
+                               "EUR:5",
+                               MHD_HTTP_OK),
+    TALER_TESTING_cmd_deposit_replay ("deposit-cs-simple-replay",
+                                      "deposit-cs-simple",
+                                      MHD_HTTP_OK),
+    TALER_TESTING_cmd_deposit ("deposit-cs-reused-coin-key-failure",
+                               "withdraw-cs-coin-1x",
+                               0,
+                               bc.user42_payto,
+                               "{\"items\":[{\"name\":\"ice 
cream\",\"value\":1}]}",
+                               GNUNET_TIME_UNIT_ZERO,
+                               "EUR:1",
+                               MHD_HTTP_CONFLICT),
+    /**
+     * Try to double spend using different wire details.
+     */
+    TALER_TESTING_cmd_deposit ("deposit-cs-double-1",
+                               "withdraw-cs-coin-1",
+                               0,
+                               bc.user43_payto,
+                               "{\"items\":[{\"name\":\"ice 
cream\",\"value\":1}]}",
+                               GNUNET_TIME_UNIT_ZERO,
+                               "EUR:5",
+                               MHD_HTTP_CONFLICT),
+    /* Try to double spend using a different transaction id.
+     * The test needs the contract terms to differ. This
+     * is currently the case because of the "timestamp" field,
+     * which is set automatically by #TALER_TESTING_cmd_deposit().
+     * This could theoretically fail if at some point a deposit
+     * command executes in less than 1 ms. *///
+    TALER_TESTING_cmd_deposit ("deposit-cs-double-1",
+                               "withdraw-cs-coin-1",
+                               0,
+                               bc.user43_payto,
+                               "{\"items\":[{\"name\":\"ice 
cream\",\"value\":1}]}",
+                               GNUNET_TIME_UNIT_ZERO,
+                               "EUR:5",
+                               MHD_HTTP_CONFLICT),
+    /**
+     * Try to double spend with different proposal.
+     */
+    TALER_TESTING_cmd_deposit ("deposit-cs-double-2",
+                               "withdraw-cs-coin-1",
+                               0,
+                               bc.user43_payto,
+                               "{\"items\":[{\"name\":\"ice 
cream\",\"value\":2}]}",
+                               GNUNET_TIME_UNIT_ZERO,
+                               "EUR:5",
+                               MHD_HTTP_CONFLICT),
+    TALER_TESTING_cmd_end ()
+  };
+
+  // TODO: CS refresh
+
+  struct TALER_TESTING_Command track_cs[] = {
+    /* Try resolving a deposit's WTID, as we never triggered
+     * execution of transactions, the answer should be that
+     * the exchange knows about the deposit, but has no WTID yet.
+     *///
+    TALER_TESTING_cmd_track_transaction ("deposit-cs-wtid-found",
+                                         "deposit-cs-simple",
+                                         0,
+                                         MHD_HTTP_ACCEPTED,
+                                         NULL),
+    /* Try resolving a deposit's WTID for a failed deposit.
+     * As the deposit failed, the answer should be that the
+     * exchange does NOT know about the deposit.
+     */
+    TALER_TESTING_cmd_track_transaction ("deposit-cs-wtid-failing",
+                                         "deposit-cs-double-2",
+                                         0,
+                                         MHD_HTTP_NOT_FOUND,
+                                         NULL),
+    TALER_TESTING_cmd_end ()
+  };
+
 #define RESERVE_OPEN_CLOSE_CHUNK 4
 #define RESERVE_OPEN_CLOSE_ITERATIONS 3
 
@@ -961,6 +1099,13 @@ run (void *cls,
                                refund),
       TALER_TESTING_cmd_batch ("recoup",
                                recoup),
+      TALER_TESTING_cmd_batch ("withdraw-cs",
+                               withdraw_cs),
+      TALER_TESTING_cmd_batch ("spend-cs",
+                               spend_cs),
+      // TODO: Clause Schnorr refresh
+      TALER_TESTING_cmd_batch ("track-cs",
+                               track_cs),
       TALER_TESTING_cmd_batch ("reserve-open-close",
                                reserve_open_close),
       /* End the suite. */
diff --git a/src/testing/test_exchange_api.conf 
b/src/testing/test_exchange_api.conf
index 48d5c200..4f9f24f3 100644
--- a/src/testing/test_exchange_api.conf
+++ b/src/testing/test_exchange_api.conf
@@ -94,8 +94,20 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_ct_10]
 value = EUR:0.10
 duration_withdraw = 7 days
@@ -105,8 +117,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_1]
 value = EUR:1
 duration_withdraw = 7 days
@@ -116,8 +140,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_5]
 value = EUR:5
 duration_withdraw = 7 days
@@ -127,8 +163,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_6]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_10]
 value = EUR:10
 duration_withdraw = 7 days
@@ -138,6 +186,7 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
 [coin_eur_ct_1_age_restricted]
@@ -151,6 +200,7 @@ fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
 rsa_keysize = 1024
 age_restricted = true
+CIPHER = RSA
 
 [coin_eur_ct_10_age_restricted]
 value = EUR:0.10
@@ -163,6 +213,7 @@ fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
 rsa_keysize = 1024
 age_restricted = true
+CIPHER = RSA
 
 [coin_eur_1_age_restricted]
 value = EUR:1
@@ -175,6 +226,7 @@ fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
 rsa_keysize = 1024
 age_restricted = true
+CIPHER = RSA
 
 [coin_eur_5_age_restricted]
 value = EUR:5
@@ -187,6 +239,8 @@ fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
 rsa_keysize = 1024
 age_restricted = true
+CIPHER = RSA
+
 
 [coin_eur_10_age_restricted]
 value = EUR:10
@@ -199,3 +253,4 @@ fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
 rsa_keysize = 1024
 age_restricted = true
+CIPHER = RSA
diff --git a/src/testing/test_exchange_api_keys_cherry_picking.conf 
b/src/testing/test_exchange_api_keys_cherry_picking.conf
index d7dd9535..f4edaf42 100644
--- a/src/testing/test_exchange_api_keys_cherry_picking.conf
+++ b/src/testing/test_exchange_api_keys_cherry_picking.conf
@@ -22,6 +22,10 @@ CURRENCY = EUR
 # Reduce from 1 year to speed up test
 LOOKAHEAD_SIGN = 24 days
 
+[taler-exchange-secmod-cs]
+# Reduce from 1 year to speed up test
+LOOKAHEAD_SIGN = 24 days
+
 [taler-exchange-secmod-eddsa]
 # Reduce from 1 year to speed up test
 LOOKAHEAD_SIGN = 24 days
@@ -81,6 +85,10 @@ HTTP_PORT=8082
 OVERLAP_DURATION = 1 s
 LOOKAHEAD_SIGN = 20 s
 
+[taler-exchange-secmod-cs]
+OVERLAP_DURATION = 1 s
+LOOKAHEAD_SIGN = 20 s
+
 [taler-exchange-secmod-eddsa]
 OVERLAP_DURATION = 1 s
 DURATION = 30 s
@@ -95,4 +103,16 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 5 s
+duration_spend = 6 s
+duration_legal = 7 s
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf
index 8ca6b74b..2dce408b 100644
--- a/src/testing/test_kyc_api.conf
+++ b/src/testing/test_kyc_api.conf
@@ -110,8 +110,20 @@ fee_withdraw = EUR:0.00
 fee_deposit = EUR:0.00
 fee_refresh = EUR:0.01
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_ct_2]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_ct_10]
 value = EUR:0.10
 duration_withdraw = 7 days
@@ -121,8 +133,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_ct_11]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_1]
 value = EUR:1
 duration_withdraw = 7 days
@@ -132,8 +156,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_2]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_5]
 value = EUR:5
 duration_withdraw = 7 days
@@ -143,8 +179,20 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
 
+[coin_eur_6]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
+
 [coin_eur_10]
 value = EUR:10
 duration_withdraw = 7 days
@@ -154,4 +202,16 @@ fee_withdraw = EUR:0.01
 fee_deposit = EUR:0.01
 fee_refresh = EUR:0.03
 fee_refund = EUR:0.01
+CIPHER = RSA
 rsa_keysize = 1024
+
+[coin_eur_11]
+value = EUR:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = CS
\ No newline at end of file
diff --git a/src/testing/testing_api_cmd_insert_deposit.c 
b/src/testing/testing_api_cmd_insert_deposit.c
index d45bd0c6..013c2293 100644
--- a/src/testing/testing_api_cmd_insert_deposit.c
+++ b/src/testing/testing_api_cmd_insert_deposit.c
@@ -201,27 +201,29 @@ insert_deposit_run (void *cls,
     struct TALER_CoinPubHash c_hash;
     struct TALER_PlanchetDetail pd;
     struct TALER_BlindedDenominationSignature bds;
-    union TALER_DenominationBlindingKeyP bks;
+    struct TALER_PlanchetSecretsP ps;
+    struct TALER_ExchangeWithdrawValues alg_values;
 
-    TALER_blinding_secret_create (&bks);
+    alg_values.cipher = TALER_DENOMINATION_RSA;
+    TALER_planchet_blinding_secret_create (&ps,
+                                           &alg_values);
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_blind (&dpk,
-                                      &bks,
+                                      &ps.blinding_key,
                                       NULL, /* FIXME-Oec */
                                       &deposit.coin.coin_pub,
+                                      &alg_values,
                                       &c_hash,
-                                      &pd.coin_ev,
-                                      &pd.coin_ev_size));
+                                      &pd.blinded_planchet));
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_sign_blinded (&bds,
                                              &denom_priv,
-                                             pd.coin_ev,
-                                             pd.coin_ev_size));
-    GNUNET_free (pd.coin_ev);
+                                             &pd.blinded_planchet));
+    TALER_blinded_planchet_free (&pd.blinded_planchet);
     GNUNET_assert (GNUNET_OK ==
                    TALER_denom_sig_unblind (&deposit.coin.denom_sig,
                                             &bds,
-                                            &bks,
+                                            &ps.blinding_key,
                                             &dpk));
     TALER_blinded_denom_sig_free (&bds);
   }
diff --git a/src/testing/testing_api_cmd_refresh.c 
b/src/testing/testing_api_cmd_refresh.c
index d2c2c714..0b47f508 100644
--- a/src/testing/testing_api_cmd_refresh.c
+++ b/src/testing/testing_api_cmd_refresh.c
@@ -1048,8 +1048,10 @@ melt_run (void *cls,
         TALER_TESTING_interpreter_fail (rms->is);
         return;
       }
-      fresh_pk = TALER_TESTING_find_pk
-                   (TALER_EXCHANGE_get_keys (is->exchange), &fresh_amount);
+      fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
+                                        &fresh_amount,
+                                        // FIXME: replace hardcoded value
+                                        TALER_DENOMINATION_RSA);
       if (NULL == fresh_pk)
       {
         GNUNET_break (0);
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index 8e6cba70..da514ddf 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -72,6 +72,11 @@ struct WithdrawState
    */
   struct TALER_Amount amount;
 
+  /**
+   * Type of denomination that we should withdraw
+   */
+  enum TALER_DenominationCipher cipher;
+
   /**
    * If @e amount is NULL, this specifies the denomination key to
    * use.  Otherwise, this will be set (by the interpreter) to the
@@ -115,6 +120,11 @@ struct WithdrawState
    */
   struct TALER_PlanchetSecretsP ps;
 
+  /**
+   * Withdraw Values used for planchet creation
+   */
+  struct TALER_ExchangeWithdrawValues alg_values;
+
   /**
    * Reserve history entry that corresponds to this operation.
    * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
@@ -386,9 +396,10 @@ withdraw_run (void *cls,
   ws->reserve_payto_uri
     = TALER_payto_from_reserve (ws->exchange_url,
                                 &ws->reserve_pub);
+  ws->alg_values.cipher = ws->cipher;
   if (NULL == ws->reuse_coin_key_ref)
   {
-    TALER_planchet_setup_random (&ws->ps);
+    TALER_planchet_setup_random (&ws->ps, &ws->alg_values);
   }
   else
   {
@@ -409,13 +420,14 @@ withdraw_run (void *cls,
                    TALER_TESTING_get_trait_coin_priv (cref,
                                                       index,
                                                       &coin_priv));
-    TALER_planchet_setup_random (&ws->ps);
+    TALER_planchet_setup_random (&ws->ps, &ws->alg_values);
     ws->ps.coin_priv = *coin_priv;
   }
   if (NULL == ws->pk)
   {
     dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
-                                 &ws->amount);
+                                 &ws->amount,
+                                 ws->cipher);
     if (NULL == dpk)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -443,6 +455,7 @@ withdraw_run (void *cls,
                                      ws->pk,
                                      rp,
                                      &ws->ps,
+                                     &ws->alg_values,
                                      &reserve_withdraw_cb,
                                      ws);
   if (NULL == ws->wsh)
@@ -557,6 +570,8 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
                                    const char *amount,
                                    unsigned int expected_response_code)
 {
+  // TODO: ATM this is hardcoded to RSA denominations
+  // (use TALER_TESTING_cmd_withdraw_cs_amount for Clause Schnorr)
   struct WithdrawState *ws;
 
   ws = GNUNET_new (struct WithdrawState);
@@ -572,6 +587,43 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
     GNUNET_assert (0);
   }
   ws->expected_response_code = expected_response_code;
+  ws->cipher = TALER_DENOMINATION_RSA;
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = ws,
+      .label = label,
+      .run = &withdraw_run,
+      .cleanup = &withdraw_cleanup,
+      .traits = &withdraw_traits
+    };
+
+    return cmd;
+  }
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_cs_amount (const char *label,
+                                      const char *reserve_reference,
+                                      const char *amount,
+                                      unsigned int expected_response_code)
+{
+  struct WithdrawState *ws;
+
+  ws = GNUNET_new (struct WithdrawState);
+  ws->reserve_reference = reserve_reference;
+  if (GNUNET_OK !=
+      TALER_string_to_amount (amount,
+                              &ws->amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %s\n",
+                amount,
+                label);
+    GNUNET_assert (0);
+  }
+  ws->expected_response_code = expected_response_code;
+  ws->cipher = TALER_DENOMINATION_CS;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = ws,
@@ -609,6 +661,8 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key (
   const char *coin_ref,
   unsigned int expected_response_code)
 {
+  // TODO: ATM this is hardcoded to RSA denominations
+  // (use TALER_TESTING_cmd_withdraw_cs_amount for Clause Schnorr)
   struct TALER_TESTING_Command cmd;
 
   cmd = TALER_TESTING_cmd_withdraw_amount (label,
@@ -624,6 +678,29 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key (
 }
 
 
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_cs_amount_reuse_key (
+  const char *label,
+  const char *reserve_reference,
+  const char *amount,
+  const char *coin_ref,
+  unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+
+  cmd = TALER_TESTING_cmd_withdraw_cs_amount (label,
+                                              reserve_reference,
+                                              amount,
+                                              expected_response_code);
+  {
+    struct WithdrawState *ws = cmd.cls;
+
+    ws->reuse_coin_key_ref = coin_ref;
+  }
+  return cmd;
+}
+
+
 /**
  * Create withdraw command, letting the caller specify the
  * amount by a denomination key.
@@ -656,6 +733,7 @@ TALER_TESTING_cmd_withdraw_denomination (
   ws->reserve_reference = reserve_reference;
   ws->pk = TALER_EXCHANGE_copy_denomination_key (dk);
   ws->expected_response_code = expected_response_code;
+  ws->cipher = dk->key.cipher;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = ws,
diff --git a/src/testing/testing_api_helpers_exchange.c 
b/src/testing/testing_api_helpers_exchange.c
index fe758810..a30db033 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -416,11 +416,13 @@ TALER_TESTING_prepare_exchange (const char 
*config_filename,
  *
  * @param keys array of keys to search
  * @param amount coin value to look for
+ * @param cipher denomination cipher
  * @return NULL if no matching key was found
  */
 const struct TALER_EXCHANGE_DenomPublicKey *
 TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
-                       const struct TALER_Amount *amount)
+                       const struct TALER_Amount *amount,
+                       const enum TALER_DenominationCipher cipher)
 {
   struct GNUNET_TIME_Timestamp now;
   struct TALER_EXCHANGE_DenomPublicKey *pk;
@@ -430,6 +432,8 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys 
*keys,
   for (unsigned int i = 0; i<keys->num_denom_keys; i++)
   {
     pk = &keys->denom_keys[i];
+    if (cipher != pk->key.cipher)
+      continue;
     if ( (0 == TALER_amount_cmp (amount,
                                  &pk->value)) &&
          (GNUNET_TIME_timestamp_cmp (now,
@@ -446,6 +450,8 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys 
*keys,
   for (unsigned int i = 0; i<keys->num_denom_keys; i++)
   {
     pk = &keys->denom_keys[i];
+    if (cipher != pk->key.cipher)
+      continue;
     if ( (0 == TALER_amount_cmp (amount,
                                  &pk->value)) &&
          (GNUNET_TIME_timestamp_cmp (now,
@@ -467,6 +473,25 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys 
*keys,
       return NULL;
     }
   }
+  // do 3rd pass to check if cipher type is to blame for failure
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+  {
+    pk = &keys->denom_keys[i];
+    if ( (0 == TALER_amount_cmp (amount,
+                                 &pk->value)) &&
+         (cipher != pk->key.cipher) )
+    {
+      GNUNET_log
+        (GNUNET_ERROR_TYPE_WARNING,
+        "Have denomination key for `%s', but with wrong"
+        " cipher type %d vs %d\n",
+        str,
+        cipher,
+        pk->key.cipher);
+      GNUNET_free (str);
+      return NULL;
+    }
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
               "No denomination key for amount %s found\n",
               str);
@@ -608,9 +633,9 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main 
main_cb,
  * @param[in] helpers the process handles.
  */
 static void
-stop_helpers (struct GNUNET_OS_Process *helpers[2])
+stop_helpers (struct GNUNET_OS_Process *helpers[3])
 {
-  for (unsigned int i = 0; i<2; i++)
+  for (unsigned int i = 0; i<3; i++)
   {
     if (NULL == helpers[i])
       continue;
@@ -632,7 +657,7 @@ stop_helpers (struct GNUNET_OS_Process *helpers[2])
  */
 static enum GNUNET_GenericReturnValue
 start_helpers (const char *config_filename,
-               struct GNUNET_OS_Process *helpers[2])
+               struct GNUNET_OS_Process *helpers[3])
 {
   char *dir;
   const struct GNUNET_OS_ProjectData *pd;
@@ -678,9 +703,26 @@ start_helpers (const char *config_filename,
                                           NULL);
     GNUNET_free (fn);
   }
+  {
+    char *fn;
+
+    GNUNET_asprintf (&fn,
+                     "%s/%s",
+                     dir,
+                     "taler-exchange-secmod-cs");
+    helpers[2] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
+                                          NULL, NULL, NULL,
+                                          fn,
+                                          "taler-exchange-secmod-cs",
+                                          "-c", config_filename,
+                                          "-L", "INFO",
+                                          NULL);
+    GNUNET_free (fn);
+  }
   GNUNET_free (dir);
   if ( (NULL == helpers[0]) ||
-       (NULL == helpers[1]) )
+       (NULL == helpers[1]) ||
+       (NULL == helpers[2]) )
   {
     stop_helpers (helpers);
     return GNUNET_SYSERR;
@@ -696,7 +738,7 @@ TALER_TESTING_setup_with_exchange_cfg (
 {
   const struct TALER_TESTING_SetupContext *setup_ctx = cls;
   struct GNUNET_OS_Process *exchanged;
-  struct GNUNET_OS_Process *helpers[2];
+  struct GNUNET_OS_Process *helpers[3];
   unsigned long long port;
   char *serve;
   char *base_url;
diff --git a/src/util/.gitignore b/src/util/.gitignore
index f25567f3..c5f8c76d 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -1,8 +1,11 @@
 taler-config
 test_payto
 taler-exchange-secmod-rsa
+taler-exchange-secmod-cs
 taler-exchange-secmod-eddsa
 test_helper_rsa
 test_helper_rsa_home/
+test_helper_cs
+test_helper_cs_home/
 test_helper_eddsa
 test_helper_eddsa_home/
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 35e58034..997b49f2 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -12,17 +12,20 @@ pkgcfgdir = $(prefix)/share/taler/config.d/
 pkgcfg_DATA = \
   paths.conf \
   taler-exchange-secmod-eddsa.conf \
-  taler-exchange-secmod-rsa.conf
+  taler-exchange-secmod-rsa.conf \
+  taler-exchange-secmod-cs.conf
 
 EXTRA_DIST = \
   $(pkgcfg_DATA) \
   taler-config.in \
   test_helper_eddsa.conf \
-  test_helper_rsa.conf
+  test_helper_rsa.conf \
+  test_helper_cs.conf
 
 bin_PROGRAMS = \
   taler-exchange-secmod-eddsa \
-  taler-exchange-secmod-rsa
+  taler-exchange-secmod-rsa \
+  taler-exchange-secmod-cs
 
 bin_SCRIPTS = \
   taler-config
@@ -48,6 +51,16 @@ taler_exchange_secmod_rsa_LDADD = \
   $(LIBGCRYPT_LIBS) \
   $(XLIB)
 
+taler_exchange_secmod_cs_SOURCES = \
+  taler-exchange-secmod-cs.c taler-exchange-secmod-cs.h \
+  secmod_common.c secmod_common.h
+taler_exchange_secmod_cs_LDADD = \
+  libtalerutil.la \
+  -lgnunetutil \
+  -lpthread \
+  $(LIBGCRYPT_LIBS) \
+  $(XLIB)
+
 taler_exchange_secmod_eddsa_SOURCES = \
   taler-exchange-secmod-eddsa.c taler-exchange-secmod-eddsa.h \
   secmod_common.c secmod_common.h
@@ -68,6 +81,7 @@ libtalerutil_la_SOURCES = \
   crypto.c \
   crypto_helper_common.c crypto_helper_common.h \
   crypto_helper_rsa.c \
+  crypto_helper_cs.c \
   crypto_helper_esign.c \
   crypto_wire.c \
   denom.c \
@@ -105,6 +119,7 @@ check_PROGRAMS = \
  test_crypto \
  test_helper_eddsa \
  test_helper_rsa \
+ test_helper_cs \
  test_payto \
  test_url
 
@@ -142,6 +157,12 @@ test_helper_rsa_LDADD = \
   -lgnunetutil \
   libtalerutil.la
 
+test_helper_cs_SOURCES = \
+  test_helper_cs.c
+test_helper_cs_LDADD = \
+  -lgnunetutil \
+  libtalerutil.la
+
 test_url_SOURCES = \
   test_url.c
 test_url_LDADD = \
diff --git a/src/util/crypto.c b/src/util/crypto.c
index 178db3aa..fee3f31e 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -167,61 +167,254 @@ TALER_planchet_setup_refresh (const struct 
TALER_TransferSecretP *secret_seed,
 
 
 void
-TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps)
+cs_blinding_seed_derive (const struct
+                         TALER_CoinSpendPrivateKeyP *coin_priv,
+                         const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
+                         struct GNUNET_CRYPTO_CsNonce *blind_seed)
+{
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CRYPTO_hkdf (blind_seed,
+                                     sizeof (*blind_seed),
+                                     GCRY_MD_SHA512,
+                                     GCRY_MD_SHA256,
+                                     "bseed",
+                                     strlen ("bseed"),
+                                     coin_priv,
+                                     sizeof(*coin_priv),
+                                     r_pub,
+                                     sizeof(struct GNUNET_CRYPTO_CsRPublic) * 
2,
+                                     NULL,
+                                     0));
+}
+
+
+void
+TALER_cs_withdraw_nonce_derive (const struct
+                                TALER_CoinSpendPrivateKeyP *coin_priv,
+                                struct TALER_CsNonce *nonce)
+{
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CRYPTO_hkdf (nonce,
+                                     sizeof (*nonce),
+                                     GCRY_MD_SHA512,
+                                     GCRY_MD_SHA256,
+                                     "n",
+                                     strlen ("n"),
+                                     coin_priv,
+                                     sizeof(*coin_priv),
+                                     NULL,
+                                     0));
+}
+
+
+void
+TALER_cs_withdraw_nonce_generate (struct TALER_CsNonce *nonce)
 {
   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
-                              ps,
-                              sizeof (*ps));
+                              nonce,
+                              sizeof (*nonce));
+}
+
+
+void
+TALER_planchet_blinding_secret_create (struct TALER_PlanchetSecretsP *ps,
+                                       const struct
+                                       TALER_ExchangeWithdrawValues 
*alg_values)
+{
+  switch (alg_values->cipher)
+  {
+  case TALER_DENOMINATION_INVALID:
+    GNUNET_break (0);
+    return;
+  case TALER_DENOMINATION_RSA:
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
+                                &ps->blinding_key.rsa_bks,
+                                sizeof (struct
+                                        GNUNET_CRYPTO_RsaBlindingKeySecret));
+    return;
+  case TALER_DENOMINATION_CS:
+    {
+      cs_blinding_seed_derive (&ps->coin_priv,
+                               alg_values->details.cs_values.r_pub.r_pub,
+                               &ps->blinding_key.nonce);
+      return;
+    }
+  default:
+    GNUNET_break (0);
+  }
+}
+
+
+/**
+ * @brief setup a random planchet
+ * In Case of RSA planchet, the bks gets set
+ * In Case of Clause Schnorr this will be set in future
+ */
+void
+TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps,
+                             const struct
+                             TALER_ExchangeWithdrawValues *alg_values)
+{
+  GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
+                              &ps->coin_priv,
+                              sizeof (struct TALER_CoinSpendPrivateKeyP));
+  switch (alg_values->cipher)
+  {
+  case TALER_DENOMINATION_INVALID:
+    GNUNET_break (0);
+    return;
+  case TALER_DENOMINATION_RSA:
+    TALER_planchet_blinding_secret_create (ps,
+                                           alg_values);
+    return;
+  case TALER_DENOMINATION_CS:
+    // Will be set in a later stage for Clause Blind Schnorr Scheme
+    return;
+  default:
+    GNUNET_break (0);
+  }
 }
 
 
 enum GNUNET_GenericReturnValue
 TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
-                        const struct TALER_PlanchetSecretsP *ps,
+                        const struct TALER_ExchangeWithdrawValues *alg_values,
+                        struct TALER_PlanchetSecretsP *ps,
                         struct TALER_CoinPubHash *c_hash,
                         struct TALER_PlanchetDetail *pd)
 {
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
+  GNUNET_assert (alg_values->cipher == dk->cipher);
+
   GNUNET_CRYPTO_eddsa_key_get_public (&ps->coin_priv.eddsa_priv,
                                       &coin_pub.eddsa_pub);
-  if (GNUNET_OK !=
-      TALER_denom_blind (dk,
-                         &ps->blinding_key,
-                         NULL, /* FIXME-Oec */
-                         &coin_pub,
-                         c_hash,
-                         &pd->coin_ev,
-                         &pd->coin_ev_size))
+
+  switch (dk->cipher)
   {
+  case TALER_DENOMINATION_RSA:
+    if (GNUNET_OK !=
+        TALER_denom_blind (dk,
+                           &ps->blinding_key,
+                           NULL, /* FIXME-Oec */
+                           &coin_pub,
+                           alg_values,
+                           c_hash,
+                           &pd->blinded_planchet))
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    break;
+  case TALER_DENOMINATION_CS:
+    if (GNUNET_OK !=
+        TALER_denom_blind (dk,
+                           &ps->blinding_key,
+                           NULL,   /* FIXME-Oec */
+                           &coin_pub,
+                           alg_values,
+                           c_hash,
+                           &pd->blinded_planchet))
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    break;
+  default:
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
+
+  pd->blinded_planchet.cipher = dk->cipher;
   TALER_denom_pub_hash (dk,
                         &pd->denom_pub_hash);
   return GNUNET_OK;
 }
 
 
+void
+TALER_blinded_planchet_free (struct TALER_BlindedPlanchet *blinded_planchet)
+{
+  switch (blinded_planchet->cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    GNUNET_free (blinded_planchet->details.rsa_blinded_planchet.blinded_msg);
+    break;
+  case TALER_DENOMINATION_CS:
+    // nothing to do for CS
+    break;
+  default:
+    GNUNET_break (0);
+  }
+}
+
+
 enum GNUNET_GenericReturnValue
-TALER_planchet_to_coin (
-  const struct TALER_DenominationPublicKey *dk,
-  const struct TALER_BlindedDenominationSignature *blind_sig,
-  const struct TALER_PlanchetSecretsP *ps,
-  const struct TALER_CoinPubHash *c_hash,
-  struct TALER_FreshCoin *coin)
+TALER_planchet_to_coin (const struct TALER_DenominationPublicKey *dk,
+                        const struct
+                        TALER_BlindedDenominationSignature *blind_sig,
+                        const struct TALER_PlanchetSecretsP *ps,
+                        const struct TALER_CoinPubHash *c_hash,
+                        const struct TALER_ExchangeWithdrawValues *alg_values,
+                        struct TALER_FreshCoin *coin)
 {
   struct TALER_DenominationSignature sig;
 
-  if (GNUNET_OK !=
-      TALER_denom_sig_unblind (&sig,
-                               blind_sig,
-                               &ps->blinding_key,
-                               dk))
+  if (dk->cipher != blind_sig->cipher
+      && dk->cipher != alg_values->cipher)
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
+
+  switch (dk->cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    if (GNUNET_OK !=
+        TALER_denom_sig_unblind (&sig,
+                                 blind_sig,
+                                 &ps->blinding_key,
+                                 dk))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    break;
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_CRYPTO_CsC c[2];
+      struct GNUNET_CRYPTO_CsBlindingSecret bs[2];
+      struct TALER_DenominationCsPublicR r_pub_blind;
+
+      GNUNET_CRYPTO_cs_blinding_secrets_derive (&ps->blinding_key.nonce, bs);
+
+      GNUNET_CRYPTO_cs_calc_blinded_c (bs,
+                                       
alg_values->details.cs_values.r_pub.r_pub,
+                                       &dk->details.cs_public_key,
+                                       &c_hash->hash,
+                                       sizeof(struct GNUNET_HashCode),
+                                       c,
+                                       r_pub_blind.r_pub);
+
+      sig.details.cs_signature.r_point
+        = r_pub_blind.r_pub[blind_sig->details.blinded_cs_answer.b];
+
+      if (GNUNET_OK !=
+          TALER_denom_sig_unblind (&sig,
+                                   blind_sig,
+                                   &ps->blinding_key,
+                                   dk))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      break;
+    }
+  default:
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
   if (GNUNET_OK !=
       TALER_denom_pub_verify (dk,
                               &sig,
@@ -231,6 +424,7 @@ TALER_planchet_to_coin (
     TALER_denom_sig_free (&sig);
     return GNUNET_SYSERR;
   }
+
   coin->sig = sig;
   coin->coin_priv = ps->coin_priv;
   return GNUNET_OK;
@@ -306,14 +500,52 @@ TALER_refresh_get_commitment (struct 
TALER_RefreshCommitmentP *rc,
 }
 
 
-void
-TALER_coin_ev_hash (const void *coin_ev,
-                    size_t coin_ev_size,
+enum GNUNET_GenericReturnValue
+TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
+                    const struct TALER_DenominationHash *denom_hash,
                     struct TALER_BlindedCoinHash *bch)
 {
-  GNUNET_CRYPTO_hash (coin_ev,
-                      coin_ev_size,
-                      &bch->hash);
+  switch (blinded_planchet->cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    {
+      struct GNUNET_HashContext *hash_context;
+      hash_context = GNUNET_CRYPTO_hash_context_start ();
+
+      // // FIXME: Include denom_pub into hash
+      // 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;
+  }
 }
 
 
diff --git a/src/util/crypto_helper_rsa.c b/src/util/crypto_helper_cs.c
similarity index 60%
copy from src/util/crypto_helper_rsa.c
copy to src/util/crypto_helper_cs.c
index fbfc9770..593aa0c2 100644
--- a/src/util/crypto_helper_rsa.c
+++ b/src/util/crypto_helper_cs.c
@@ -14,24 +14,24 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file util/crypto_helper_denom.c
+ * @file util/crypto_helper_cs.c
  * @brief utility functions for running out-of-process private key operations
  * @author Christian Grothoff
  */
 #include "platform.h"
 #include "taler_util.h"
 #include "taler_signatures.h"
-#include "taler-exchange-secmod-rsa.h"
+#include "taler-exchange-secmod-cs.h"
 #include <poll.h>
 #include "crypto_helper_common.h"
 
 
-struct TALER_CRYPTO_RsaDenominationHelper
+struct TALER_CRYPTO_CsDenominationHelper
 {
   /**
    * Function to call with updates to available key material.
    */
-  TALER_CRYPTO_RsaDenominationKeyStatusCallback dkc;
+  TALER_CRYPTO_CsDenominationKeyStatusCallback dkc;
 
   /**
    * Closure for @e dkc
@@ -63,7 +63,7 @@ struct TALER_CRYPTO_RsaDenominationHelper
  * @param[in,out] dh handle to tear down connection of
  */
 static void
-do_disconnect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
+do_disconnect (struct TALER_CRYPTO_CsDenominationHelper *dh)
 {
   GNUNET_break (0 == close (dh->sock));
   dh->sock = -1;
@@ -79,7 +79,7 @@ do_disconnect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-try_connect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
+try_connect (struct TALER_CRYPTO_CsDenominationHelper *dh)
 {
   if (-1 != dh->sock)
     return GNUNET_OK;
@@ -105,28 +105,28 @@ try_connect (struct TALER_CRYPTO_RsaDenominationHelper 
*dh)
     do_disconnect (dh);
     return GNUNET_SYSERR;
   }
-  TALER_CRYPTO_helper_rsa_poll (dh);
+  TALER_CRYPTO_helper_cs_poll (dh);
   return GNUNET_OK;
 }
 
 
-struct TALER_CRYPTO_RsaDenominationHelper *
-TALER_CRYPTO_helper_rsa_connect (
+struct TALER_CRYPTO_CsDenominationHelper *
+TALER_CRYPTO_helper_cs_connect (
   const struct GNUNET_CONFIGURATION_Handle *cfg,
-  TALER_CRYPTO_RsaDenominationKeyStatusCallback dkc,
+  TALER_CRYPTO_CsDenominationKeyStatusCallback dkc,
   void *dkc_cls)
 {
-  struct TALER_CRYPTO_RsaDenominationHelper *dh;
+  struct TALER_CRYPTO_CsDenominationHelper *dh;
   char *unixpath;
 
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               "taler-exchange-secmod-rsa",
+                                               "taler-exchange-secmod-cs",
                                                "UNIXPATH",
                                                &unixpath))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
+                               "taler-exchange-secmod-cs",
                                "UNIXPATH");
     return NULL;
   }
@@ -135,13 +135,13 @@ TALER_CRYPTO_helper_rsa_connect (
   if (strlen (unixpath) >= sizeof (dh->sa.sun_path))
   {
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
+                               "taler-exchange-secmod-cs",
                                "UNIXPATH",
                                "path too long");
     GNUNET_free (unixpath);
     return NULL;
   }
-  dh = GNUNET_new (struct TALER_CRYPTO_RsaDenominationHelper);
+  dh = GNUNET_new (struct TALER_CRYPTO_CsDenominationHelper);
   dh->dkc = dkc;
   dh->dkc_cls = dkc_cls;
   dh->sa.sun_family = AF_UNIX;
@@ -153,7 +153,7 @@ TALER_CRYPTO_helper_rsa_connect (
   if (GNUNET_OK !=
       try_connect (dh))
   {
-    TALER_CRYPTO_helper_rsa_disconnect (dh);
+    TALER_CRYPTO_helper_cs_disconnect (dh);
     return NULL;
   }
   return dh;
@@ -161,21 +161,20 @@ TALER_CRYPTO_helper_rsa_connect (
 
 
 /**
- * Handle a #TALER_HELPER_RSA_MT_AVAIL message from the helper.
+ * Handle a #TALER_HELPER_CS_MT_AVAIL message from the helper.
  *
  * @param dh helper context
  * @param hdr message that we received
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper *dh,
+handle_mt_avail (struct TALER_CRYPTO_CsDenominationHelper *dh,
                  const struct GNUNET_MessageHeader *hdr)
 {
-  const struct TALER_CRYPTO_RsaKeyAvailableNotification *kan
-    = (const struct TALER_CRYPTO_RsaKeyAvailableNotification *) hdr;
+  const struct TALER_CRYPTO_CsKeyAvailableNotification *kan
+    = (const struct TALER_CRYPTO_CsKeyAvailableNotification *) hdr;
   const char *buf = (const char *) &kan[1];
   const char *section_name;
-  uint16_t ps;
   uint16_t snl;
 
   if (sizeof (*kan) > ntohs (hdr->size))
@@ -183,9 +182,8 @@ handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  ps = ntohs (kan->pub_size);
   snl = ntohs (kan->section_name_len);
-  if (ntohs (hdr->size) != sizeof (*kan) + ps + snl)
+  if (ntohs (hdr->size) != sizeof (*kan) + snl)
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -195,7 +193,7 @@ handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  section_name = &buf[ps];
+  section_name = buf;
   if ('\0' != section_name[snl - 1])
   {
     GNUNET_break_op (0);
@@ -204,26 +202,19 @@ handle_mt_avail (struct 
TALER_CRYPTO_RsaDenominationHelper *dh,
 
   {
     struct TALER_DenominationPublicKey denom_pub;
-    struct TALER_RsaPubHashP h_rsa;
+    struct TALER_CsPubHashP h_cs;
 
-    denom_pub.cipher = TALER_DENOMINATION_RSA;
-    denom_pub.details.rsa_public_key
-      = GNUNET_CRYPTO_rsa_public_key_decode (buf,
-                                             ntohs (kan->pub_size));
-    if (NULL == denom_pub.details.rsa_public_key)
-    {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-    GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.details.rsa_public_key,
-                                       &h_rsa.hash);
+    denom_pub.cipher = TALER_DENOMINATION_CS;
+    denom_pub.details.cs_public_key = kan->denom_pub;
+
+    TALER_cs_pub_hash (&denom_pub.details.cs_public_key, &h_cs);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Received RSA key %s (%s)\n",
-                GNUNET_h2s (&h_rsa.hash),
+                "Received CS key %s (%s)\n",
+                GNUNET_h2s (&h_cs.hash),
                 section_name);
     if (GNUNET_OK !=
-        TALER_exchange_secmod_rsa_verify (
-          &h_rsa,
+        TALER_exchange_secmod_cs_verify (
+          &h_cs,
           section_name,
           GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
           GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
@@ -238,7 +229,7 @@ handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
              section_name,
              GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
              GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
-             &h_rsa,
+             &h_cs,
              &denom_pub,
              &kan->secm_pub,
              &kan->secm_sig);
@@ -249,18 +240,18 @@ handle_mt_avail (struct 
TALER_CRYPTO_RsaDenominationHelper *dh,
 
 
 /**
- * Handle a #TALER_HELPER_RSA_MT_PURGE message from the helper.
+ * Handle a #TALER_HELPER_CS_MT_PURGE message from the helper.
  *
  * @param dh helper context
  * @param hdr message that we received
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-handle_mt_purge (struct TALER_CRYPTO_RsaDenominationHelper *dh,
+handle_mt_purge (struct TALER_CRYPTO_CsDenominationHelper *dh,
                  const struct GNUNET_MessageHeader *hdr)
 {
-  const struct TALER_CRYPTO_RsaKeyPurgeNotification *pn
-    = (const struct TALER_CRYPTO_RsaKeyPurgeNotification *) hdr;
+  const struct TALER_CRYPTO_CsKeyPurgeNotification *pn
+    = (const struct TALER_CRYPTO_CsKeyPurgeNotification *) hdr;
 
   if (sizeof (*pn) != ntohs (hdr->size))
   {
@@ -269,12 +260,12 @@ handle_mt_purge (struct 
TALER_CRYPTO_RsaDenominationHelper *dh,
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received revocation of denomination key %s\n",
-              GNUNET_h2s (&pn->h_rsa.hash));
+              GNUNET_h2s (&pn->h_cs.hash));
   dh->dkc (dh->dkc_cls,
            NULL,
            GNUNET_TIME_UNIT_ZERO_TS,
            GNUNET_TIME_UNIT_ZERO,
-           &pn->h_rsa,
+           &pn->h_cs,
            NULL,
            NULL,
            NULL);
@@ -283,7 +274,7 @@ handle_mt_purge (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
 
 
 void
-TALER_CRYPTO_helper_rsa_poll (struct TALER_CRYPTO_RsaDenominationHelper *dh)
+TALER_CRYPTO_helper_cs_poll (struct TALER_CRYPTO_CsDenominationHelper *dh)
 {
   char buf[UINT16_MAX];
   size_t off = 0;
@@ -344,7 +335,7 @@ more:
                 (unsigned int) msize);
     switch (ntohs (hdr->type))
     {
-    case TALER_HELPER_RSA_MT_AVAIL:
+    case TALER_HELPER_CS_MT_AVAIL:
       if (GNUNET_OK !=
           handle_mt_avail (dh,
                            hdr))
@@ -354,7 +345,7 @@ more:
         return;
       }
       break;
-    case TALER_HELPER_RSA_MT_PURGE:
+    case TALER_HELPER_CS_MT_PURGE:
       if (GNUNET_OK !=
           handle_mt_purge (dh,
                            hdr))
@@ -364,9 +355,9 @@ more:
         return;
       }
       break;
-    case TALER_HELPER_RSA_SYNCED:
+    case TALER_HELPER_CS_SYNCED:
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Now synchronized with RSA helper\n");
+                  "Now synchronized with CS helper\n");
       dh->synced = true;
       break;
     default:
@@ -388,11 +379,10 @@ more:
 
 
 struct TALER_BlindedDenominationSignature
-TALER_CRYPTO_helper_rsa_sign (
-  struct TALER_CRYPTO_RsaDenominationHelper *dh,
-  const struct TALER_RsaPubHashP *h_rsa,
-  const void *msg,
-  size_t msg_size,
+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 ds = {
@@ -413,17 +403,15 @@ TALER_CRYPTO_helper_rsa_sign (
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Requesting signature\n");
   {
-    char buf[sizeof (struct TALER_CRYPTO_SignRequest) + msg_size];
-    struct TALER_CRYPTO_SignRequest *sr
-      = (struct TALER_CRYPTO_SignRequest *) buf;
+    char buf[sizeof (struct TALER_CRYPTO_CsSignRequest)];
+    struct TALER_CRYPTO_CsSignRequest *sr
+      = (struct TALER_CRYPTO_CsSignRequest *) buf;
 
     sr->header.size = htons (sizeof (buf));
-    sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN);
+    sr->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN);
     sr->reserved = htonl (0);
-    sr->h_rsa = *h_rsa;
-    memcpy (&sr[1],
-            msg,
-            msg_size);
+    sr->h_cs = *h_cs;
+    sr->planchet = *blinded_planchet;
     if (GNUNET_OK !=
         TALER_crypto_helper_send_all (dh->sock,
                                       buf,
@@ -490,7 +478,7 @@ more:
         continue;
       switch (ntohs (hdr->type))
       {
-      case TALER_HELPER_RSA_MT_RES_SIGNATURE:
+      case TALER_HELPER_CS_MT_RES_SIGNATURE:
         if (msize < sizeof (struct TALER_CRYPTO_SignResponse))
         {
           GNUNET_break_op (0);
@@ -508,27 +496,16 @@ more:
         {
           const struct TALER_CRYPTO_SignResponse *sr =
             (const struct TALER_CRYPTO_SignResponse *) buf;
-          struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
-
-          rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (
-            &sr[1],
-            msize - sizeof (*sr));
-          if (NULL == rsa_signature)
-          {
-            GNUNET_break_op (0);
-            do_disconnect (dh);
-            *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-            goto end;
-          }
+          // TODO: add nullcheck
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       "Received signature\n");
           *ec = TALER_EC_NONE;
           finished = true;
-          ds.cipher = TALER_DENOMINATION_RSA;
-          ds.details.blinded_rsa_signature = rsa_signature;
+          ds.cipher = TALER_DENOMINATION_CS;
+          ds.details.blinded_cs_answer = sr->cs_answer;
           break;
         }
-      case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE:
+      case TALER_HELPER_CS_MT_RES_SIGN_FAILURE:
         if (msize != sizeof (struct TALER_CRYPTO_SignFailure))
         {
           GNUNET_break_op (0);
@@ -546,7 +523,7 @@ more:
           finished = true;
           break;
         }
-      case TALER_HELPER_RSA_MT_AVAIL:
+      case TALER_HELPER_CS_MT_AVAIL:
         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                     "Received new key!\n");
         if (GNUNET_OK !=
@@ -559,7 +536,7 @@ more:
           goto end;
         }
         break; /* while(1) loop ensures we recvfrom() again */
-      case TALER_HELPER_RSA_MT_PURGE:
+      case TALER_HELPER_CS_MT_PURGE:
         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                     "Received revocation!\n");
         if (GNUNET_OK !=
@@ -572,9 +549,9 @@ more:
           goto end;
         }
         break; /* while(1) loop ensures we recvfrom() again */
-      case TALER_HELPER_RSA_SYNCED:
+      case TALER_HELPER_CS_SYNCED:
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Synchronized add odd time with RSA helper!\n");
+                    "Synchronized add odd time with CS helper!\n");
         dh->synced = true;
         break;
       default:
@@ -601,14 +578,14 @@ end:
 
 
 void
-TALER_CRYPTO_helper_rsa_revoke (
-  struct TALER_CRYPTO_RsaDenominationHelper *dh,
-  const struct TALER_RsaPubHashP *h_rsa)
+TALER_CRYPTO_helper_cs_revoke (
+  struct TALER_CRYPTO_CsDenominationHelper *dh,
+  const struct TALER_CsPubHashP *h_cs)
 {
-  struct TALER_CRYPTO_RevokeRequest rr = {
+  struct TALER_CRYPTO_CsRevokeRequest rr = {
     .header.size = htons (sizeof (rr)),
-    .header.type = htons (TALER_HELPER_RSA_MT_REQ_REVOKE),
-    .h_rsa = *h_rsa
+    .header.type = htons (TALER_HELPER_CS_MT_REQ_REVOKE),
+    .h_cs = *h_cs
   };
 
   if (GNUNET_OK !=
@@ -626,13 +603,208 @@ TALER_CRYPTO_helper_rsa_revoke (
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Requested revocation of denomination key %s\n",
-              GNUNET_h2s (&h_rsa->hash));
+              GNUNET_h2s (&h_cs->hash));
+}
+
+
+struct TALER_DenominationCsPublicR
+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_DenominationCsPublicR r_pub;
+
+  memset (&r_pub,
+          0,
+          sizeof (r_pub));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting R derivation process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+    return r_pub;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting R\n");
+  {
+    struct TALER_CRYPTO_CsRDeriveRequest rdr;
+
+    rdr.header.size = htons (sizeof (rdr));
+    rdr.header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE);
+    rdr.reserved = htonl (0);
+    rdr.h_cs = *h_cs;
+    rdr.nonce = *nonce;
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (dh->sock,
+                                      &rdr,
+                                      sizeof (rdr)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "send");
+      do_disconnect (dh);
+      *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+      return r_pub;
+    }
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Awaiting reply\n");
+  {
+    char buf[UINT16_MAX];
+    size_t off = 0;
+    const struct GNUNET_MessageHeader *hdr
+      = (const struct GNUNET_MessageHeader *) buf;
+    bool finished = false;
+
+    *ec = TALER_EC_INVALID;
+    while (1)
+    {
+      uint16_t msize;
+      ssize_t ret;
+
+      ret = recv (dh->sock,
+                  &buf[off],
+                  sizeof (buf) - off,
+                  (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+      if (ret < 0)
+      {
+        if (EINTR == errno)
+          continue;
+        if (EAGAIN == errno)
+        {
+          GNUNET_assert (finished);
+          GNUNET_assert (0 == off);
+          return r_pub;
+        }
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "recv");
+        do_disconnect (dh);
+        *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+        break;
+      }
+      if (0 == ret)
+      {
+        GNUNET_break (0 == off);
+        if (! finished)
+          *ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        return r_pub;
+      }
+      off += ret;
+more:
+      if (off < sizeof (struct GNUNET_MessageHeader))
+        continue;
+      msize = ntohs (hdr->size);
+      if (off < msize)
+        continue;
+      switch (ntohs (hdr->type))
+      {
+      case TALER_HELPER_CS_MT_RES_RDERIVE:
+        if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        if (finished)
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        {
+          const struct TALER_CRYPTO_RDeriveResponse *rdr =
+            (const struct TALER_CRYPTO_RDeriveResponse *) buf;
+
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received R\n");
+          *ec = TALER_EC_NONE;
+          finished = true;
+          r_pub = rdr->r_pub;
+          break;
+        }
+      case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE:
+        if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        {
+          const struct TALER_CRYPTO_RDeriveFailure *rdf =
+            (const struct TALER_CRYPTO_RDeriveFailure *) buf;
+
+          *ec = (enum TALER_ErrorCode) ntohl (rdf->ec);
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "R derivation failed!\n");
+          finished = true;
+          break;
+        }
+      case TALER_HELPER_CS_MT_AVAIL:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received new key!\n");
+        if (GNUNET_OK !=
+            handle_mt_avail (dh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        break; /* while(1) loop ensures we recvfrom() again */
+      case TALER_HELPER_CS_MT_PURGE:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received revocation!\n");
+        if (GNUNET_OK !=
+            handle_mt_purge (dh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        break; /* while(1) loop ensures we recvfrom() again */
+      case TALER_HELPER_CS_SYNCED:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Synchronized add odd time with CS helper!\n");
+        dh->synced = true;
+        break;
+      default:
+        GNUNET_break_op (0);
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Received unexpected message of type %u\n",
+                    ntohs (hdr->type));
+        do_disconnect (dh);
+        *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        goto end;
+      }
+      memmove (buf,
+               &buf[msize],
+               off - msize);
+      off -= msize;
+      goto more;
+    } /* while(1) */
+end:
+    return r_pub;
+  }
 }
 
 
 void
-TALER_CRYPTO_helper_rsa_disconnect (
-  struct TALER_CRYPTO_RsaDenominationHelper *dh)
+TALER_CRYPTO_helper_cs_disconnect (
+  struct TALER_CRYPTO_CsDenominationHelper *dh)
 {
   if (-1 != dh->sock)
     do_disconnect (dh);
@@ -640,4 +812,4 @@ TALER_CRYPTO_helper_rsa_disconnect (
 }
 
 
-/* end of crypto_helper_denom.c */
+/* end of crypto_helper_cs.c */
diff --git a/src/util/denom.c b/src/util/denom.c
index b6b3764d..88bdd611 100644
--- a/src/util/denom.c
+++ b/src/util/denom.c
@@ -34,6 +34,7 @@ TALER_denom_priv_create (struct TALER_DenominationPrivateKey 
*denom_priv,
   memset (denom_pub,
           0,
           sizeof (*denom_pub));
+
   switch (cipher)
   {
   case TALER_DENOMINATION_INVALID:
@@ -63,10 +64,17 @@ TALER_denom_priv_create (struct 
TALER_DenominationPrivateKey *denom_priv,
     denom_pub->details.rsa_public_key
       = GNUNET_CRYPTO_rsa_private_key_get_public (
           denom_priv->details.rsa_private_key);
-    denom_priv->cipher = cipher;
-    denom_pub->cipher = cipher;
+    denom_priv->cipher = TALER_DENOMINATION_RSA;
+    denom_pub->cipher = TALER_DENOMINATION_RSA;
+    return GNUNET_OK;
+  case TALER_DENOMINATION_CS:
+    GNUNET_CRYPTO_cs_private_key_generate 
(&denom_priv->details.cs_private_key);
+    GNUNET_CRYPTO_cs_private_key_get_public (
+      &denom_priv->details.cs_private_key,
+      &denom_pub->details.cs_public_key);
+    denom_priv->cipher = TALER_DENOMINATION_CS;
+    denom_pub->cipher = TALER_DENOMINATION_CS;
     return GNUNET_OK;
-  // TODO: add case for Clause-Schnorr
   default:
     GNUNET_break (0);
   }
@@ -74,15 +82,43 @@ TALER_denom_priv_create (struct 
TALER_DenominationPrivateKey *denom_priv,
 }
 
 
+enum GNUNET_GenericReturnValue
+TALER_denom_cs_derive_r_public (const struct TALER_CsNonce *nonce,
+                                const struct
+                                TALER_DenominationPrivateKey *denom_priv,
+                                struct TALER_DenominationCsPublicR *r_pub)
+{
+  if (denom_priv->cipher != TALER_DENOMINATION_CS)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  struct GNUNET_CRYPTO_CsRSecret r[2];
+  GNUNET_CRYPTO_cs_r_derive (&nonce->nonce,
+                             &denom_priv->details.cs_private_key,
+                             r);
+  GNUNET_CRYPTO_cs_r_get_public (&r[0], &r_pub->r_pub[0]);
+  GNUNET_CRYPTO_cs_r_get_public (&r[1], &r_pub->r_pub[1]);
+  return GNUNET_OK;
+}
+
+
 enum GNUNET_GenericReturnValue
 TALER_denom_sign_blinded (struct TALER_BlindedDenominationSignature *denom_sig,
                           const struct TALER_DenominationPrivateKey 
*denom_priv,
-                          void *blinded_msg,
-                          size_t blinded_msg_size)
+                          const struct TALER_BlindedPlanchet *blinded_planchet)
 {
   memset (denom_sig,
           0,
           sizeof (*denom_sig));
+
+  if (blinded_planchet->cipher != denom_priv->cipher)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
   switch (denom_priv->cipher)
   {
   case TALER_DENOMINATION_INVALID:
@@ -92,8 +128,8 @@ TALER_denom_sign_blinded (struct 
TALER_BlindedDenominationSignature *denom_sig,
     denom_sig->details.blinded_rsa_signature
       = GNUNET_CRYPTO_rsa_sign_blinded (
           denom_priv->details.rsa_private_key,
-          blinded_msg,
-          blinded_msg_size);
+          blinded_planchet->details.rsa_blinded_planchet.blinded_msg,
+          blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size);
     if (NULL == denom_sig->details.blinded_rsa_signature)
     {
       GNUNET_break (0);
@@ -101,7 +137,27 @@ TALER_denom_sign_blinded (struct 
TALER_BlindedDenominationSignature *denom_sig,
     }
     denom_sig->cipher = TALER_DENOMINATION_RSA;
     return GNUNET_OK;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_CRYPTO_CsRSecret r[2];
+      GNUNET_CRYPTO_cs_r_derive (
+        &blinded_planchet->details.cs_blinded_planchet.nonce.nonce,
+        &denom_priv->details.cs_private_key,
+        r);
+
+      denom_sig->details.blinded_cs_answer.b =
+        GNUNET_CRYPTO_cs_sign_derive (&denom_priv->details.cs_private_key,
+                                      r,
+                                      blinded_planchet->details.
+                                      cs_blinded_planchet.c,
+                                      &blinded_planchet->details.
+                                      cs_blinded_planchet.nonce.nonce,
+                                      &denom_sig->details.blinded_cs_answer.
+                                      s_scalar);
+
+      denom_sig->cipher = TALER_DENOMINATION_CS;
+    }
+    return GNUNET_OK;
   default:
     GNUNET_break (0);
   }
@@ -139,7 +195,17 @@ TALER_denom_sig_unblind (
     }
     denom_sig->cipher = TALER_DENOMINATION_RSA;
     return GNUNET_OK;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_CRYPTO_CsBlindingSecret bs[2];
+      GNUNET_CRYPTO_cs_blinding_secrets_derive (&bks->nonce, bs);
+
+      GNUNET_CRYPTO_cs_unblind 
(&bdenom_sig->details.blinded_cs_answer.s_scalar,
+                                &bs[bdenom_sig->details.blinded_cs_answer.b],
+                                &denom_sig->details.cs_signature.s_scalar);
+      denom_sig->cipher = TALER_DENOMINATION_CS;
+      return GNUNET_OK;
+    }
   default:
     GNUNET_break (0);
   }
@@ -147,15 +213,6 @@ TALER_denom_sig_unblind (
 }
 
 
-void
-TALER_blinding_secret_create (union TALER_DenominationBlindingKeyP *bs)
-{
-  GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
-                              bs,
-                              sizeof (*bs));
-}
-
-
 /**
  * Hash @a rsa.
  *
@@ -172,6 +229,22 @@ TALER_rsa_pub_hash (const struct 
GNUNET_CRYPTO_RsaPublicKey *rsa,
 }
 
 
+/**
+ * Hash @a cs. key
+ *
+ * @param cs key to hash
+ * @param[out] h_cs where to write the result
+ */
+void
+TALER_cs_pub_hash (const struct GNUNET_CRYPTO_CsPublicKey *cs,
+                   struct TALER_CsPubHashP *h_cs)
+{
+  GNUNET_CRYPTO_hash (cs,
+                      sizeof(*cs),
+                      &h_cs->hash);
+}
+
+
 void
 TALER_denom_pub_hash (const struct TALER_DenominationPublicKey *denom_pub,
                       struct TALER_DenominationHash *denom_hash)
@@ -202,7 +275,11 @@ TALER_denom_pub_hash (const struct 
TALER_DenominationPublicKey *denom_pub,
       GNUNET_free (buf);
     }
     break;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    GNUNET_CRYPTO_hash_context_read (hc,
+                                     &denom_pub->details.cs_public_key,
+                                     sizeof(denom_pub->details.cs_public_key));
+    break;
   default:
     GNUNET_assert (0);
   }
@@ -225,7 +302,13 @@ TALER_denom_priv_to_pub (const struct 
TALER_DenominationPrivateKey *denom_priv,
       = GNUNET_CRYPTO_rsa_private_key_get_public (
           denom_priv->details.rsa_private_key);
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    denom_pub->cipher = TALER_DENOMINATION_CS;
+    denom_pub->age_mask = age_mask;
+    GNUNET_CRYPTO_cs_private_key_get_public (
+      &denom_priv->details.cs_private_key,
+      &denom_pub->details.cs_public_key);
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -237,28 +320,49 @@ TALER_denom_blind (const struct 
TALER_DenominationPublicKey *dk,
                    const union TALER_DenominationBlindingKeyP *coin_bks,
                    const struct TALER_AgeHash *age_commitment_hash,
                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                   const struct TALER_ExchangeWithdrawValues *alg_values,
                    struct TALER_CoinPubHash *c_hash,
-                   void **coin_ev,
-                   size_t *coin_ev_size)
+                   struct TALER_BlindedPlanchet *blinded_planchet)
 {
+  TALER_coin_pub_hash (coin_pub,
+                       age_commitment_hash,
+                       c_hash);
   switch (dk->cipher)
   {
   case TALER_DENOMINATION_RSA:
-    TALER_coin_pub_hash (coin_pub,
-                         age_commitment_hash,
-                         c_hash);
+    blinded_planchet->cipher = dk->cipher;
+
     if (GNUNET_YES !=
         GNUNET_CRYPTO_rsa_blind (&c_hash->hash,
                                  &coin_bks->rsa_bks,
                                  dk->details.rsa_public_key,
-                                 coin_ev,
-                                 coin_ev_size))
+                                 
&blinded_planchet->details.rsa_blinded_planchet
+                                 .blinded_msg,
+                                 
&blinded_planchet->details.rsa_blinded_planchet
+                                 .blinded_msg_size))
     {
       GNUNET_break (0);
       return GNUNET_SYSERR;
     }
     return GNUNET_OK;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    {
+      blinded_planchet->cipher = dk->cipher;
+      struct TALER_DenominationCsPublicR blinded_r_pub;
+      struct GNUNET_CRYPTO_CsBlindingSecret bs[2];
+
+      GNUNET_CRYPTO_cs_blinding_secrets_derive (&coin_bks->nonce, bs);
+
+      GNUNET_CRYPTO_cs_calc_blinded_c (bs,
+                                       
alg_values->details.cs_values.r_pub.r_pub,
+                                       &dk->details.cs_public_key,
+                                       &c_hash->hash,
+                                       sizeof(struct GNUNET_HashCode),
+                                       blinded_planchet->details.
+                                       cs_blinded_planchet.c,
+                                       blinded_r_pub.r_pub);
+      return GNUNET_OK;
+    }
   default:
     GNUNET_break (0);
     return GNUNET_SYSERR;
@@ -276,6 +380,7 @@ TALER_denom_pub_verify (const struct 
TALER_DenominationPublicKey *denom_pub,
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
+
   switch (denom_pub->cipher)
   {
   case TALER_DENOMINATION_INVALID:
@@ -292,7 +397,18 @@ TALER_denom_pub_verify (const struct 
TALER_DenominationPublicKey *denom_pub,
       return GNUNET_NO;
     }
     return GNUNET_YES;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_cs_verify (&denom_sig->details.cs_signature,
+                                 &denom_pub->details.cs_public_key,
+                                 &c_hash->hash,
+                                 sizeof(struct GNUNET_HashCode)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Coin signature is invalid\n");
+      return GNUNET_NO;
+    }
+    return GNUNET_YES;
   default:
     GNUNET_assert (0);
   }
@@ -314,7 +430,9 @@ TALER_denom_pub_free (struct TALER_DenominationPublicKey 
*denom_pub)
     }
     denom_pub->cipher = TALER_DENOMINATION_INVALID;
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // ATM nothing needs to be freed, but check again after implementation.
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -336,7 +454,9 @@ TALER_denom_priv_free (struct TALER_DenominationPrivateKey 
*denom_priv)
     }
     denom_priv->cipher = TALER_DENOMINATION_INVALID;
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // ATM nothing needs to be freed, but check again after implementation.
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -358,7 +478,9 @@ TALER_denom_sig_free (struct TALER_DenominationSignature 
*denom_sig)
     }
     denom_sig->cipher = TALER_DENOMINATION_INVALID;
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // ATM nothing needs to be freed, but check again after implementation.
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -382,7 +504,9 @@ TALER_blinded_denom_sig_free (
     }
     denom_sig->cipher = TALER_DENOMINATION_INVALID;
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // ATM nothing needs to be freed, but check again after implementation.
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -408,7 +532,9 @@ TALER_denom_pub_deep_copy (struct 
TALER_DenominationPublicKey *denom_dst,
       = GNUNET_CRYPTO_rsa_public_key_dup (
           denom_src->details.rsa_public_key);
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // In Case of CS, the above is already a deep copy *denom_dst = *denom_src;
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -429,7 +555,9 @@ TALER_denom_sig_deep_copy (struct 
TALER_DenominationSignature *denom_dst,
       = GNUNET_CRYPTO_rsa_signature_dup (
           denom_src->details.rsa_signature);
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // In Case of CS, the above is already a deep copy *denom_dst = *denom_src;
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -451,7 +579,9 @@ TALER_blinded_denom_sig_deep_copy (
       = GNUNET_CRYPTO_rsa_signature_dup (
           denom_src->details.blinded_rsa_signature);
     return;
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    // In Case of CS, the above is already a deep copy *denom_dst = *denom_src;
+    return;
   default:
     GNUNET_assert (0);
   }
@@ -473,7 +603,9 @@ TALER_denom_pub_cmp (const struct 
TALER_DenominationPublicKey *denom1,
   case TALER_DENOMINATION_RSA:
     return GNUNET_CRYPTO_rsa_public_key_cmp (denom1->details.rsa_public_key,
                                              denom2->details.rsa_public_key);
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    return GNUNET_memcmp (&denom1->details.cs_public_key,
+                          &denom2->details.cs_public_key);
   default:
     GNUNET_assert (0);
   }
@@ -494,7 +626,9 @@ TALER_denom_sig_cmp (const struct 
TALER_DenominationSignature *sig1,
   case TALER_DENOMINATION_RSA:
     return GNUNET_CRYPTO_rsa_signature_cmp (sig1->details.rsa_signature,
                                             sig2->details.rsa_signature);
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    return GNUNET_memcmp (&sig1->details.cs_signature,
+                          &sig2->details.cs_signature);
   default:
     GNUNET_assert (0);
   }
@@ -516,7 +650,9 @@ TALER_blinded_denom_sig_cmp (
   case TALER_DENOMINATION_RSA:
     return GNUNET_CRYPTO_rsa_signature_cmp 
(sig1->details.blinded_rsa_signature,
                                             
sig2->details.blinded_rsa_signature);
-  // TODO: add case for Clause-Schnorr
+  case TALER_DENOMINATION_CS:
+    return GNUNET_memcmp (&sig1->details.blinded_cs_answer,
+                          &sig2->details.blinded_cs_answer);
   default:
     GNUNET_assert (0);
   }
diff --git a/src/util/secmod_signatures.c b/src/util/secmod_signatures.c
index 9cb15bcf..8e629ebb 100644
--- a/src/util/secmod_signatures.c
+++ b/src/util/secmod_signatures.c
@@ -81,7 +81,7 @@ TALER_exchange_secmod_rsa_sign (
   struct TALER_DenominationKeyAnnouncementPS dka = {
     .purpose.purpose = htonl (TALER_SIGNATURE_SM_RSA_DENOMINATION_KEY),
     .purpose.size = htonl (sizeof (dka)),
-    .h_rsa = *h_rsa,
+    .h_denom.hash = h_rsa->hash,
     .anchor_time = GNUNET_TIME_timestamp_hton (start_sign),
     .duration_withdraw = GNUNET_TIME_relative_hton (duration)
   };
@@ -108,7 +108,7 @@ TALER_exchange_secmod_rsa_verify (
   struct TALER_DenominationKeyAnnouncementPS dka = {
     .purpose.purpose = htonl (TALER_SIGNATURE_SM_RSA_DENOMINATION_KEY),
     .purpose.size = htonl (sizeof (dka)),
-    .h_rsa = *h_rsa,
+    .h_denom.hash = h_rsa->hash,
     .anchor_time = GNUNET_TIME_timestamp_hton (start_sign),
     .duration_withdraw = GNUNET_TIME_relative_hton (duration)
   };
@@ -124,4 +124,59 @@ TALER_exchange_secmod_rsa_verify (
 }
 
 
+void
+TALER_exchange_secmod_cs_sign (
+  const struct TALER_CsPubHashP *h_cs,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_sign,
+  struct GNUNET_TIME_Relative duration,
+  const struct TALER_SecurityModulePrivateKeyP *secm_priv,
+  struct TALER_SecurityModuleSignatureP *secm_sig)
+{
+  struct TALER_DenominationKeyAnnouncementPS dka = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_SM_CS_DENOMINATION_KEY),
+    .purpose.size = htonl (sizeof (dka)),
+    .h_denom.hash = h_cs->hash,
+    .anchor_time = GNUNET_TIME_timestamp_hton (start_sign),
+    .duration_withdraw = GNUNET_TIME_relative_hton (duration)
+  };
+
+  GNUNET_CRYPTO_hash (section_name,
+                      strlen (section_name) + 1,
+                      &dka.h_section_name);
+  GNUNET_CRYPTO_eddsa_sign (&secm_priv->eddsa_priv,
+                            &dka,
+                            &secm_sig->eddsa_signature);
+
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_exchange_secmod_cs_verify (
+  const struct TALER_CsPubHashP *h_cs,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_sign,
+  struct GNUNET_TIME_Relative duration,
+  const struct TALER_SecurityModulePublicKeyP *secm_pub,
+  const struct TALER_SecurityModuleSignatureP *secm_sig)
+{
+  struct TALER_DenominationKeyAnnouncementPS dka = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_SM_CS_DENOMINATION_KEY),
+    .purpose.size = htonl (sizeof (dka)),
+    .h_denom.hash = h_cs->hash,
+    .anchor_time = GNUNET_TIME_timestamp_hton (start_sign),
+    .duration_withdraw = GNUNET_TIME_relative_hton (duration)
+  };
+
+  GNUNET_CRYPTO_hash (section_name,
+                      strlen (section_name) + 1,
+                      &dka.h_section_name);
+  return
+    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SM_CS_DENOMINATION_KEY,
+                                &dka,
+                                &secm_sig->eddsa_signature,
+                                &secm_pub->eddsa_pub);
+}
+
+
 /* end of secmod_signatures.c */
diff --git a/src/util/taler-exchange-secmod-rsa.c 
b/src/util/taler-exchange-secmod-cs.c
similarity index 80%
copy from src/util/taler-exchange-secmod-rsa.c
copy to src/util/taler-exchange-secmod-cs.c
index 43387929..a47e9f22 100644
--- a/src/util/taler-exchange-secmod-rsa.c
+++ b/src/util/taler-exchange-secmod-cs.c
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file util/taler-exchange-secmod-rsa.c
- * @brief Standalone process to perform private key RSA operations
+ * @file util/taler-exchange-secmod-cs.c
+ * @brief Standalone process to perform private key CS operations
  * @author Christian Grothoff
  *
  * Key design points:
@@ -33,15 +33,18 @@
  */
 #include "platform.h"
 #include "taler_util.h"
-#include "taler-exchange-secmod-rsa.h"
+#include "taler-exchange-secmod-cs.h"
 #include <gcrypt.h>
 #include <pthread.h>
+#include <sys/eventfd.h>
 #include "taler_error_codes.h"
 #include "taler_signatures.h"
 #include "secmod_common.h"
 #include <poll.h>
 
 
+#define TALER_CFG_CIPHER_LEN 3
+
 /**
  * Information we keep per denomination.
  */
@@ -77,22 +80,22 @@ struct DenominationKey
   /**
    * The private key of the denomination.
    */
-  struct GNUNET_CRYPTO_RsaPrivateKey *denom_priv;
+  struct GNUNET_CRYPTO_CsPrivateKey denom_priv;
 
   /**
    * The public key of the denomination.
    */
-  struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
+  struct GNUNET_CRYPTO_CsPublicKey denom_pub;
 
   /**
    * Message to transmit to clients to introduce this public key.
    */
-  struct TALER_CRYPTO_RsaKeyAvailableNotification *an;
+  struct TALER_CRYPTO_CsKeyAvailableNotification *an;
 
   /**
    * Hash of this denomination's public key.
    */
-  struct TALER_RsaPubHashP h_rsa;
+  struct TALER_CsPubHashP h_cs;
 
   /**
    * Time at which this key is supposed to become valid.
@@ -156,10 +159,6 @@ struct Denomination
    */
   char *section;
 
-  /**
-   * Length of (new) RSA keys (in bits).
-   */
-  uint32_t rsa_keysize;
 };
 
 
@@ -208,7 +207,7 @@ static struct Denomination *denom_head;
 static struct Denomination *denom_tail;
 
 /**
- * Map of hashes of public (RSA) keys to `struct DenominationKey *`
+ * Map of hashes of public (CS) keys to `struct DenominationKey *`
  * with the respective private keys.
  */
 static struct GNUNET_CONTAINER_MultiHashMap *keys;
@@ -239,38 +238,30 @@ generate_response (struct DenominationKey *dk)
 {
   struct Denomination *denom = dk->denom;
   size_t nlen = strlen (denom->section) + 1;
-  struct TALER_CRYPTO_RsaKeyAvailableNotification *an;
-  size_t buf_len;
-  void *buf;
+  struct TALER_CRYPTO_CsKeyAvailableNotification *an;
   void *p;
   size_t tlen;
 
-  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dk->denom_pub,
-                                                 &buf);
-  GNUNET_assert (buf_len < UINT16_MAX);
+  GNUNET_assert (sizeof(dk->denom_pub) < UINT16_MAX);
   GNUNET_assert (nlen < UINT16_MAX);
-  tlen = buf_len + nlen + sizeof (*an);
+  tlen = nlen + sizeof (*an);
   GNUNET_assert (tlen < UINT16_MAX);
   an = GNUNET_malloc (tlen);
   an->header.size = htons ((uint16_t) tlen);
-  an->header.type = htons (TALER_HELPER_RSA_MT_AVAIL);
-  an->pub_size = htons ((uint16_t) buf_len);
+  an->header.type = htons (TALER_HELPER_CS_MT_AVAIL);
   an->section_name_len = htons ((uint16_t) nlen);
   an->anchor_time = GNUNET_TIME_timestamp_hton (dk->anchor);
   an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw);
-  TALER_exchange_secmod_rsa_sign (&dk->h_rsa,
-                                  denom->section,
-                                  dk->anchor,
-                                  denom->duration_withdraw,
-                                  &TES_smpriv,
-                                  &an->secm_sig);
+  an->denom_pub = dk->denom_pub;
+  TALER_exchange_secmod_cs_sign (&dk->h_cs,
+                                 denom->section,
+                                 dk->anchor,
+                                 denom->duration_withdraw,
+                                 &TES_smpriv,
+                                 &an->secm_sig);
   an->secm_pub = TES_smpub;
   p = (void *) &an[1];
   memcpy (p,
-          buf,
-          buf_len);
-  GNUNET_free (buf);
-  memcpy (p + buf_len,
           denom->section,
           nlen);
   dk->an = an;
@@ -288,29 +279,29 @@ generate_response (struct DenominationKey *dk)
  */
 static enum GNUNET_GenericReturnValue
 handle_sign_request (struct TES_Client *client,
-                     const struct TALER_CRYPTO_SignRequest *sr)
+                     const struct TALER_CRYPTO_CsSignRequest *sr)
 {
   struct DenominationKey *dk;
-  const void *blinded_msg = &sr[1];
-  size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr);
-  struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
+  struct GNUNET_CRYPTO_CsRSecret r[2];
+
+  struct TALER_BlindedDenominationCsSignAnswer cs_answer;
   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
 
   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   dk = GNUNET_CONTAINER_multihashmap_get (keys,
-                                          &sr->h_rsa.hash);
+                                          &sr->h_cs.hash);
   if (NULL == dk)
   {
     struct TALER_CRYPTO_SignFailure sf = {
       .header.size = htons (sizeof (sr)),
-      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
+      .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
       .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
     };
 
     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Signing request failed, denomination key %s unknown\n",
-                GNUNET_h2s (&sr->h_rsa.hash));
+                GNUNET_h2s (&sr->h_cs.hash));
     return TES_transmit (client->csock,
                          &sf.header);
   }
@@ -319,75 +310,70 @@ handle_sign_request (struct TES_Client *client,
     /* it is too early */
     struct TALER_CRYPTO_SignFailure sf = {
       .header.size = htons (sizeof (sr)),
-      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
+      .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
       .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
     };
 
     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Signing request failed, denomination key %s is not yet 
valid\n",
-                GNUNET_h2s (&sr->h_rsa.hash));
+                GNUNET_h2s (&sr->h_cs.hash));
     return TES_transmit (client->csock,
                          &sf.header);
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Received request to sign over %u bytes with key %s\n",
-              (unsigned int) blinded_msg_size,
-              GNUNET_h2s (&sr->h_rsa.hash));
+              "Received request to sign over bytes with key %s\n",
+              GNUNET_h2s (&sr->h_cs.hash));
   GNUNET_assert (dk->rc < UINT_MAX);
   dk->rc++;
   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
-  rsa_signature
-    = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv,
-                                      blinded_msg,
-                                      blinded_msg_size);
+
+  GNUNET_CRYPTO_cs_r_derive (&sr->planchet.nonce.nonce, &dk->denom_priv, r);
+  cs_answer.b = GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv,
+                                              r,
+                                              sr->planchet.c,
+                                              &sr->planchet.nonce.nonce,
+                                              &cs_answer.s_scalar);
+
   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   GNUNET_assert (dk->rc > 0);
   dk->rc--;
   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
-  if (NULL == rsa_signature)
-  {
-    struct TALER_CRYPTO_SignFailure sf = {
-      .header.size = htons (sizeof (sf)),
-      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
-      .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
-    };
-
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Signing request failed, worker failed to produce 
signature\n");
-    return TES_transmit (client->csock,
-                         &sf.header);
-  }
+  // if (NULL == cs_answer)
+  // {
+  //   struct TALER_CRYPTO_SignFailure sf = {
+  //     .header.size = htons (sizeof (sf)),
+  //     .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
+  //     .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
+  //   };
+
+  //   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+  //               "Signing request failed, worker failed to produce 
signature\n");
+  //   return TES_transmit (client->csock,
+  //                        &sf.header);
+  // }
 
   {
     struct TALER_CRYPTO_SignResponse *sr;
-    void *buf;
-    size_t buf_size;
     size_t tsize;
     enum GNUNET_GenericReturnValue ret;
 
-    buf_size = GNUNET_CRYPTO_rsa_signature_encode (rsa_signature,
-                                                   &buf);
-    GNUNET_CRYPTO_rsa_signature_free (rsa_signature);
-    tsize = sizeof (*sr) + buf_size;
+    tsize = sizeof (*sr) + sizeof(cs_answer);
     GNUNET_assert (tsize < UINT16_MAX);
     sr = GNUNET_malloc (tsize);
     sr->header.size = htons (tsize);
-    sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE);
-    memcpy (&sr[1],
-            buf,
-            buf_size);
-    GNUNET_free (buf);
+    sr->header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE);
+    sr->cs_answer = cs_answer;
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Sending RSA signature after %s\n",
+                "Sending CS signature after %s\n",
                 GNUNET_TIME_relative2s (
                   GNUNET_TIME_absolute_get_duration (now),
                   GNUNET_YES));
     ret = TES_transmit (client->csock,
                         &sr->header);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Sent RSA signature after %s\n",
+                "Sent CS signature after %s\n",
                 GNUNET_TIME_relative2s (
                   GNUNET_TIME_absolute_get_duration (now),
                   GNUNET_YES));
@@ -409,30 +395,16 @@ setup_key (struct DenominationKey *dk,
            struct DenominationKey *position)
 {
   struct Denomination *denom = dk->denom;
-  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
-  struct GNUNET_CRYPTO_RsaPublicKey *pub;
-  size_t buf_size;
-  void *buf;
+  struct GNUNET_CRYPTO_CsPrivateKey priv;
+  struct GNUNET_CRYPTO_CsPublicKey pub;
+
+  GNUNET_CRYPTO_cs_private_key_generate (&priv);
+  GNUNET_CRYPTO_cs_private_key_get_public (&priv, &pub);
+  // TODO: Add nullcheck?
+
+  TALER_cs_pub_hash (&pub,
+                     &dk->h_cs);
 
-  priv = GNUNET_CRYPTO_rsa_private_key_create (denom->rsa_keysize);
-  if (NULL == priv)
-  {
-    GNUNET_break (0);
-    GNUNET_SCHEDULER_shutdown ();
-    global_ret = EXIT_FAILURE;
-    return GNUNET_SYSERR;
-  }
-  pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
-  if (NULL == pub)
-  {
-    GNUNET_break (0);
-    GNUNET_CRYPTO_rsa_private_key_free (priv);
-    return GNUNET_SYSERR;
-  }
-  buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv,
-                                                   &buf);
-  TALER_rsa_pub_hash (pub,
-                      &dk->h_rsa);
   GNUNET_asprintf (&dk->filename,
                    "%s/%s/%llu",
                    keydir,
@@ -441,22 +413,18 @@ setup_key (struct DenominationKey *dk,
                                          / 
GNUNET_TIME_UNIT_SECONDS.rel_value_us));
   if (GNUNET_OK !=
       GNUNET_DISK_fn_write (dk->filename,
-                            buf,
-                            buf_size,
+                            &priv,
+                            sizeof(priv),
                             GNUNET_DISK_PERM_USER_READ))
   {
     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
                               "write",
                               dk->filename);
-    GNUNET_free (buf);
-    GNUNET_CRYPTO_rsa_private_key_free (priv);
-    GNUNET_CRYPTO_rsa_public_key_free (pub);
     return GNUNET_SYSERR;
   }
-  GNUNET_free (buf);
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
-              GNUNET_h2s (&dk->h_rsa.hash),
+              GNUNET_h2s (&dk->h_cs.hash),
               GNUNET_TIME_timestamp2s (dk->anchor),
               dk->filename,
               (unsigned long long) key_gen);
@@ -467,14 +435,12 @@ setup_key (struct DenominationKey *dk,
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap_put (
         keys,
-        &dk->h_rsa.hash,
+        &dk->h_cs.hash,
         dk,
         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Duplicate private key created! Terminating.\n");
-    GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv);
-    GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub);
     GNUNET_free (dk->filename);
     GNUNET_free (dk->an);
     GNUNET_free (dk);
@@ -518,7 +484,7 @@ purge_key (struct DenominationKey *dk)
  */
 static enum GNUNET_GenericReturnValue
 handle_revoke_request (struct TES_Client *client,
-                       const struct TALER_CRYPTO_RevokeRequest *rr)
+                       const struct TALER_CRYPTO_CsRevokeRequest *rr)
 {
   struct DenominationKey *dk;
   struct DenominationKey *ndk;
@@ -527,13 +493,13 @@ handle_revoke_request (struct TES_Client *client,
   (void) client;
   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   dk = GNUNET_CONTAINER_multihashmap_get (keys,
-                                          &rr->h_rsa.hash);
+                                          &rr->h_cs.hash);
   if (NULL == dk)
   {
     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Revocation request ignored, denomination key %s unknown\n",
-                GNUNET_h2s (&rr->h_rsa.hash));
+                GNUNET_h2s (&rr->h_cs.hash));
     return GNUNET_OK;
   }
   if (dk->purge)
@@ -541,14 +507,14 @@ handle_revoke_request (struct TES_Client *client,
     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Revocation request ignored, denomination key %s already 
revoked\n",
-                GNUNET_h2s (&rr->h_rsa.hash));
+                GNUNET_h2s (&rr->h_cs.hash));
     return GNUNET_OK;
   }
 
   key_gen++;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Revoking key %s, bumping generation to %llu\n",
-              GNUNET_h2s (&rr->h_rsa.hash),
+              GNUNET_h2s (&rr->h_cs.hash),
               (unsigned long long) key_gen);
   purge_key (dk);
 
@@ -573,6 +539,99 @@ handle_revoke_request (struct TES_Client *client,
 }
 
 
+/**
+ * Handle @a client request @a sr to create signature. Create the
+ * signature using the respective key and return the result to
+ * the client.
+ *
+ * @param client the client making the request
+ * @param sr the request details
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_r_derive_request (struct TES_Client *client,
+                         const struct TALER_CRYPTO_CsRDeriveRequest *rdr)
+{
+  struct DenominationKey *dk;
+  struct TALER_DenominationCsPrivateR r_priv;
+  struct TALER_DenominationCsPublicR r_pub;
+  struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  dk = GNUNET_CONTAINER_multihashmap_get (keys,
+                                          &rdr->h_cs.hash);
+  if (NULL == dk)
+  {
+    struct TALER_CRYPTO_RDeriveFailure rdf = {
+      .header.size = htons (sizeof (rdr)),
+      .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
+      .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
+    };
+
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "R Derive request failed, denomination key %s unknown\n",
+                GNUNET_h2s (&rdr->h_cs.hash));
+    return TES_transmit (client->csock,
+                         &rdf.header);
+  }
+  if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time))
+  {
+    /* it is too early */
+    struct TALER_CRYPTO_RDeriveFailure rdf = {
+      .header.size = htons (sizeof (rdr)),
+      .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
+      .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
+    };
+
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "R Derive request failed, denomination key %s is not yet 
valid\n",
+                GNUNET_h2s (&rdr->h_cs.hash));
+    return TES_transmit (client->csock,
+                         &rdf.header);
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received request to derive R with key %s\n",
+              GNUNET_h2s (&rdr->h_cs.hash));
+  GNUNET_assert (dk->rc < UINT_MAX);
+  dk->rc++;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  GNUNET_CRYPTO_cs_r_derive (&rdr->nonce.nonce,
+                             &dk->denom_priv,
+                             r_priv.r);
+  GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[0], &r_pub.r_pub[0]);
+  GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1], &r_pub.r_pub[1]);
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  GNUNET_assert (dk->rc > 0);
+  dk->rc--;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+
+  {
+    struct TALER_CRYPTO_RDeriveResponse rdr;
+    enum GNUNET_GenericReturnValue ret;
+
+    rdr.header.size = htons (sizeof (struct TALER_CRYPTO_RDeriveResponse));
+    rdr.header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE);
+    rdr.r_pub = r_pub;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Sending CS Derived R after %s\n",
+                GNUNET_TIME_relative2s (
+                  GNUNET_TIME_absolute_get_duration (now),
+                  GNUNET_YES));
+    ret = TES_transmit (client->csock,
+                        &rdr.header);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Sent CS Derived R after %s\n",
+                GNUNET_TIME_relative2s (
+                  GNUNET_TIME_absolute_get_duration (now),
+                  GNUNET_YES));
+    return ret;
+  }
+}
+
+
 /**
  * Handle @a hdr message received from @a client.
  *
@@ -581,31 +640,40 @@ handle_revoke_request (struct TES_Client *client,
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-rsa_work_dispatch (struct TES_Client *client,
-                   const struct GNUNET_MessageHeader *hdr)
+cs_work_dispatch (struct TES_Client *client,
+                  const struct GNUNET_MessageHeader *hdr)
 {
   uint16_t msize = ntohs (hdr->size);
 
   switch (ntohs (hdr->type))
   {
-  case TALER_HELPER_RSA_MT_REQ_SIGN:
-    if (msize <= sizeof (struct TALER_CRYPTO_SignRequest))
+  case TALER_HELPER_CS_MT_REQ_SIGN:
+    if (msize < sizeof (struct TALER_CRYPTO_CsSignRequest))
     {
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
     return handle_sign_request (
       client,
-      (const struct TALER_CRYPTO_SignRequest *) hdr);
-  case TALER_HELPER_RSA_MT_REQ_REVOKE:
-    if (msize != sizeof (struct TALER_CRYPTO_RevokeRequest))
+      (const struct TALER_CRYPTO_CsSignRequest *) hdr);
+  case TALER_HELPER_CS_MT_REQ_REVOKE:
+    if (msize != sizeof (struct TALER_CRYPTO_CsRevokeRequest))
     {
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
     return handle_revoke_request (
       client,
-      (const struct TALER_CRYPTO_RevokeRequest *) hdr);
+      (const struct TALER_CRYPTO_CsRevokeRequest *) hdr);
+  case TALER_HELPER_CS_MT_REQ_RDERIVE:
+    if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return handle_r_derive_request (client,
+                                    (const struct
+                                     TALER_CRYPTO_CsRDeriveRequest *) hdr);
   default:
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -621,7 +689,7 @@ rsa_work_dispatch (struct TES_Client *client,
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-rsa_client_init (struct TES_Client *client)
+cs_client_init (struct TES_Client *client)
 {
   size_t obs = 0;
   char *buf;
@@ -673,12 +741,12 @@ rsa_client_init (struct TES_Client *client)
   GNUNET_free (buf);
   {
     struct GNUNET_MessageHeader synced = {
-      .type = htons (TALER_HELPER_RSA_SYNCED),
+      .type = htons (TALER_HELPER_CS_SYNCED),
       .size = htons (sizeof (synced))
     };
 
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Sending RSA SYNCED message to %p\n",
+                "Sending CS SYNCED message to %p\n",
                 client);
     if (GNUNET_OK !=
         TES_transmit (client->csock,
@@ -700,7 +768,7 @@ rsa_client_init (struct TES_Client *client)
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-rsa_update_client_keys (struct TES_Client *client)
+cs_update_client_keys (struct TES_Client *client)
 {
   size_t obs = 0;
   char *buf;
@@ -718,7 +786,7 @@ rsa_update_client_keys (struct TES_Client *client)
       if (key->key_gen <= client->key_gen)
         continue;
       if (key->purge)
-        obs += sizeof (struct TALER_CRYPTO_RsaKeyPurgeNotification);
+        obs += sizeof (struct TALER_CRYPTO_CsKeyPurgeNotification);
       else
         obs += ntohs (key->an->header.size);
     }
@@ -744,10 +812,10 @@ rsa_update_client_keys (struct TES_Client *client)
         continue;
       if (key->purge)
       {
-        struct TALER_CRYPTO_RsaKeyPurgeNotification pn = {
-          .header.type = htons (TALER_HELPER_RSA_MT_PURGE),
+        struct TALER_CRYPTO_CsKeyPurgeNotification pn = {
+          .header.type = htons (TALER_HELPER_CS_MT_PURGE),
           .header.size = htons (sizeof (pn)),
-          .h_rsa = key->h_rsa
+          .h_cs = key->h_cs
         };
 
         memcpy (&buf[obs],
@@ -869,7 +937,7 @@ update_keys (struct Denomination *denom,
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Updating keys of denomination `%s', last key %s valid for 
another %s\n",
                 denom->section,
-                GNUNET_h2s (&denom->keys_tail->h_rsa.hash),
+                GNUNET_h2s (&denom->keys_tail->h_cs.hash),
                 GNUNET_TIME_relative2s (
                   GNUNET_TIME_absolute_get_remaining (
                     GNUNET_TIME_absolute_subtract (
@@ -919,7 +987,7 @@ update_keys (struct Denomination *denom,
     GNUNET_assert (GNUNET_OK ==
                    GNUNET_CONTAINER_multihashmap_remove (
                      keys,
-                     &key->h_rsa.hash,
+                     &key->h_cs.hash,
                      key));
     if ( (! key->purge) &&
          (0 != unlink (key->filename)) )
@@ -927,8 +995,6 @@ update_keys (struct Denomination *denom,
                                 "unlink",
                                 key->filename);
     GNUNET_free (key->filename);
-    GNUNET_CRYPTO_rsa_private_key_free (key->denom_priv);
-    GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub);
     GNUNET_free (key->an);
     GNUNET_free (key);
     key = nxt;
@@ -1011,10 +1077,8 @@ update_denominations (void *cls)
 static void
 parse_key (struct Denomination *denom,
            const char *filename,
-           const void *buf,
-           size_t buf_size)
+           const struct GNUNET_CRYPTO_CsPrivateKey *priv)
 {
-  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
   char *anchor_s;
   char dummy;
   unsigned long long anchor_ll;
@@ -1051,51 +1115,33 @@ parse_key (struct Denomination *denom,
                 filename);
     return;
   }
-  priv = GNUNET_CRYPTO_rsa_private_key_decode (buf,
-                                               buf_size);
-  if (NULL == priv)
   {
-    /* Parser failure. */
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "File `%s' is malformed, skipping\n",
-                filename);
-    return;
-  }
-
-  {
-    struct GNUNET_CRYPTO_RsaPublicKey *pub;
+    struct GNUNET_CRYPTO_CsPublicKey pub;
     struct DenominationKey *dk;
     struct DenominationKey *before;
 
-    pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
-    if (NULL == pub)
-    {
-      GNUNET_break (0);
-      GNUNET_CRYPTO_rsa_private_key_free (priv);
-      return;
-    }
+    // TODO: Add check if pubkey is set?
+    GNUNET_CRYPTO_cs_private_key_get_public (priv, &pub);
     dk = GNUNET_new (struct DenominationKey);
-    dk->denom_priv = priv;
+    dk->denom_priv = *priv;
     dk->denom = denom;
     dk->anchor = anchor;
     dk->filename = GNUNET_strdup (filename);
-    TALER_rsa_pub_hash (pub,
-                        &dk->h_rsa);
+    TALER_cs_pub_hash (&pub,
+                       &dk->h_cs);
     dk->denom_pub = pub;
     generate_response (dk);
     if (GNUNET_OK !=
         GNUNET_CONTAINER_multihashmap_put (
           keys,
-          &dk->h_rsa.hash,
+          &dk->h_cs.hash,
           dk,
           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Duplicate private key %s detected in file `%s'. 
Skipping.\n",
-                  GNUNET_h2s (&dk->h_rsa.hash),
+                  GNUNET_h2s (&dk->h_cs.hash),
                   filename);
-      GNUNET_CRYPTO_rsa_private_key_free (priv);
-      GNUNET_CRYPTO_rsa_public_key_free (pub);
       GNUNET_free (dk->an);
       GNUNET_free (dk);
       return;
@@ -1115,7 +1161,7 @@ parse_key (struct Denomination *denom,
                                        dk);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Imported key %s from `%s'\n",
-                GNUNET_h2s (&dk->h_rsa.hash),
+                GNUNET_h2s (&dk->h_cs.hash),
                 filename);
   }
 }
@@ -1210,7 +1256,7 @@ import_key (void *cls,
     GNUNET_break (0 == close (fd));
     return GNUNET_OK;
   }
-  if (sbuf.st_size > 16 * 1024)
+  if (sbuf.st_size != sizeof(struct GNUNET_CRYPTO_CsPrivateKey))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "File `%s' too big to be a private key\n",
@@ -1232,8 +1278,7 @@ import_key (void *cls,
   }
   parse_key (denom,
              filename,
-             ptr,
-             (size_t) sbuf.st_size);
+             (const struct GNUNET_CRYPTO_CsPrivateKey *) ptr);
   GNUNET_DISK_file_unmap (map);
   GNUNET_DISK_file_close (fh);
   return GNUNET_OK;
@@ -1254,8 +1299,6 @@ parse_denomination_cfg (const struct 
GNUNET_CONFIGURATION_Handle *cfg,
                         const char *ct,
                         struct Denomination *denom)
 {
-  unsigned long long rsa_keysize;
-
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_time (cfg,
                                            ct,
@@ -1272,32 +1315,11 @@ parse_denomination_cfg (const struct 
GNUNET_CONFIGURATION_Handle *cfg,
                                 denom->duration_withdraw))
   {
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
+                               "taler-exchange-secmod-cs",
                                "OVERLAP_DURATION",
                                "Value given must be smaller than value for 
DURATION_WITHDRAW!");
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (cfg,
-                                             ct,
-                                             "RSA_KEYSIZE",
-                                             &rsa_keysize))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               ct,
-                               "RSA_KEYSIZE");
-    return GNUNET_SYSERR;
-  }
-  if ( (rsa_keysize > 4 * 2048) ||
-       (rsa_keysize < 1024) )
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               ct,
-                               "RSA_KEYSIZE",
-                               "Given RSA keysize outside of permitted range 
[1024,8192]\n");
-    return GNUNET_SYSERR;
-  }
-  denom->rsa_keysize = (unsigned int) rsa_keysize;
   denom->section = GNUNET_strdup (ct);
   return GNUNET_OK;
 }
@@ -1340,6 +1362,7 @@ load_denominations (void *cls,
   struct LoadContext *ctx = cls;
   struct Denomination *denom;
   bool wake = true;
+  char *cipher;
 
   if ( (0 != strncasecmp (denomination_alias,
                           "coin_",
@@ -1348,6 +1371,26 @@ load_denominations (void *cls,
                           "coin-",
                           strlen ("coin-"))) )
     return; /* not a denomination type definition */
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
+                                             denomination_alias,
+                                             "CIPHER",
+                                             &cipher))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               denomination_alias,
+                               "CIPHER");
+    return;
+  }
+  if (strlen (cipher) > TALER_CFG_CIPHER_LEN)
+  {
+    return; /* Cipher length must be smaller than TALER_CFG_CIPHER_LEN */
+  }
+  if (0 != strcmp (cipher, "CS"))
+  {
+    return; /* Ignore denominations of other types than CS*/
+  }
+
   denom = GNUNET_new (struct Denomination);
   if (GNUNET_OK !=
       parse_denomination_cfg (ctx->cfg,
@@ -1395,23 +1438,23 @@ load_durations (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
 {
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_time (cfg,
-                                           "taler-exchange-secmod-rsa",
+                                           "taler-exchange-secmod-cs",
                                            "OVERLAP_DURATION",
                                            &overlap_duration))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
+                               "taler-exchange-secmod-cs",
                                "OVERLAP_DURATION");
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_time (cfg,
-                                           "taler-exchange-secmod-rsa",
+                                           "taler-exchange-secmod-cs",
                                            "LOOKAHEAD_SIGN",
                                            &lookahead_sign))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
+                               "taler-exchange-secmod-cs",
                                "LOOKAHEAD_SIGN");
     return GNUNET_SYSERR;
   }
@@ -1452,9 +1495,9 @@ run (void *cls,
      const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   static struct TES_Callbacks cb = {
-    .dispatch = rsa_work_dispatch,
-    .updater = rsa_update_client_keys,
-    .init = rsa_client_init
+    .dispatch = cs_work_dispatch,
+    .updater = cs_update_client_keys,
+    .init = cs_client_init
   };
 
   (void) cls;
@@ -1472,12 +1515,12 @@ run (void *cls,
   }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               "taler-exchange-secmod-rsa",
+                                               "taler-exchange-secmod-cs",
                                                "KEY_DIR",
                                                &keydir))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
+                               "taler-exchange-secmod-cs",
                                "KEY_DIR");
     global_ret = EXIT_NOTCONFIGURED;
     return;
@@ -1489,7 +1532,7 @@ run (void *cls,
     return;
   }
   global_ret = TES_listen_start (cfg,
-                                 "taler-exchange-secmod-rsa",
+                                 "taler-exchange-secmod-cs",
                                  &cb);
   if (0 != global_ret)
     return;
@@ -1566,8 +1609,8 @@ main (int argc,
   TALER_OS_init ();
   now_tmp = now = GNUNET_TIME_timestamp_get ();
   ret = GNUNET_PROGRAM_run (argc, argv,
-                            "taler-exchange-secmod-rsa",
-                            "Handle private RSA key operations for a Taler 
exchange",
+                            "taler-exchange-secmod-cs",
+                            "Handle private CS key operations for a Taler 
exchange",
                             options,
                             &run,
                             NULL);
diff --git a/src/util/taler-exchange-secmod-rsa.conf 
b/src/util/taler-exchange-secmod-cs.conf
similarity index 66%
copy from src/util/taler-exchange-secmod-rsa.conf
copy to src/util/taler-exchange-secmod-cs.conf
index dfa87f05..5085eab7 100644
--- a/src/util/taler-exchange-secmod-rsa.conf
+++ b/src/util/taler-exchange-secmod-cs.conf
@@ -1,4 +1,4 @@
-[taler-exchange-secmod-rsa]
+[taler-exchange-secmod-cs]
 
 # How long should generated coins overlap in their validity
 # periods. Should be long enough to avoid problems with
@@ -8,16 +8,16 @@
 OVERLAP_DURATION = 5 m
 
 # Where do we store the generated private keys.
-KEY_DIR = ${TALER_DATA_HOME}/exchange-secmod-rsa/keys
+KEY_DIR = ${TALER_DATA_HOME}/exchange-secmod-cs/keys
 
 # Where does the helper listen for requests?
-UNIXPATH = $TALER_RUNTIME_DIR/exchange-secmod-rsa/server.sock
+UNIXPATH = $TALER_RUNTIME_DIR/exchange-secmod-cs/server.sock
 
 # Directory for clients.
-CLIENT_DIR = $TALER_RUNTIME_DIR/exchange-secmod-rsa/clients
+CLIENT_DIR = $TALER_RUNTIME_DIR/exchange-secmod-cs/clients
 
 # Where should the security module store its own private key?
-SM_PRIV_KEY = ${TALER_DATA_HOME}/exchange-secmod-rsa/secmod-private-key
+SM_PRIV_KEY = ${TALER_DATA_HOME}/exchange-secmod-cs/secmod-private-key
 
 # For how long into the future do we pre-generate keys?
 LOOKAHEAD_SIGN = 1 year
diff --git a/src/util/taler-exchange-secmod-cs.h 
b/src/util/taler-exchange-secmod-cs.h
new file mode 100644
index 00000000..6c3f9232
--- /dev/null
+++ b/src/util/taler-exchange-secmod-cs.h
@@ -0,0 +1,270 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020 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
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/taler-exchange-secmod-cs.h
+ * @brief IPC messages for the CS crypto helper.
+ * @author Christian Grothoff
+ * @author Gian Demarmels
+ * @author Lucien Heuzeveldt
+ */
+#ifndef TALER_EXCHANGE_SECMOD_CS_H
+#define TALER_EXCHANGE_SECMOD_CS_H
+
+#define TALER_HELPER_CS_MT_PURGE 1
+#define TALER_HELPER_CS_MT_AVAIL 2
+
+#define TALER_HELPER_CS_MT_REQ_INIT 4
+#define TALER_HELPER_CS_MT_REQ_SIGN 5
+#define TALER_HELPER_CS_MT_REQ_REVOKE 6
+#define TALER_HELPER_CS_MT_REQ_RDERIVE 7
+
+#define TALER_HELPER_CS_MT_RES_SIGNATURE 8
+#define TALER_HELPER_CS_MT_RES_SIGN_FAILURE 9
+#define TALER_HELPER_CS_MT_RES_RDERIVE 10
+#define TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE 11
+
+#define TALER_HELPER_CS_SYNCED 12
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+
+/**
+ * Message sent if a key is available.
+ */
+struct TALER_CRYPTO_CsKeyAvailableNotification
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_AVAIL
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Number of bytes of the section name.
+   */
+  uint32_t section_name_len;
+
+  /**
+   * When does the key become available?
+   */
+  struct GNUNET_TIME_TimestampNBO anchor_time;
+
+  /**
+   * How long is the key available after @e anchor_time?
+   */
+  struct GNUNET_TIME_RelativeNBO duration_withdraw;
+
+  /**
+   * Public key used to generate the @e sicm_sig.
+   */
+  struct TALER_SecurityModulePublicKeyP secm_pub;
+
+  /**
+   * Signature affirming the announcement, of
+   * purpose #TALER_SIGNATURE_SM_DENOMINATION_KEY.
+   */
+  struct TALER_SecurityModuleSignatureP secm_sig;
+
+  /**
+   * Denomination Public key
+   */
+  struct GNUNET_CRYPTO_CsPublicKey denom_pub;
+
+  /* followed by @e section_name bytes of the configuration section name
+     of the denomination of this key */
+
+};
+
+
+/**
+ * Message sent if a key was purged.
+ */
+struct TALER_CRYPTO_CsKeyPurgeNotification
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_PURGE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Hash of the public key of the purged CS key.
+   */
+  struct TALER_CsPubHashP h_cs;
+
+};
+
+
+/**
+ * Message sent if a signature is requested.
+ */
+struct TALER_CRYPTO_CsSignRequest
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_REQ_SIGN.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Hash of the public key of the CS key to use for the signature.
+   */
+  struct TALER_CsPubHashP h_cs;
+
+  /**
+   * Planchet containing message to sign
+   * and nonce to derive R from
+   */
+  struct TALER_BlindedCsPlanchet planchet;
+
+};
+
+/**
+ * Message sent if a signature is requested.
+ */
+struct TALER_CRYPTO_CsRDeriveRequest
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_REQ_RDERIVE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Hash of the public key of the CS key to use for the derivation.
+   */
+  struct TALER_CsPubHashP h_cs;
+
+  /**
+   * Withdraw nonce to derive R from
+   */
+  struct TALER_CsNonce nonce;
+};
+
+/**
+ * Message sent if a key was revoked.
+ */
+struct TALER_CRYPTO_CsRevokeRequest
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_REQ_REVOKE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Hash of the public key of the revoked CS key.
+   */
+  struct TALER_CsPubHashP h_cs;
+
+};
+
+
+/**
+ * Message sent if a signature was successfully computed.
+ */
+struct TALER_CRYPTO_SignResponse
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_RES_SIGNATURE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Contains the blindided s and the chosen b
+   */
+  struct TALER_BlindedDenominationCsSignAnswer cs_answer;
+};
+
+/**
+ * Message sent if a R is successfully derived
+ */
+struct TALER_CRYPTO_RDeriveResponse
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_RES_RDERIVE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * derived R
+   */
+  struct TALER_DenominationCsPublicR r_pub;
+};
+
+
+/**
+ * Message sent if signing failed.
+ */
+struct TALER_CRYPTO_SignFailure
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_RES_SIGN_FAILURE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * If available, Taler error code. In NBO.
+   */
+  uint32_t ec;
+
+};
+
+/**
+ * Message sent if derivation failed.
+ */
+struct TALER_CRYPTO_RDeriveFailure
+{
+  /**
+   * Type is #TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * If available, Taler error code. In NBO.
+   */
+  uint32_t ec;
+
+};
+GNUNET_NETWORK_STRUCT_END
+
+
+#endif
diff --git a/src/util/taler-exchange-secmod-rsa.c 
b/src/util/taler-exchange-secmod-rsa.c
index 43387929..fef20524 100644
--- a/src/util/taler-exchange-secmod-rsa.c
+++ b/src/util/taler-exchange-secmod-rsa.c
@@ -41,6 +41,7 @@
 #include "secmod_common.h"
 #include <poll.h>
 
+#define TALER_CFG_CIPHER_LEN 3
 
 /**
  * Information we keep per denomination.
@@ -1340,6 +1341,7 @@ load_denominations (void *cls,
   struct LoadContext *ctx = cls;
   struct Denomination *denom;
   bool wake = true;
+  char *cipher;
 
   if ( (0 != strncasecmp (denomination_alias,
                           "coin_",
@@ -1348,6 +1350,25 @@ load_denominations (void *cls,
                           "coin-",
                           strlen ("coin-"))) )
     return; /* not a denomination type definition */
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
+                                             denomination_alias,
+                                             "CIPHER",
+                                             &cipher))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               denomination_alias,
+                               "CIPHER");
+    return;
+  }
+  if (strlen (cipher) > TALER_CFG_CIPHER_LEN)
+  {
+    return; /* Cipher length must be smaller than TALER_CFG_CIPHER_LEN */
+  }
+  if (0 != strcmp (cipher, "RSA"))
+  {
+    return; /* Ignore denominations of other types than CS */
+  }
   denom = GNUNET_new (struct Denomination);
   if (GNUNET_OK !=
       parse_denomination_cfg (ctx->cfg,
diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c
index 5ee06487..9f01b74c 100644
--- a/src/util/test_crypto.c
+++ b/src/util/test_crypto.c
@@ -82,38 +82,118 @@ test_high_level (void)
  * @return 0 on success
  */
 static int
-test_planchets (void)
+test_planchets_rsa (void)
 {
   struct TALER_PlanchetSecretsP ps;
   struct TALER_DenominationPrivateKey dk_priv;
   struct TALER_DenominationPublicKey dk_pub;
+  struct TALER_ExchangeWithdrawValues alg_values;
   struct TALER_PlanchetDetail pd;
   struct TALER_BlindedDenominationSignature blind_sig;
   struct TALER_FreshCoin coin;
   struct TALER_CoinPubHash c_hash;
 
+
+  GNUNET_assert (GNUNET_SYSERR ==
+                 TALER_denom_priv_create (&dk_priv,
+                                          &dk_pub,
+                                          TALER_DENOMINATION_INVALID));
+
+  GNUNET_assert (GNUNET_SYSERR ==
+                 TALER_denom_priv_create (&dk_priv,
+                                          &dk_pub,
+                                          42));
+
   GNUNET_assert (GNUNET_OK ==
                  TALER_denom_priv_create (&dk_priv,
                                           &dk_pub,
                                           TALER_DENOMINATION_RSA,
                                           1024));
-  TALER_planchet_setup_random (&ps);
+  alg_values.cipher = TALER_DENOMINATION_RSA;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_planchet_prepare (&dk_pub,
+                                         &alg_values,
+                                         &ps,
+                                         &c_hash,
+                                         &pd));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_denom_sign_blinded (&blind_sig,
+                                           &dk_priv,
+                                           &pd.blinded_planchet));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_planchet_to_coin (&dk_pub,
+                                         &blind_sig,
+                                         &ps,
+                                         &c_hash,
+                                         &alg_values,
+                                         &coin));
+  TALER_blinded_denom_sig_free (&blind_sig);
+  TALER_denom_sig_free (&coin.sig);
+  TALER_denom_priv_free (&dk_priv);
+  TALER_denom_pub_free (&dk_pub);
+  return 0;
+}
+
+
+/**
+ * Test the basic planchet functionality of creating a fresh planchet with CS 
denomination
+ * and extracting the respective signature.
+ *
+ * @return 0 on success
+ */
+static int
+test_planchets_cs (void)
+{
+  struct TALER_PlanchetSecretsP ps;
+  struct TALER_DenominationPrivateKey dk_priv;
+  struct TALER_DenominationPublicKey dk_pub;
+  struct TALER_PlanchetDetail pd;
+  struct TALER_CoinPubHash c_hash;
+  struct TALER_BlindedDenominationSignature blind_sig;
+  struct TALER_FreshCoin coin;
+  struct TALER_ExchangeWithdrawValues alg_values;
+
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_denom_priv_create (&dk_priv,
+                                          &dk_pub,
+                                          TALER_DENOMINATION_CS));
+
+  alg_values.cipher = TALER_DENOMINATION_CS;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
+  TALER_cs_withdraw_nonce_derive (&ps.coin_priv,
+                                  &pd.blinded_planchet.details.
+                                  cs_blinded_planchet.nonce);
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_denom_cs_derive_r_public (
+                   &pd.blinded_planchet.details.cs_blinded_planchet.nonce,
+                   &dk_priv,
+                   &alg_values.details.cs_values.r_pub));
+  TALER_planchet_blinding_secret_create (&ps,
+                                         &alg_values);
+
   GNUNET_assert (GNUNET_OK ==
                  TALER_planchet_prepare (&dk_pub,
+                                         &alg_values,
                                          &ps,
                                          &c_hash,
                                          &pd));
+
   GNUNET_assert (GNUNET_OK ==
                  TALER_denom_sign_blinded (&blind_sig,
                                            &dk_priv,
-                                           pd.coin_ev,
-                                           pd.coin_ev_size));
+                                           &pd.blinded_planchet));
+
   GNUNET_assert (GNUNET_OK ==
                  TALER_planchet_to_coin (&dk_pub,
                                          &blind_sig,
                                          &ps,
                                          &c_hash,
+                                         &alg_values,
                                          &coin));
+
   TALER_blinded_denom_sig_free (&blind_sig);
   TALER_denom_sig_free (&coin.sig);
   TALER_denom_priv_free (&dk_priv);
@@ -122,6 +202,22 @@ test_planchets (void)
 }
 
 
+/**
+ * Test the basic planchet functionality of creating a fresh planchet
+ * and extracting the respective signature.
+ * Calls test_planchets_rsa and test_planchets_cs
+ *
+ * @return 0 on success
+ */
+static int
+test_planchets (void)
+{
+  if (0 != test_planchets_rsa ())
+    return -1;
+  return test_planchets_cs ();
+}
+
+
 static int
 test_exchange_sigs (void)
 {
diff --git a/src/util/test_helper_rsa.c b/src/util/test_helper_cs.c
similarity index 61%
copy from src/util/test_helper_rsa.c
copy to src/util/test_helper_cs.c
index ac4ae1dc..c4e68376 100644
--- a/src/util/test_helper_rsa.c
+++ b/src/util/test_helper_cs.c
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file util/test_helper_rsa.c
- * @brief Tests for RSA crypto helper
+ * @file util/test_helper_cs.c
+ * @brief Tests for CS crypto helper
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -74,7 +74,7 @@ struct KeyData
   /**
    * Hash of the public key.
    */
-  struct TALER_RsaPubHashP h_rsa;
+  struct TALER_CsPubHashP h_cs;
 
   /**
    * Full public key.
@@ -128,7 +128,7 @@ free_keys (void)
  *                 zero if the key has been revoked or purged
  * @param validity_duration how long does the key remain available for signing;
  *                 zero if the key has been revoked or purged
- * @param h_rsa hash of the @a denom_pub that is available (or was purged)
+ * @param h_cs hash of the @a denom_pub that is available (or was purged)
  * @param denom_pub the public key itself, NULL if the key was revoked or 
purged
  * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
  * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
@@ -139,7 +139,7 @@ key_cb (void *cls,
         const char *section_name,
         struct GNUNET_TIME_Timestamp start_time,
         struct GNUNET_TIME_Relative validity_duration,
-        const struct TALER_RsaPubHashP *h_rsa,
+        const struct TALER_CsPubHashP *h_cs,
         const struct TALER_DenominationPublicKey *denom_pub,
         const struct TALER_SecurityModulePublicKeyP *sm_pub,
         const struct TALER_SecurityModuleSignatureP *sm_sig)
@@ -149,7 +149,7 @@ key_cb (void *cls,
   (void) sm_sig;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Key notification about key %s in `%s'\n",
-              GNUNET_h2s (&h_rsa->hash),
+              GNUNET_h2s (&h_cs->hash),
               section_name);
   if (0 == validity_duration.rel_value_us)
   {
@@ -158,8 +158,8 @@ key_cb (void *cls,
     GNUNET_break (NULL == denom_pub);
     GNUNET_break (NULL == section_name);
     for (unsigned int i = 0; i<MAX_KEYS; i++)
-      if (0 == GNUNET_memcmp (h_rsa,
-                              &keys[i].h_rsa))
+      if (0 == GNUNET_memcmp (h_cs,
+                              &keys[i].h_cs))
       {
         keys[i].valid = false;
         keys[i].revoked = false;
@@ -181,7 +181,7 @@ key_cb (void *cls,
     if (! keys[i].valid)
     {
       keys[i].valid = true;
-      keys[i].h_rsa = *h_rsa;
+      keys[i].h_cs = *h_cs;
       keys[i].start_time = start_time;
       keys[i].validity_duration = validity_duration;
       TALER_denom_pub_deep_copy (&keys[i].denom_pub,
@@ -203,7 +203,7 @@ key_cb (void *cls,
  * @return 0 on success
  */
 static int
-test_revocation (struct TALER_CRYPTO_RsaDenominationHelper *dh)
+test_revocation (struct TALER_CRYPTO_CsDenominationHelper *dh)
 {
   struct timespec req = {
     .tv_nsec = 250000000
@@ -228,12 +228,12 @@ test_revocation (struct 
TALER_CRYPTO_RsaDenominationHelper *dh)
       keys[j].revoked = true;
       fprintf (stderr,
                "Revoking key %s ...",
-               GNUNET_h2s (&keys[j].h_rsa.hash));
-      TALER_CRYPTO_helper_rsa_revoke (dh,
-                                      &keys[j].h_rsa);
+               GNUNET_h2s (&keys[j].h_cs.hash));
+      TALER_CRYPTO_helper_cs_revoke (dh,
+                                     &keys[j].h_cs);
       for (unsigned int k = 0; k<1000; k++)
       {
-        TALER_CRYPTO_helper_rsa_poll (dh);
+        TALER_CRYPTO_helper_cs_poll (dh);
         if (! keys[j].revoked)
           break;
         nanosleep (&req, NULL);
@@ -244,7 +244,7 @@ test_revocation (struct TALER_CRYPTO_RsaDenominationHelper 
*dh)
         fprintf (stderr,
                  "\nFAILED: timeout trying to revoke key %u\n",
                  j);
-        TALER_CRYPTO_helper_rsa_disconnect (dh);
+        TALER_CRYPTO_helper_cs_disconnect (dh);
         return 2;
       }
       fprintf (stderr, "\n");
@@ -255,6 +255,147 @@ test_revocation (struct 
TALER_CRYPTO_RsaDenominationHelper *dh)
 }
 
 
+/**
+ * Test R derivation logic.
+ *
+ * @param dh handle to the helper
+ * @return 0 on success
+ */
+static int
+test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh)
+{
+  enum TALER_ErrorCode ec;
+  bool success = false;
+  struct TALER_PlanchetSecretsP ps;
+  struct TALER_CoinPubHash c_hash;
+  struct TALER_ExchangeWithdrawValues alg_values;
+
+  alg_values.cipher = TALER_DENOMINATION_CS;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
+  for (unsigned int i = 0; i<MAX_KEYS; i++)
+  {
+    struct TALER_PlanchetDetail pd;
+    if (! keys[i].valid)
+      continue;
+    // TODO: insert assertion into other checks
+    GNUNET_assert (TALER_DENOMINATION_CS == keys[i].denom_pub.cipher);
+    {
+      pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
+
+      TALER_cs_withdraw_nonce_derive (&ps.coin_priv,
+                                      &pd.blinded_planchet.details.
+                                      cs_blinded_planchet.nonce);
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Requesting R derivation with key %s\n",
+                  GNUNET_h2s (&keys[i].h_cs.hash));
+
+      alg_values.details.cs_values.r_pub
+        = TALER_CRYPTO_helper_cs_r_derive (dh,
+                                           &keys[i].h_cs,
+                                           &pd.blinded_planchet.
+                                           details.
+                                           cs_blinded_planchet.nonce,
+                                           &ec);
+    }
+    switch (ec)
+    {
+    case TALER_EC_NONE:
+      if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
+                                      keys[i].start_time.abs_time),
+                                    >,
+                                    GNUNET_TIME_UNIT_SECONDS))
+      {
+        /* key worked too early */
+        GNUNET_break (0);
+        return 4;
+      }
+      if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
+                                      keys[i].start_time.abs_time),
+                                    >,
+                                    keys[i].validity_duration))
+      {
+        /* key worked too later */
+        GNUNET_break (0);
+        return 5;
+      }
+
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Received valid R for key %s\n",
+                  GNUNET_h2s (&keys[i].h_cs.hash));
+
+      TALER_planchet_blinding_secret_create (&ps,
+                                             &alg_values);
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_planchet_prepare (&keys[i].denom_pub,
+                                             &alg_values,
+                                             &ps,
+                                             &c_hash,
+                                             &pd));
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Successfully prepared planchet");
+      success = true;
+      break;
+    case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
+      /* This 'failure' is expected, we're testing also for the
+         error handling! */
+      if ( (GNUNET_TIME_relative_is_zero (
+              GNUNET_TIME_absolute_get_remaining (
+                keys[i].start_time.abs_time))) &&
+           (GNUNET_TIME_relative_cmp (
+              GNUNET_TIME_absolute_get_duration (
+                keys[i].start_time.abs_time),
+              <,
+              keys[i].validity_duration)) )
+      {
+        /* key should have worked! */
+        GNUNET_break (0);
+        return 6;
+      }
+      break;
+    default:
+      /* unexpected error */
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Unexpected error %d\n",
+                  ec);
+      return 7;
+    }
+  }
+  if (! success)
+  {
+    /* no valid key for signing found, also bad */
+    GNUNET_break (0);
+    return 16;
+  }
+
+  /* check R derivation does not work if the key is unknown */
+  {
+    struct TALER_CsPubHashP rnd;
+    struct TALER_CsNonce nonce;
+
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                &rnd,
+                                sizeof (rnd));
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                &nonce,
+                                sizeof (nonce));
+    TALER_CRYPTO_helper_cs_r_derive (dh,
+                                     &rnd,
+                                     &nonce,
+                                     &ec);
+    if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
+    {
+      GNUNET_break (0);
+      return 17;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "R derivation with invalid key %s failed as desired\n",
+                GNUNET_h2s (&rnd.hash));
+  }
+  return 0;
+}
+
+
 /**
  * Test signing logic.
  *
@@ -262,37 +403,54 @@ test_revocation (struct 
TALER_CRYPTO_RsaDenominationHelper *dh)
  * @return 0 on success
  */
 static int
-test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh)
+test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
 {
   struct TALER_BlindedDenominationSignature ds;
   enum TALER_ErrorCode ec;
   bool success = false;
   struct TALER_PlanchetSecretsP ps;
   struct TALER_CoinPubHash c_hash;
+  struct TALER_ExchangeWithdrawValues alg_values;
 
-  TALER_planchet_setup_random (&ps);
+  alg_values.cipher = TALER_DENOMINATION_CS;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
   for (unsigned int i = 0; i<MAX_KEYS; i++)
   {
     if (! keys[i].valid)
       continue;
     {
       struct TALER_PlanchetDetail pd;
+      pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
+      // keys[i].denom_pub.cipher = TALER_DENOMINATION_CS;
+
+      TALER_cs_withdraw_nonce_derive (&ps.coin_priv,
+                                      &pd.blinded_planchet.details.
+                                      cs_blinded_planchet.nonce);
+      alg_values.details.cs_values.r_pub
+        = TALER_CRYPTO_helper_cs_r_derive (dh,
+                                           &keys[i].h_cs,
+                                           &pd.blinded_planchet.
+                                           details.
+                                           cs_blinded_planchet.nonce,
+                                           &ec);
+      TALER_planchet_blinding_secret_create (&ps,
+                                             &alg_values);
 
       GNUNET_assert (GNUNET_YES ==
                      TALER_planchet_prepare (&keys[i].denom_pub,
+                                             &alg_values,
                                              &ps,
                                              &c_hash,
                                              &pd));
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Requesting signature over %u bytes with key %s\n",
-                  (unsigned int) pd.coin_ev_size,
-                  GNUNET_h2s (&keys[i].h_rsa.hash));
-      ds = TALER_CRYPTO_helper_rsa_sign (dh,
-                                         &keys[i].h_rsa,
-                                         pd.coin_ev,
-                                         pd.coin_ev_size,
-                                         &ec);
-      GNUNET_free (pd.coin_ev);
+                  "Requesting signature with key %s\n",
+                  GNUNET_h2s (&keys[i].h_cs.hash));
+      ds = TALER_CRYPTO_helper_cs_sign (dh,
+                                        &keys[i].h_cs,
+                                        &pd.blinded_planchet.details.
+                                        cs_blinded_planchet,
+                                        &ec);
     }
     switch (ec)
     {
@@ -316,33 +474,22 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh)
         return 5;
       }
       {
-        struct TALER_DenominationSignature rs;
-
+        struct TALER_FreshCoin coin;
         if (GNUNET_OK !=
-            TALER_denom_sig_unblind (&rs,
-                                     &ds,
-                                     &ps.blinding_key,
-                                     &keys[i].denom_pub))
+            TALER_planchet_to_coin (&keys[i].denom_pub,
+                                    &ds,
+                                    &ps,
+                                    &c_hash,
+                                    &alg_values,
+                                    &coin))
         {
           GNUNET_break (0);
           return 6;
         }
-        TALER_blinded_denom_sig_free (&ds);
-        if (GNUNET_OK !=
-            TALER_denom_pub_verify (&keys[i].denom_pub,
-                                    &rs,
-                                    &c_hash))
-        {
-          /* signature invalid */
-          GNUNET_break (0);
-          TALER_denom_sig_free (&rs);
-          return 7;
-        }
-        TALER_denom_sig_free (&rs);
       }
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Received valid signature for key %s\n",
-                  GNUNET_h2s (&keys[i].h_rsa.hash));
+                  GNUNET_h2s (&keys[i].h_cs.hash));
       success = true;
       break;
     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
@@ -379,16 +526,25 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh)
 
   /* check signing does not work if the key is unknown */
   {
-    struct TALER_RsaPubHashP rnd;
+    struct TALER_PlanchetDetail pd;
+    struct TALER_CsPubHashP rnd;
 
     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
                                 &rnd,
                                 sizeof (rnd));
-    ds = TALER_CRYPTO_helper_rsa_sign (dh,
-                                       &rnd,
-                                       "Hello",
-                                       strlen ("Hello"),
-                                       &ec);
+    pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
+    GNUNET_assert (GNUNET_YES ==
+                   TALER_planchet_prepare (&keys[0].denom_pub,
+                                           &alg_values,
+                                           &ps,
+                                           &c_hash,
+                                           &pd));
+
+    ds = TALER_CRYPTO_helper_cs_sign (dh,
+                                      &rnd,
+                                      &pd.blinded_planchet.details.
+                                      cs_blinded_planchet,
+                                      &ec);
     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
     {
       if (TALER_EC_NONE == ec)
@@ -411,17 +567,20 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh)
  * @return 0 on success
  */
 static int
-perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
+perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
               const char *type)
 {
   struct TALER_BlindedDenominationSignature ds;
   enum TALER_ErrorCode ec;
   struct GNUNET_TIME_Relative duration;
   struct TALER_PlanchetSecretsP ps;
+  struct TALER_ExchangeWithdrawValues alg_values;
 
-  TALER_planchet_setup_random (&ps);
+  alg_values.cipher = TALER_DENOMINATION_CS;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
   duration = GNUNET_TIME_UNIT_ZERO;
-  TALER_CRYPTO_helper_rsa_poll (dh);
+  TALER_CRYPTO_helper_cs_poll (dh);
   for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
   {
     for (unsigned int i = 0; i<MAX_KEYS; i++)
@@ -441,9 +600,26 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
       {
         struct TALER_CoinPubHash c_hash;
         struct TALER_PlanchetDetail pd;
+        pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
+
+
+        TALER_cs_withdraw_nonce_derive (&ps.coin_priv,
+                                        &pd.blinded_planchet.details.
+                                        cs_blinded_planchet.nonce);
+
+        alg_values.details.cs_values.r_pub
+          = TALER_CRYPTO_helper_cs_r_derive (dh,
+                                             &keys[i].h_cs,
+                                             &pd.blinded_planchet.
+                                             details.
+                                             cs_blinded_planchet.nonce,
+                                             &ec);
+        TALER_planchet_blinding_secret_create (&ps,
+                                               &alg_values);
 
         GNUNET_assert (GNUNET_YES ==
                        TALER_planchet_prepare (&keys[i].denom_pub,
+                                               &alg_values,
                                                &ps,
                                                &c_hash,
                                                &pd));
@@ -453,11 +629,11 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
           struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get ();
           struct GNUNET_TIME_Relative delay;
 
-          ds = TALER_CRYPTO_helper_rsa_sign (dh,
-                                             &keys[i].h_rsa,
-                                             pd.coin_ev,
-                                             pd.coin_ev_size,
-                                             &ec);
+          ds = TALER_CRYPTO_helper_cs_sign (dh,
+                                            &keys[i].h_cs,
+                                            &pd.blinded_planchet.details.
+                                            cs_blinded_planchet,
+                                            &ec);
           if (TALER_EC_NONE != ec)
             break;
           delay = GNUNET_TIME_absolute_get_duration (start);
@@ -468,7 +644,6 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
           if (NUM_SIGN_PERFS <= j)
             break;
         }
-        GNUNET_free (pd.coin_ev);
       }
     } /* for i */
   } /* for j */
@@ -494,7 +669,7 @@ par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
   struct GNUNET_TIME_Absolute start;
   struct GNUNET_TIME_Relative duration;
   pid_t pids[NUM_CORES];
-  struct TALER_CRYPTO_RsaDenominationHelper *dh;
+  struct TALER_CRYPTO_CsDenominationHelper *dh;
 
   start = GNUNET_TIME_absolute_get ();
   for (unsigned int i = 0; i<NUM_CORES; i++)
@@ -506,13 +681,13 @@ par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
     {
       int ret;
 
-      dh = TALER_CRYPTO_helper_rsa_connect (cfg,
-                                            &key_cb,
-                                            NULL);
+      dh = TALER_CRYPTO_helper_cs_connect (cfg,
+                                           &key_cb,
+                                           NULL);
       GNUNET_assert (NULL != dh);
       ret = perf_signing (dh,
                           "parallel");
-      TALER_CRYPTO_helper_rsa_disconnect (dh);
+      TALER_CRYPTO_helper_cs_disconnect (dh);
       free_keys ();
       exit (ret);
     }
@@ -543,7 +718,7 @@ static int
 run_test (void)
 {
   struct GNUNET_CONFIGURATION_Handle *cfg;
-  struct TALER_CRYPTO_RsaDenominationHelper *dh;
+  struct TALER_CRYPTO_CsDenominationHelper *dh;
   struct timespec req = {
     .tv_nsec = 250000000
   };
@@ -552,7 +727,7 @@ run_test (void)
   cfg = GNUNET_CONFIGURATION_create ();
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_load (cfg,
-                                 "test_helper_rsa.conf"))
+                                 "test_helper_cs.conf"))
   {
     GNUNET_break (0);
     return 77;
@@ -563,9 +738,9 @@ run_test (void)
   {
     nanosleep (&req,
                NULL);
-    dh = TALER_CRYPTO_helper_rsa_connect (cfg,
-                                          &key_cb,
-                                          NULL);
+    dh = TALER_CRYPTO_helper_cs_connect (cfg,
+                                         &key_cb,
+                                         NULL);
     if (NULL != dh)
       break;
     fprintf (stderr, ".");
@@ -581,7 +756,7 @@ run_test (void)
   {
     fprintf (stderr,
              "\nFAILED: timeout trying to connect to helper\n");
-    TALER_CRYPTO_helper_rsa_disconnect (dh);
+    TALER_CRYPTO_helper_cs_disconnect (dh);
     GNUNET_CONFIGURATION_destroy (cfg);
     return 1;
   }
@@ -591,12 +766,14 @@ run_test (void)
   ret = 0;
   if (0 == ret)
     ret = test_revocation (dh);
+  if (0 == ret)
+    ret = test_r_derive (dh);
   if (0 == ret)
     ret = test_signing (dh);
   if (0 == ret)
     ret = perf_signing (dh,
                         "sequential");
-  TALER_CRYPTO_helper_rsa_disconnect (dh);
+  TALER_CRYPTO_helper_cs_disconnect (dh);
   free_keys ();
   if (0 == ret)
     ret = par_signing (cfg);
@@ -621,7 +798,7 @@ main (int argc,
   (void) argv;
   unsetenv ("XDG_DATA_HOME");
   unsetenv ("XDG_CONFIG_HOME");
-  GNUNET_log_setup ("test-helper-rsa",
+  GNUNET_log_setup ("test-helper-cs",
                     "WARNING",
                     NULL);
   GNUNET_OS_init (TALER_project_data_default ());
@@ -629,14 +806,14 @@ main (int argc,
   GNUNET_asprintf (&binary_name,
                    "%s/%s",
                    libexec_dir,
-                   "taler-exchange-secmod-rsa");
+                   "taler-exchange-secmod-cs");
   GNUNET_free (libexec_dir);
   helper = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
                                     NULL, NULL, NULL,
                                     binary_name,
                                     binary_name,
                                     "-c",
-                                    "test_helper_rsa.conf",
+                                    "test_helper_cs.conf",
                                     "-L",
                                     "WARNING",
                                     NULL);
@@ -678,4 +855,4 @@ main (int argc,
 }
 
 
-/* end of test_helper_rsa.c */
+/* end of test_helper_cs.c */
diff --git a/src/util/test_helper_rsa.conf b/src/util/test_helper_cs.conf
similarity index 61%
copy from src/util/test_helper_rsa.conf
copy to src/util/test_helper_cs.conf
index 6f445fc5..f3b5b834 100644
--- a/src/util/test_helper_rsa.conf
+++ b/src/util/test_helper_cs.conf
@@ -1,11 +1,11 @@
 [PATHS]
 # Persistent data storage for the testcase
-TALER_TEST_HOME = test_helper_rsa_home/
+TALER_TEST_HOME = test_helper_cs_home/
 
 [coin_1]
 DURATION_WITHDRAW = 1 minute
-RSA_KEYSIZE = 2048
+CIPHER = CS
 
-[taler-exchange-secmod-rsa]
+[taler-exchange-secmod-cs]
 LOOKAHEAD_SIGN = 5 minutes
 OVERLAP_DURATION = 1 s
diff --git a/src/util/test_helper_rsa.c b/src/util/test_helper_rsa.c
index ac4ae1dc..f9f1a860 100644
--- a/src/util/test_helper_rsa.c
+++ b/src/util/test_helper_rsa.c
@@ -268,31 +268,42 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh)
   enum TALER_ErrorCode ec;
   bool success = false;
   struct TALER_PlanchetSecretsP ps;
+  struct TALER_ExchangeWithdrawValues alg_values;
   struct TALER_CoinPubHash c_hash;
 
-  TALER_planchet_setup_random (&ps);
+  alg_values.cipher = TALER_DENOMINATION_RSA;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
   for (unsigned int i = 0; i<MAX_KEYS; i++)
   {
     if (! keys[i].valid)
       continue;
+    if (TALER_DENOMINATION_RSA != keys[i].denom_pub.cipher)
+      continue;
     {
       struct TALER_PlanchetDetail pd;
+      pd.blinded_planchet.cipher = TALER_DENOMINATION_RSA;
 
       GNUNET_assert (GNUNET_YES ==
                      TALER_planchet_prepare (&keys[i].denom_pub,
+                                             &alg_values,
                                              &ps,
                                              &c_hash,
                                              &pd));
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Requesting signature over %u bytes with key %s\n",
-                  (unsigned int) pd.coin_ev_size,
+                  (unsigned
+                   int) pd.blinded_planchet.details.rsa_blinded_planchet.
+                  blinded_msg_size,
                   GNUNET_h2s (&keys[i].h_rsa.hash));
       ds = TALER_CRYPTO_helper_rsa_sign (dh,
                                          &keys[i].h_rsa,
-                                         pd.coin_ev,
-                                         pd.coin_ev_size,
+                                         pd.blinded_planchet.details.
+                                         rsa_blinded_planchet.blinded_msg,
+                                         pd.blinded_planchet.details.
+                                         rsa_blinded_planchet.blinded_msg_size,
                                          &ec);
-      GNUNET_free (pd.coin_ev);
+      TALER_blinded_planchet_free (&pd.blinded_planchet);
     }
     switch (ec)
     {
@@ -418,8 +429,11 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
   enum TALER_ErrorCode ec;
   struct GNUNET_TIME_Relative duration;
   struct TALER_PlanchetSecretsP ps;
+  struct TALER_ExchangeWithdrawValues alg_values;
 
-  TALER_planchet_setup_random (&ps);
+  alg_values.cipher = TALER_DENOMINATION_RSA;
+  TALER_planchet_setup_random (&ps,
+                               &alg_values);
   duration = GNUNET_TIME_UNIT_ZERO;
   TALER_CRYPTO_helper_rsa_poll (dh);
   for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
@@ -428,6 +442,8 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
     {
       if (! keys[i].valid)
         continue;
+      if (TALER_DENOMINATION_RSA != keys[i].denom_pub.cipher)
+        continue;
       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
                                       keys[i].start_time.abs_time),
                                     >,
@@ -444,6 +460,7 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
 
         GNUNET_assert (GNUNET_YES ==
                        TALER_planchet_prepare (&keys[i].denom_pub,
+                                               &alg_values,
                                                &ps,
                                                &c_hash,
                                                &pd));
@@ -455,8 +472,11 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper 
*dh,
 
           ds = TALER_CRYPTO_helper_rsa_sign (dh,
                                              &keys[i].h_rsa,
-                                             pd.coin_ev,
-                                             pd.coin_ev_size,
+                                             pd.blinded_planchet.details.
+                                             rsa_blinded_planchet.blinded_msg,
+                                             pd.blinded_planchet.details.
+                                             rsa_blinded_planchet.
+                                             blinded_msg_size,
                                              &ec);
           if (TALER_EC_NONE != ec)
             break;
@@ -468,7 +488,7 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
           if (NUM_SIGN_PERFS <= j)
             break;
         }
-        GNUNET_free (pd.coin_ev);
+        TALER_blinded_planchet_free (&pd.blinded_planchet);
       }
     } /* for i */
   } /* for j */
diff --git a/src/util/test_helper_rsa.conf b/src/util/test_helper_rsa.conf
index 6f445fc5..d50e64d9 100644
--- a/src/util/test_helper_rsa.conf
+++ b/src/util/test_helper_rsa.conf
@@ -4,6 +4,7 @@ TALER_TEST_HOME = test_helper_rsa_home/
 
 [coin_1]
 DURATION_WITHDRAW = 1 minute
+CIPHER = RSA
 RSA_KEYSIZE = 2048
 
 [taler-exchange-secmod-rsa]

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]