exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

crypto_helper_cs.c (27177B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020, 2021, 2022 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file util/crypto_helper_cs.c
     18  * @brief utility functions for running out-of-process private key operations
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "taler/taler_util.h"
     23 #include "taler/taler_signatures.h"
     24 #include "secmod_cs.h"
     25 #include <poll.h>
     26 #include "crypto_helper_common.h"
     27 
     28 
     29 struct TALER_CRYPTO_CsDenominationHelper
     30 {
     31   /**
     32    * Function to call with updates to available key material.
     33    */
     34   TALER_CRYPTO_CsDenominationKeyStatusCallback dkc;
     35 
     36   /**
     37    * Closure for @e dkc
     38    */
     39   void *dkc_cls;
     40 
     41   /**
     42    * Socket address of the denomination helper process.
     43    * Used to reconnect if the connection breaks.
     44    */
     45   struct sockaddr_un sa;
     46 
     47   /**
     48    * The UNIX domain socket, -1 if we are currently not connected.
     49    */
     50   int sock;
     51 
     52   /**
     53    * Have we ever been sync'ed?
     54    */
     55   bool synced;
     56 };
     57 
     58 
     59 /**
     60  * Disconnect from the helper process.  Updates
     61  * @e sock field in @a dh.
     62  *
     63  * @param[in,out] dh handle to tear down connection of
     64  */
     65 static void
     66 do_disconnect (struct TALER_CRYPTO_CsDenominationHelper *dh)
     67 {
     68   GNUNET_break (0 == close (dh->sock));
     69   dh->sock = -1;
     70   dh->synced = false;
     71 }
     72 
     73 
     74 /**
     75  * Try to connect to the helper process.  Updates
     76  * @e sock field in @a dh.
     77  *
     78  * @param[in,out] dh handle to establish connection for
     79  * @return #GNUNET_OK on success
     80  */
     81 static enum GNUNET_GenericReturnValue
     82 try_connect (struct TALER_CRYPTO_CsDenominationHelper *dh)
     83 {
     84   if (-1 != dh->sock)
     85     return GNUNET_OK;
     86   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     87               "Establishing connection!\n");
     88   dh->sock = socket (AF_UNIX,
     89                      SOCK_STREAM,
     90                      0);
     91   if (-1 == dh->sock)
     92   {
     93     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     94                          "socket");
     95     return GNUNET_SYSERR;
     96   }
     97   if (0 !=
     98       connect (dh->sock,
     99                (const struct sockaddr *) &dh->sa,
    100                sizeof (dh->sa)))
    101   {
    102     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    103                               "connect",
    104                               dh->sa.sun_path);
    105     do_disconnect (dh);
    106     return GNUNET_SYSERR;
    107   }
    108   TALER_CRYPTO_helper_cs_poll (dh);
    109   return GNUNET_OK;
    110 }
    111 
    112 
    113 struct TALER_CRYPTO_CsDenominationHelper *
    114 TALER_CRYPTO_helper_cs_connect (
    115   const struct GNUNET_CONFIGURATION_Handle *cfg,
    116   const char *section,
    117   TALER_CRYPTO_CsDenominationKeyStatusCallback dkc,
    118   void *dkc_cls)
    119 {
    120   struct TALER_CRYPTO_CsDenominationHelper *dh;
    121   char *unixpath;
    122   char *secname;
    123 
    124   GNUNET_asprintf (&secname,
    125                    "%s-secmod-cs",
    126                    section);
    127   if (GNUNET_OK !=
    128       GNUNET_CONFIGURATION_get_value_filename (cfg,
    129                                                secname,
    130                                                "UNIXPATH",
    131                                                &unixpath))
    132   {
    133     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    134                                secname,
    135                                "UNIXPATH");
    136     GNUNET_free (secname);
    137     return NULL;
    138   }
    139   /* we use >= here because we want the sun_path to always
    140      be 0-terminated */
    141   if (strlen (unixpath) >= sizeof (dh->sa.sun_path))
    142   {
    143     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    144                                secname,
    145                                "UNIXPATH",
    146                                "path too long");
    147     GNUNET_free (unixpath);
    148     GNUNET_free (secname);
    149     return NULL;
    150   }
    151   GNUNET_free (secname);
    152   dh = GNUNET_new (struct TALER_CRYPTO_CsDenominationHelper);
    153   dh->dkc = dkc;
    154   dh->dkc_cls = dkc_cls;
    155   dh->sa.sun_family = AF_UNIX;
    156   strncpy (dh->sa.sun_path,
    157            unixpath,
    158            sizeof (dh->sa.sun_path) - 1);
    159   GNUNET_free (unixpath);
    160   dh->sock = -1;
    161   if (GNUNET_OK !=
    162       try_connect (dh))
    163   {
    164     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    165                 "Could not connect to %s. Will keep trying\n",
    166                 "taler-exchange-helper-secmod-cs");
    167   }
    168   return dh;
    169 }
    170 
    171 
    172 /**
    173  * Handle a #TALER_HELPER_CS_MT_AVAIL message from the helper.
    174  *
    175  * @param dh helper context
    176  * @param hdr message that we received
    177  * @return #GNUNET_OK on success
    178  */
    179 static enum GNUNET_GenericReturnValue
    180 handle_mt_avail (struct TALER_CRYPTO_CsDenominationHelper *dh,
    181                  const struct GNUNET_MessageHeader *hdr)
    182 {
    183   const struct TALER_CRYPTO_CsKeyAvailableNotification *kan
    184     = (const struct TALER_CRYPTO_CsKeyAvailableNotification *) hdr;
    185   const char *buf = (const char *) &kan[1];
    186   const char *section_name;
    187   uint16_t snl;
    188 
    189   if (sizeof (*kan) > ntohs (hdr->size))
    190   {
    191     GNUNET_break_op (0);
    192     return GNUNET_SYSERR;
    193   }
    194   snl = ntohs (kan->section_name_len);
    195   if (ntohs (hdr->size) != sizeof (*kan) + snl)
    196   {
    197     GNUNET_break_op (0);
    198     return GNUNET_SYSERR;
    199   }
    200   if (0 == snl)
    201   {
    202     GNUNET_break_op (0);
    203     return GNUNET_SYSERR;
    204   }
    205   section_name = buf;
    206   if ('\0' != section_name[snl - 1])
    207   {
    208     GNUNET_break_op (0);
    209     return GNUNET_SYSERR;
    210   }
    211 
    212   {
    213     struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub;
    214     struct TALER_CsPubHashP h_cs;
    215 
    216     bsign_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey);
    217     bsign_pub->cipher = GNUNET_CRYPTO_BSA_CS;
    218     bsign_pub->rc = 1;
    219     bsign_pub->details.cs_public_key = kan->denom_pub;
    220 
    221     GNUNET_CRYPTO_hash (&bsign_pub->details.cs_public_key,
    222                         sizeof (bsign_pub->details.cs_public_key),
    223                         &bsign_pub->pub_key_hash);
    224     h_cs.hash = bsign_pub->pub_key_hash;
    225     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    226                 "Received CS key %s (%s)\n",
    227                 GNUNET_h2s (&h_cs.hash),
    228                 section_name);
    229     if (GNUNET_OK !=
    230         TALER_exchange_secmod_cs_verify (
    231           &h_cs,
    232           section_name,
    233           GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
    234           GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
    235           &kan->secm_pub,
    236           &kan->secm_sig))
    237     {
    238       GNUNET_break_op (0);
    239       GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub);
    240       return GNUNET_SYSERR;
    241     }
    242     dh->dkc (dh->dkc_cls,
    243              section_name,
    244              GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
    245              GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
    246              &h_cs,
    247              bsign_pub,
    248              &kan->secm_pub,
    249              &kan->secm_sig);
    250     GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub);
    251   }
    252   return GNUNET_OK;
    253 }
    254 
    255 
    256 /**
    257  * Handle a #TALER_HELPER_CS_MT_PURGE message from the helper.
    258  *
    259  * @param dh helper context
    260  * @param hdr message that we received
    261  * @return #GNUNET_OK on success
    262  */
    263 static enum GNUNET_GenericReturnValue
    264 handle_mt_purge (struct TALER_CRYPTO_CsDenominationHelper *dh,
    265                  const struct GNUNET_MessageHeader *hdr)
    266 {
    267   const struct TALER_CRYPTO_CsKeyPurgeNotification *pn
    268     = (const struct TALER_CRYPTO_CsKeyPurgeNotification *) hdr;
    269 
    270   if (sizeof (*pn) != ntohs (hdr->size))
    271   {
    272     GNUNET_break_op (0);
    273     return GNUNET_SYSERR;
    274   }
    275   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    276               "Received revocation of denomination key %s\n",
    277               GNUNET_h2s (&pn->h_cs.hash));
    278   dh->dkc (dh->dkc_cls,
    279            NULL,
    280            GNUNET_TIME_UNIT_ZERO_TS,
    281            GNUNET_TIME_UNIT_ZERO,
    282            &pn->h_cs,
    283            NULL,
    284            NULL,
    285            NULL);
    286   return GNUNET_OK;
    287 }
    288 
    289 
    290 void
    291 TALER_CRYPTO_helper_cs_poll (struct TALER_CRYPTO_CsDenominationHelper *dh)
    292 {
    293   char buf[UINT16_MAX];
    294   size_t off = 0;
    295   unsigned int retry_limit = 3;
    296   const struct GNUNET_MessageHeader *hdr
    297     = (const struct GNUNET_MessageHeader *) buf;
    298 
    299   if (GNUNET_OK !=
    300       try_connect (dh))
    301     return; /* give up */
    302   while (1)
    303   {
    304     uint16_t msize;
    305     ssize_t ret;
    306 
    307     ret = recv (dh->sock,
    308                 buf + off,
    309                 sizeof (buf) - off,
    310                 (dh->synced && (0 == off))
    311                 ? MSG_DONTWAIT
    312                 : 0);
    313     if (ret < 0)
    314     {
    315       if (EINTR == errno)
    316         continue;
    317       if (EAGAIN == errno)
    318       {
    319         GNUNET_assert (dh->synced);
    320         GNUNET_assert (0 == off);
    321         break;
    322       }
    323       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    324                            "recv");
    325       do_disconnect (dh);
    326       if (0 == retry_limit)
    327         return; /* give up */
    328       if (GNUNET_OK !=
    329           try_connect (dh))
    330         return; /* give up */
    331       retry_limit--;
    332       continue;
    333     }
    334     if (0 == ret)
    335     {
    336       GNUNET_break (0 == off);
    337       return;
    338     }
    339     off += ret;
    340 more:
    341     if (off < sizeof (struct GNUNET_MessageHeader))
    342       continue;
    343     msize = ntohs (hdr->size);
    344     if (off < msize)
    345       continue;
    346     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    347                 "Received message of type %u and length %u\n",
    348                 (unsigned int) ntohs (hdr->type),
    349                 (unsigned int) msize);
    350     switch (ntohs (hdr->type))
    351     {
    352     case TALER_HELPER_CS_MT_AVAIL:
    353       if (GNUNET_OK !=
    354           handle_mt_avail (dh,
    355                            hdr))
    356       {
    357         GNUNET_break_op (0);
    358         do_disconnect (dh);
    359         return;
    360       }
    361       break;
    362     case TALER_HELPER_CS_MT_PURGE:
    363       if (GNUNET_OK !=
    364           handle_mt_purge (dh,
    365                            hdr))
    366       {
    367         GNUNET_break_op (0);
    368         do_disconnect (dh);
    369         return;
    370       }
    371       break;
    372     case TALER_HELPER_CS_SYNCED:
    373       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    374                   "Now synchronized with CS helper\n");
    375       dh->synced = true;
    376       break;
    377     default:
    378       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    379                   "Received unexpected message of type %d (len: %u)\n",
    380                   (unsigned int) ntohs (hdr->type),
    381                   (unsigned int) msize);
    382       GNUNET_break_op (0);
    383       do_disconnect (dh);
    384       return;
    385     }
    386     memmove (buf,
    387              &buf[msize],
    388              off - msize);
    389     off -= msize;
    390     goto more;
    391   }
    392 }
    393 
    394 
    395 void
    396 TALER_CRYPTO_helper_cs_revoke (
    397   struct TALER_CRYPTO_CsDenominationHelper *dh,
    398   const struct TALER_CsPubHashP *h_cs)
    399 {
    400   struct TALER_CRYPTO_CsRevokeRequest rr = {
    401     .header.size = htons (sizeof (rr)),
    402     .header.type = htons (TALER_HELPER_CS_MT_REQ_REVOKE),
    403     .h_cs = *h_cs
    404   };
    405 
    406   if (GNUNET_OK !=
    407       try_connect (dh))
    408     return; /* give up */
    409   if (GNUNET_OK !=
    410       TALER_crypto_helper_send_all (dh->sock,
    411                                     &rr,
    412                                     sizeof (rr)))
    413   {
    414     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    415                          "send");
    416     do_disconnect (dh);
    417     return;
    418   }
    419   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    420               "Requested revocation of denomination key %s\n",
    421               GNUNET_h2s (&h_cs->hash));
    422 }
    423 
    424 
    425 enum TALER_ErrorCode
    426 TALER_CRYPTO_helper_cs_batch_sign (
    427   struct TALER_CRYPTO_CsDenominationHelper *dh,
    428   unsigned int reqs_length,
    429   const struct TALER_CRYPTO_CsSignRequest reqs[static reqs_length],
    430   bool for_melt,
    431   struct TALER_BlindedDenominationSignature bss[static reqs_length])
    432 {
    433   enum TALER_ErrorCode ec = TALER_EC_INVALID;
    434   unsigned int rpos;
    435   unsigned int rend;
    436   unsigned int wpos;
    437 
    438   memset (bss,
    439           0,
    440           sizeof (*bss) * reqs_length);
    441   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    442               "Starting signature process\n");
    443   if (GNUNET_OK !=
    444       try_connect (dh))
    445   {
    446     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    447                 "Failed to connect to helper\n");
    448     return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
    449   }
    450 
    451   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    452               "Requesting %u signatures\n",
    453               reqs_length);
    454   rpos = 0;
    455   rend = 0;
    456   wpos = 0;
    457   while (rpos < reqs_length)
    458   {
    459     unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest);
    460 
    461     while ( (rend < reqs_length) &&
    462             (mlen + sizeof (struct TALER_CRYPTO_CsSignRequestMessage)
    463              < UINT16_MAX) )
    464     {
    465       mlen += sizeof (struct TALER_CRYPTO_CsSignRequestMessage);
    466       rend++;
    467     }
    468     {
    469       char obuf[mlen] GNUNET_ALIGN;
    470       struct TALER_CRYPTO_BatchSignRequest *bsr
    471         = (struct TALER_CRYPTO_BatchSignRequest *) obuf;
    472       void *wbuf;
    473 
    474       bsr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_SIGN);
    475       bsr->header.size = htons (mlen);
    476       bsr->batch_size = htonl (rend - rpos);
    477       wbuf = &bsr[1];
    478       for (unsigned int i = rpos; i<rend; i++)
    479       {
    480         struct TALER_CRYPTO_CsSignRequestMessage *csm = wbuf;
    481         const struct TALER_CRYPTO_CsSignRequest *csr = &reqs[i];
    482 
    483         csm->header.size = htons (sizeof (*csm));
    484         csm->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN);
    485         csm->for_melt = htonl (for_melt ? 1 : 0);
    486         csm->h_cs = *csr->h_cs;
    487         csm->message = *csr->blinded_planchet;
    488         wbuf += sizeof (*csm);
    489       }
    490       GNUNET_assert (wbuf == &obuf[mlen]);
    491       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    492                   "Sending batch request [%u-%u)\n",
    493                   rpos,
    494                   rend);
    495       if (GNUNET_OK !=
    496           TALER_crypto_helper_send_all (dh->sock,
    497                                         obuf,
    498                                         sizeof (obuf)))
    499       {
    500         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    501                              "send");
    502         do_disconnect (dh);
    503         return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
    504       }
    505     } /* end of obuf scope */
    506     rpos = rend;
    507     {
    508       char buf[UINT16_MAX];
    509       size_t off = 0;
    510       const struct GNUNET_MessageHeader *hdr
    511         = (const struct GNUNET_MessageHeader *) buf;
    512       bool finished = false;
    513 
    514       while (1)
    515       {
    516         uint16_t msize;
    517         ssize_t ret;
    518 
    519         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    520                     "Awaiting reply at %u (up to %u)\n",
    521                     wpos,
    522                     rend);
    523         ret = recv (dh->sock,
    524                     &buf[off],
    525                     sizeof (buf) - off,
    526                     (finished && (0 == off))
    527                   ? MSG_DONTWAIT
    528                   : 0);
    529         if (ret < 0)
    530         {
    531           if (EINTR == errno)
    532             continue;
    533           if (EAGAIN == errno)
    534           {
    535             GNUNET_assert (finished);
    536             GNUNET_assert (0 == off);
    537             break;
    538           }
    539           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    540                                "recv");
    541           do_disconnect (dh);
    542           return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
    543         }
    544         if (0 == ret)
    545         {
    546           GNUNET_break (0 == off);
    547           if (! finished)
    548             return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    549           if (TALER_EC_NONE == ec)
    550             break;
    551           return ec;
    552         }
    553         off += ret;
    554 more:
    555         if (off < sizeof (struct GNUNET_MessageHeader))
    556           continue;
    557         msize = ntohs (hdr->size);
    558         if (off < msize)
    559           continue;
    560         switch (ntohs (hdr->type))
    561         {
    562         case TALER_HELPER_CS_MT_RES_SIGNATURE:
    563           if (msize != sizeof (struct TALER_CRYPTO_SignResponse))
    564           {
    565             GNUNET_break_op (0);
    566             do_disconnect (dh);
    567             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    568           }
    569           if (finished)
    570           {
    571             GNUNET_break_op (0);
    572             do_disconnect (dh);
    573             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    574           }
    575           {
    576             const struct TALER_CRYPTO_SignResponse *sr =
    577               (const struct TALER_CRYPTO_SignResponse *) buf;
    578             struct GNUNET_CRYPTO_BlindedSignature *blinded_sig;
    579             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    580                         "Received %u signature\n",
    581                         wpos);
    582             blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
    583             blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS;
    584             blinded_sig->rc = 1;
    585             blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b);
    586             blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer;
    587 
    588             bss[wpos].blinded_sig = blinded_sig;
    589             wpos++;
    590             if (wpos == rend)
    591             {
    592               if (TALER_EC_INVALID == ec)
    593                 ec = TALER_EC_NONE;
    594               finished = true;
    595             }
    596             break;
    597           }
    598 
    599         case TALER_HELPER_CS_MT_RES_SIGN_FAILURE:
    600           if (msize != sizeof (struct TALER_CRYPTO_SignFailure))
    601           {
    602             GNUNET_break_op (0);
    603             do_disconnect (dh);
    604             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    605           }
    606           {
    607             const struct TALER_CRYPTO_SignFailure *sf =
    608               (const struct TALER_CRYPTO_SignFailure *) buf;
    609 
    610             ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
    611             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    612                         "Signing %u failed with status %d!\n",
    613                         wpos,
    614                         ec);
    615             wpos++;
    616             if (wpos == rend)
    617             {
    618               finished = true;
    619             }
    620             break;
    621           }
    622         case TALER_HELPER_CS_MT_AVAIL:
    623           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    624                       "Received new key!\n");
    625           if (GNUNET_OK !=
    626               handle_mt_avail (dh,
    627                                hdr))
    628           {
    629             GNUNET_break_op (0);
    630             do_disconnect (dh);
    631             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    632           }
    633           break; /* while(1) loop ensures we recvfrom() again */
    634         case TALER_HELPER_CS_MT_PURGE:
    635           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    636                       "Received revocation!\n");
    637           if (GNUNET_OK !=
    638               handle_mt_purge (dh,
    639                                hdr))
    640           {
    641             GNUNET_break_op (0);
    642             do_disconnect (dh);
    643             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    644           }
    645           break; /* while(1) loop ensures we recvfrom() again */
    646         case TALER_HELPER_CS_SYNCED:
    647           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    648                       "Synchronized add odd time with CS helper!\n");
    649           dh->synced = true;
    650           break;
    651         default:
    652           GNUNET_break_op (0);
    653           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    654                       "Received unexpected message of type %u\n",
    655                       ntohs (hdr->type));
    656           do_disconnect (dh);
    657           return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    658         }
    659         memmove (buf,
    660                  &buf[msize],
    661                  off - msize);
    662         off -= msize;
    663         goto more;
    664       } /* while(1) */
    665     } /* scope */
    666   } /* while (rpos < cdrs_length) */
    667   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    668               "Existing with %u signatures and status %d\n",
    669               wpos,
    670               ec);
    671   return ec;
    672 }
    673 
    674 
    675 enum TALER_ErrorCode
    676 TALER_CRYPTO_helper_cs_r_batch_derive (
    677   struct TALER_CRYPTO_CsDenominationHelper *dh,
    678   unsigned int cdrs_length,
    679   const struct TALER_CRYPTO_CsDeriveRequest cdrs[static cdrs_length],
    680   bool for_melt,
    681   struct GNUNET_CRYPTO_CSPublicRPairP crps[static cdrs_length])
    682 {
    683   enum TALER_ErrorCode ec = TALER_EC_INVALID;
    684   unsigned int rpos;
    685   unsigned int rend;
    686   unsigned int wpos;
    687 
    688   memset (crps,
    689           0,
    690           sizeof (*crps) * cdrs_length);
    691   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    692               "Starting R derivation process\n");
    693   if (GNUNET_OK !=
    694       try_connect (dh))
    695   {
    696     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    697                 "Failed to connect to helper\n");
    698     return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
    699   }
    700 
    701   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    702               "Requesting %u R pairs\n",
    703               cdrs_length);
    704   rpos = 0;
    705   rend = 0;
    706   wpos = 0;
    707   while (rpos < cdrs_length)
    708   {
    709     unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchDeriveRequest);
    710 
    711     while ( (rend < cdrs_length) &&
    712             (mlen + sizeof (struct TALER_CRYPTO_CsRDeriveRequest)
    713              < UINT16_MAX) )
    714     {
    715       mlen += sizeof (struct TALER_CRYPTO_CsRDeriveRequest);
    716       rend++;
    717     }
    718     {
    719       char obuf[mlen] GNUNET_ALIGN;
    720       struct TALER_CRYPTO_BatchDeriveRequest *bdr
    721         = (struct TALER_CRYPTO_BatchDeriveRequest *) obuf;
    722       void *wbuf;
    723 
    724       bdr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE);
    725       bdr->header.size = htons (mlen);
    726       bdr->batch_size = htonl (rend - rpos);
    727       wbuf = &bdr[1];
    728       for (unsigned int i = rpos; i<rend; i++)
    729       {
    730         struct TALER_CRYPTO_CsRDeriveRequest *rdr = wbuf;
    731         const struct TALER_CRYPTO_CsDeriveRequest *cdr = &cdrs[i];
    732 
    733         rdr->header.size = htons (sizeof (*rdr));
    734         rdr->header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE);
    735         rdr->for_melt = htonl (for_melt ? 1 : 0);
    736         rdr->h_cs = *cdr->h_cs;
    737         rdr->nonce = *cdr->nonce;
    738         wbuf += sizeof (*rdr);
    739       }
    740       GNUNET_assert (wbuf == &obuf[mlen]);
    741       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    742                   "Sending batch request [%u-%u)\n",
    743                   rpos,
    744                   rend);
    745       if (GNUNET_OK !=
    746           TALER_crypto_helper_send_all (dh->sock,
    747                                         obuf,
    748                                         sizeof (obuf)))
    749       {
    750         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    751                              "send");
    752         do_disconnect (dh);
    753         return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
    754       }
    755     } /* end of obuf scope */
    756     rpos = rend;
    757     {
    758       char buf[UINT16_MAX];
    759       size_t off = 0;
    760       const struct GNUNET_MessageHeader *hdr
    761         = (const struct GNUNET_MessageHeader *) buf;
    762       bool finished = false;
    763 
    764       while (1)
    765       {
    766         uint16_t msize;
    767         ssize_t ret;
    768 
    769         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    770                     "Awaiting reply at %u (up to %u)\n",
    771                     wpos,
    772                     rend);
    773         ret = recv (dh->sock,
    774                     &buf[off],
    775                     sizeof (buf) - off,
    776                     (finished && (0 == off))
    777                   ? MSG_DONTWAIT
    778                   : 0);
    779         if (ret < 0)
    780         {
    781           if (EINTR == errno)
    782             continue;
    783           if (EAGAIN == errno)
    784           {
    785             GNUNET_assert (finished);
    786             GNUNET_assert (0 == off);
    787             break;
    788           }
    789           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    790                                "recv");
    791           do_disconnect (dh);
    792           return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
    793         }
    794         if (0 == ret)
    795         {
    796           GNUNET_break (0 == off);
    797           if (! finished)
    798             return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    799           if (TALER_EC_NONE == ec)
    800             break;
    801           return ec;
    802         }
    803         off += ret;
    804 more:
    805         if (off < sizeof (struct GNUNET_MessageHeader))
    806           continue;
    807         msize = ntohs (hdr->size);
    808         if (off < msize)
    809           continue;
    810         switch (ntohs (hdr->type))
    811         {
    812         case TALER_HELPER_CS_MT_RES_RDERIVE:
    813           if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse))
    814           {
    815             GNUNET_break_op (0);
    816             do_disconnect (dh);
    817             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    818           }
    819           if (finished)
    820           {
    821             GNUNET_break_op (0);
    822             do_disconnect (dh);
    823             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    824           }
    825           {
    826             const struct TALER_CRYPTO_RDeriveResponse *rdr =
    827               (const struct TALER_CRYPTO_RDeriveResponse *) buf;
    828 
    829             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    830                         "Received %u R pair\n",
    831                         wpos);
    832             crps[wpos] = rdr->r_pub;
    833             wpos++;
    834             if (wpos == rend)
    835             {
    836               if (TALER_EC_INVALID == ec)
    837                 ec = TALER_EC_NONE;
    838               finished = true;
    839             }
    840             break;
    841           }
    842         case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE:
    843           if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure))
    844           {
    845             GNUNET_break_op (0);
    846             do_disconnect (dh);
    847             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    848           }
    849           {
    850             const struct TALER_CRYPTO_RDeriveFailure *rdf =
    851               (const struct TALER_CRYPTO_RDeriveFailure *) buf;
    852 
    853             ec = (enum TALER_ErrorCode) (int) ntohl (rdf->ec);
    854             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    855                         "R derivation %u failed with status %d!\n",
    856                         wpos,
    857                         ec);
    858             wpos++;
    859             if (wpos == rend)
    860             {
    861               finished = true;
    862             }
    863             break;
    864           }
    865         case TALER_HELPER_CS_MT_AVAIL:
    866           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    867                       "Received new key!\n");
    868           if (GNUNET_OK !=
    869               handle_mt_avail (dh,
    870                                hdr))
    871           {
    872             GNUNET_break_op (0);
    873             do_disconnect (dh);
    874             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    875           }
    876           break; /* while(1) loop ensures we recvfrom() again */
    877         case TALER_HELPER_CS_MT_PURGE:
    878           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    879                       "Received revocation!\n");
    880           if (GNUNET_OK !=
    881               handle_mt_purge (dh,
    882                                hdr))
    883           {
    884             GNUNET_break_op (0);
    885             do_disconnect (dh);
    886             return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    887           }
    888           break; /* while(1) loop ensures we recvfrom() again */
    889         case TALER_HELPER_CS_SYNCED:
    890           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    891                       "Synchronized add odd time with CS helper!\n");
    892           dh->synced = true;
    893           break;
    894         default:
    895           GNUNET_break_op (0);
    896           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    897                       "Received unexpected message of type %u\n",
    898                       ntohs (hdr->type));
    899           do_disconnect (dh);
    900           return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
    901         }
    902         memmove (buf,
    903                  &buf[msize],
    904                  off - msize);
    905         off -= msize;
    906         goto more;
    907       } /* while(1) */
    908     } /* scope */
    909   } /* while (rpos < cdrs_length) */
    910   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    911               "Existing with %u signatures and status %d\n",
    912               wpos,
    913               ec);
    914   return ec;
    915 }
    916 
    917 
    918 void
    919 TALER_CRYPTO_helper_cs_disconnect (
    920   struct TALER_CRYPTO_CsDenominationHelper *dh)
    921 {
    922   if (-1 != dh->sock)
    923     do_disconnect (dh);
    924   GNUNET_free (dh);
    925 }
    926 
    927 
    928 /* end of crypto_helper_cs.c */