exchange

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

crypto_helper_esign.c (15825B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020, 2021 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_esign.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_eddsa.h"
     25 #include <poll.h>
     26 #include "crypto_helper_common.h"
     27 
     28 
     29 struct TALER_CRYPTO_ExchangeSignHelper
     30 {
     31   /**
     32    * Function to call with updates to available key material.
     33    */
     34   TALER_CRYPTO_ExchangeKeyStatusCallback ekc;
     35 
     36   /**
     37    * Closure for @e ekc
     38    */
     39   void *ekc_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 reached the sync'ed state?
     54    */
     55   bool synced;
     56 
     57 };
     58 
     59 
     60 /**
     61  * Disconnect from the helper process.  Updates
     62  * @e sock field in @a esh.
     63  *
     64  * @param[in,out] esh handle to tear down connection of
     65  */
     66 static void
     67 do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
     68 {
     69   GNUNET_break (0 == close (esh->sock));
     70   esh->sock = -1;
     71   esh->synced = false;
     72 }
     73 
     74 
     75 /**
     76  * Try to connect to the helper process.  Updates
     77  * @e sock field in @a esh.
     78  *
     79  * @param[in,out] esh handle to establish connection for
     80  * @return #GNUNET_OK on success
     81  */
     82 static enum GNUNET_GenericReturnValue
     83 try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
     84 {
     85   if (-1 != esh->sock)
     86     return GNUNET_OK;
     87   esh->sock = socket (AF_UNIX,
     88                       SOCK_STREAM,
     89                       0);
     90   if (-1 == esh->sock)
     91   {
     92     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     93                          "socket");
     94     return GNUNET_SYSERR;
     95   }
     96   if (0 !=
     97       connect (esh->sock,
     98                (const struct sockaddr *) &esh->sa,
     99                sizeof (esh->sa)))
    100   {
    101     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    102                               "connect",
    103                               esh->sa.sun_path);
    104     do_disconnect (esh);
    105     return GNUNET_SYSERR;
    106   }
    107   return GNUNET_OK;
    108 }
    109 
    110 
    111 struct TALER_CRYPTO_ExchangeSignHelper *
    112 TALER_CRYPTO_helper_esign_connect (
    113   const struct GNUNET_CONFIGURATION_Handle *cfg,
    114   const char *section,
    115   TALER_CRYPTO_ExchangeKeyStatusCallback ekc,
    116   void *ekc_cls)
    117 {
    118   struct TALER_CRYPTO_ExchangeSignHelper *esh;
    119   char *unixpath;
    120   char *secname;
    121 
    122   GNUNET_asprintf (&secname,
    123                    "%s-secmod-eddsa",
    124                    section);
    125 
    126   if (GNUNET_OK !=
    127       GNUNET_CONFIGURATION_get_value_filename (cfg,
    128                                                secname,
    129                                                "UNIXPATH",
    130                                                &unixpath))
    131   {
    132     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    133                                secname,
    134                                "UNIXPATH");
    135     GNUNET_free (secname);
    136     return NULL;
    137   }
    138   /* we use >= here because we want the sun_path to always
    139      be 0-terminated */
    140   if (strlen (unixpath) >= sizeof (esh->sa.sun_path))
    141   {
    142     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    143                                secname,
    144                                "UNIXPATH",
    145                                "path too long");
    146     GNUNET_free (unixpath);
    147     GNUNET_free (secname);
    148     return NULL;
    149   }
    150   GNUNET_free (secname);
    151   esh = GNUNET_new (struct TALER_CRYPTO_ExchangeSignHelper);
    152   esh->ekc = ekc;
    153   esh->ekc_cls = ekc_cls;
    154   esh->sa.sun_family = AF_UNIX;
    155   strncpy (esh->sa.sun_path,
    156            unixpath,
    157            sizeof (esh->sa.sun_path) - 1);
    158   GNUNET_free (unixpath);
    159   esh->sock = -1;
    160   if (GNUNET_OK !=
    161       try_connect (esh))
    162   {
    163     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    164                 "Could not connect to %s. Will keep trying\n",
    165                 "taler-exchange-helper-secmod-eddsa");
    166     return esh;
    167   }
    168   TALER_CRYPTO_helper_esign_poll (esh);
    169   return esh;
    170 }
    171 
    172 
    173 /**
    174  * Handle a #TALER_HELPER_EDDSA_MT_AVAIL message from the helper.
    175  *
    176  * @param esh helper context
    177  * @param hdr message that we received
    178  * @return #GNUNET_OK on success
    179  */
    180 static enum GNUNET_GenericReturnValue
    181 handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh,
    182                  const struct GNUNET_MessageHeader *hdr)
    183 {
    184   const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan
    185     = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr;
    186 
    187   if (sizeof (*kan) != ntohs (hdr->size))
    188   {
    189     GNUNET_break_op (0);
    190     return GNUNET_SYSERR;
    191   }
    192   if (GNUNET_OK !=
    193       TALER_exchange_secmod_eddsa_verify (
    194         &kan->exchange_pub,
    195         GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
    196         GNUNET_TIME_relative_ntoh (kan->duration),
    197         &kan->secm_pub,
    198         &kan->secm_sig))
    199   {
    200     GNUNET_break_op (0);
    201     return GNUNET_SYSERR;
    202   }
    203   esh->ekc (esh->ekc_cls,
    204             GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
    205             GNUNET_TIME_relative_ntoh (kan->duration),
    206             &kan->exchange_pub,
    207             &kan->secm_pub,
    208             &kan->secm_sig);
    209   return GNUNET_OK;
    210 }
    211 
    212 
    213 /**
    214  * Handle a #TALER_HELPER_EDDSA_MT_PURGE message from the helper.
    215  *
    216  * @param esh helper context
    217  * @param hdr message that we received
    218  * @return #GNUNET_OK on success
    219  */
    220 static enum GNUNET_GenericReturnValue
    221 handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper *esh,
    222                  const struct GNUNET_MessageHeader *hdr)
    223 {
    224   const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn
    225     = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr;
    226 
    227   if (sizeof (*pn) != ntohs (hdr->size))
    228   {
    229     GNUNET_break_op (0);
    230     return GNUNET_SYSERR;
    231   }
    232   esh->ekc (esh->ekc_cls,
    233             GNUNET_TIME_UNIT_ZERO_TS,
    234             GNUNET_TIME_UNIT_ZERO,
    235             &pn->exchange_pub,
    236             NULL,
    237             NULL);
    238   return GNUNET_OK;
    239 }
    240 
    241 
    242 void
    243 TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh)
    244 {
    245   char buf[UINT16_MAX];
    246   size_t off = 0;
    247   unsigned int retry_limit = 3;
    248   const struct GNUNET_MessageHeader *hdr
    249     = (const struct GNUNET_MessageHeader *) buf;
    250 
    251   if (GNUNET_OK !=
    252       try_connect (esh))
    253     return; /* give up */
    254   while (1)
    255   {
    256     uint16_t msize;
    257     ssize_t ret;
    258 
    259     ret = recv (esh->sock,
    260                 buf + off,
    261                 sizeof (buf) - off,
    262                 (esh->synced && (0 == off))
    263                 ? MSG_DONTWAIT
    264                 : 0);
    265     if (ret < 0)
    266     {
    267       if (EINTR == errno)
    268         continue;
    269       if (EAGAIN == errno)
    270       {
    271         GNUNET_assert (esh->synced);
    272         GNUNET_assert (0 == off);
    273         break;
    274       }
    275       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    276                            "recv");
    277       do_disconnect (esh);
    278       if (0 == retry_limit)
    279         return; /* give up */
    280       if (GNUNET_OK !=
    281           try_connect (esh))
    282         return; /* give up */
    283       retry_limit--;
    284       continue;
    285     }
    286     if (0 == ret)
    287     {
    288       GNUNET_break (0 == off);
    289       return;
    290     }
    291     off += ret;
    292 more:
    293     if (off < sizeof (struct GNUNET_MessageHeader))
    294       continue;
    295     msize = ntohs (hdr->size);
    296     if (msize < sizeof (struct GNUNET_MessageHeader))
    297     {
    298       GNUNET_break_op (0);
    299       do_disconnect (esh);
    300       return;
    301     }
    302     if (off < msize)
    303       continue;
    304     switch (ntohs (hdr->type))
    305     {
    306     case TALER_HELPER_EDDSA_MT_AVAIL:
    307       if (GNUNET_OK !=
    308           handle_mt_avail (esh,
    309                            hdr))
    310       {
    311         GNUNET_break_op (0);
    312         do_disconnect (esh);
    313         return;
    314       }
    315       break;
    316     case TALER_HELPER_EDDSA_MT_PURGE:
    317       if (GNUNET_OK !=
    318           handle_mt_purge (esh,
    319                            hdr))
    320       {
    321         GNUNET_break_op (0);
    322         do_disconnect (esh);
    323         return;
    324       }
    325       break;
    326     case TALER_HELPER_EDDSA_SYNCED:
    327       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    328                   "Now synchronized with EdDSA helper\n");
    329       esh->synced = true;
    330       break;
    331     default:
    332       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    333                   "Received unexpected message of type %d (len: %u)\n",
    334                   (unsigned int) ntohs (hdr->type),
    335                   (unsigned int) msize);
    336       GNUNET_break_op (0);
    337       do_disconnect (esh);
    338       return;
    339     }
    340     memmove (buf,
    341              &buf[msize],
    342              off - msize);
    343     off -= msize;
    344     goto more;
    345   }
    346 }
    347 
    348 
    349 enum TALER_ErrorCode
    350 TALER_CRYPTO_helper_esign_sign_ (
    351   struct TALER_CRYPTO_ExchangeSignHelper *esh,
    352   const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
    353   struct TALER_ExchangePublicKeyP *exchange_pub,
    354   struct TALER_ExchangeSignatureP *exchange_sig)
    355 {
    356   uint32_t purpose_size = ntohl (purpose->size);
    357 
    358   /* clear the outputs, so that we never return
    359      uninitialized values on failures */
    360   memset (exchange_pub,
    361           0,
    362           sizeof (*exchange_pub));
    363   memset (exchange_sig,
    364           0,
    365           sizeof (*exchange_sig));
    366   if (GNUNET_OK !=
    367       try_connect (esh))
    368   {
    369     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    370                 "Failed to connect to helper\n");
    371     return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
    372   }
    373   GNUNET_assert (purpose_size >=
    374                  sizeof (struct TALER_CRYPTO_EddsaSignRequest));
    375   GNUNET_assert (purpose_size <
    376                  UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
    377   {
    378     char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
    379              - sizeof (struct GNUNET_CRYPTO_SignaturePurpose)];
    380     struct TALER_CRYPTO_EddsaSignRequest *sr
    381       = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
    382 
    383     sr->header.size = htons (sizeof (buf));
    384     sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
    385     sr->reserved = htonl (0);
    386     GNUNET_memcpy (&sr->purpose,
    387                    purpose,
    388                    purpose_size);
    389     if (GNUNET_OK !=
    390         TALER_crypto_helper_send_all (esh->sock,
    391                                       buf,
    392                                       sizeof (buf)))
    393     {
    394       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    395                                 "send",
    396                                 esh->sa.sun_path);
    397       do_disconnect (esh);
    398       return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
    399     }
    400   }
    401 
    402   {
    403     char buf[UINT16_MAX];
    404     size_t off = 0;
    405     const struct GNUNET_MessageHeader *hdr
    406       = (const struct GNUNET_MessageHeader *) buf;
    407     bool finished = false;
    408     enum TALER_ErrorCode ec = TALER_EC_INVALID;
    409 
    410     while (1)
    411     {
    412       ssize_t ret;
    413       uint16_t msize;
    414 
    415       ret = recv (esh->sock,
    416                   &buf[off],
    417                   sizeof (buf) - off,
    418                   (finished && (0 == off))
    419                   ? MSG_DONTWAIT
    420                   : 0);
    421       if (ret < 0)
    422       {
    423         if (EINTR == errno)
    424           continue;
    425         if (EAGAIN == errno)
    426         {
    427           GNUNET_assert (finished);
    428           GNUNET_assert (0 == off);
    429           break;
    430         }
    431         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    432                              "recv");
    433         do_disconnect (esh);
    434         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
    435       }
    436       if (0 == ret)
    437       {
    438         /* helper closed the connection */
    439         GNUNET_break (0 == off);
    440         do_disconnect (esh);
    441         if (! finished)
    442           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    443         if (TALER_EC_NONE == ec)
    444           break;
    445         return ec;
    446       }
    447       off += ret;
    448 more:
    449       if (off < sizeof (struct GNUNET_MessageHeader))
    450         continue;
    451       msize = ntohs (hdr->size);
    452       if (msize < sizeof (struct GNUNET_MessageHeader))
    453       {
    454         GNUNET_break_op (0);
    455         do_disconnect (esh);
    456         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    457       }
    458       if (off < msize)
    459         continue;
    460       switch (ntohs (hdr->type))
    461       {
    462       case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
    463         if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
    464         {
    465           GNUNET_break_op (0);
    466           do_disconnect (esh);
    467           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    468         }
    469         if (finished)
    470         {
    471           GNUNET_break_op (0);
    472           do_disconnect (esh);
    473           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    474         }
    475         {
    476           const struct TALER_CRYPTO_EddsaSignResponse *sr =
    477             (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
    478           *exchange_sig = sr->exchange_sig;
    479           *exchange_pub = sr->exchange_pub;
    480           finished = true;
    481           ec = TALER_EC_NONE;
    482           break;
    483         }
    484       case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
    485         if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
    486         {
    487           GNUNET_break_op (0);
    488           do_disconnect (esh);
    489           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    490         }
    491         {
    492           const struct TALER_CRYPTO_EddsaSignFailure *sf =
    493             (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
    494 
    495           finished = true;
    496           ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
    497           break;
    498         }
    499       case TALER_HELPER_EDDSA_MT_AVAIL:
    500         if (GNUNET_OK !=
    501             handle_mt_avail (esh,
    502                              hdr))
    503         {
    504           GNUNET_break_op (0);
    505           do_disconnect (esh);
    506           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    507         }
    508         break; /* while(1) loop ensures we recv() again */
    509       case TALER_HELPER_EDDSA_MT_PURGE:
    510         if (GNUNET_OK !=
    511             handle_mt_purge (esh,
    512                              hdr))
    513         {
    514           GNUNET_break_op (0);
    515           do_disconnect (esh);
    516           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    517         }
    518         break; /* while(1) loop ensures we recv() again */
    519       case TALER_HELPER_EDDSA_SYNCED:
    520         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    521                     "Synchronized add odd time with EdDSA helper!\n");
    522         esh->synced = true;
    523         break;
    524       default:
    525         GNUNET_break_op (0);
    526         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    527                     "Received unexpected message of type %u\n",
    528                     ntohs (hdr->type));
    529         do_disconnect (esh);
    530         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    531       }
    532       memmove (buf,
    533                &buf[msize],
    534                off - msize);
    535       off -= msize;
    536       goto more;
    537     } /* while(1) */
    538     return ec;
    539   }
    540 }
    541 
    542 
    543 void
    544 TALER_CRYPTO_helper_esign_revoke (
    545   struct TALER_CRYPTO_ExchangeSignHelper *esh,
    546   const struct TALER_ExchangePublicKeyP *exchange_pub)
    547 {
    548   if (GNUNET_OK !=
    549       try_connect (esh))
    550     return; /* give up */
    551   {
    552     struct TALER_CRYPTO_EddsaRevokeRequest rr = {
    553       .header.size = htons (sizeof (rr)),
    554       .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
    555       .exchange_pub = *exchange_pub
    556     };
    557 
    558     if (GNUNET_OK !=
    559         TALER_crypto_helper_send_all (esh->sock,
    560                                       &rr,
    561                                       sizeof (rr)))
    562     {
    563       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    564                            "send");
    565       do_disconnect (esh);
    566       return;
    567     }
    568   }
    569 }
    570 
    571 
    572 void
    573 TALER_CRYPTO_helper_esign_disconnect (
    574   struct TALER_CRYPTO_ExchangeSignHelper *esh)
    575 {
    576   if (-1 != esh->sock)
    577     do_disconnect (esh);
    578   GNUNET_free (esh);
    579 }
    580 
    581 
    582 /* end of crypto_helper_esign.c */