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 (15780B)


      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 >= sizeof (*purpose));
    374   GNUNET_assert (purpose_size <
    375                  UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
    376   {
    377     char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
    378              - sizeof (struct GNUNET_CRYPTO_SignaturePurpose)];
    379     struct TALER_CRYPTO_EddsaSignRequest *sr
    380       = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
    381 
    382     sr->header.size = htons (sizeof (buf));
    383     sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
    384     sr->reserved = htonl (0);
    385     GNUNET_memcpy (&sr->purpose,
    386                    purpose,
    387                    purpose_size);
    388     if (GNUNET_OK !=
    389         TALER_crypto_helper_send_all (esh->sock,
    390                                       buf,
    391                                       sizeof (buf)))
    392     {
    393       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    394                                 "send",
    395                                 esh->sa.sun_path);
    396       do_disconnect (esh);
    397       return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
    398     }
    399   }
    400 
    401   {
    402     char buf[UINT16_MAX];
    403     size_t off = 0;
    404     const struct GNUNET_MessageHeader *hdr
    405       = (const struct GNUNET_MessageHeader *) buf;
    406     bool finished = false;
    407     enum TALER_ErrorCode ec = TALER_EC_INVALID;
    408 
    409     while (1)
    410     {
    411       ssize_t ret;
    412       uint16_t msize;
    413 
    414       ret = recv (esh->sock,
    415                   &buf[off],
    416                   sizeof (buf) - off,
    417                   (finished && (0 == off))
    418                   ? MSG_DONTWAIT
    419                   : 0);
    420       if (ret < 0)
    421       {
    422         if (EINTR == errno)
    423           continue;
    424         if (EAGAIN == errno)
    425         {
    426           GNUNET_assert (finished);
    427           GNUNET_assert (0 == off);
    428           break;
    429         }
    430         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    431                              "recv");
    432         do_disconnect (esh);
    433         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
    434       }
    435       if (0 == ret)
    436       {
    437         /* helper closed the connection */
    438         GNUNET_break (0 == off);
    439         do_disconnect (esh);
    440         if (! finished)
    441           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    442         if (TALER_EC_NONE == ec)
    443           break;
    444         return ec;
    445       }
    446       off += ret;
    447 more:
    448       if (off < sizeof (struct GNUNET_MessageHeader))
    449         continue;
    450       msize = ntohs (hdr->size);
    451       if (msize < sizeof (struct GNUNET_MessageHeader))
    452       {
    453         GNUNET_break_op (0);
    454         do_disconnect (esh);
    455         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    456       }
    457       if (off < msize)
    458         continue;
    459       switch (ntohs (hdr->type))
    460       {
    461       case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
    462         if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
    463         {
    464           GNUNET_break_op (0);
    465           do_disconnect (esh);
    466           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    467         }
    468         if (finished)
    469         {
    470           GNUNET_break_op (0);
    471           do_disconnect (esh);
    472           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    473         }
    474         {
    475           const struct TALER_CRYPTO_EddsaSignResponse *sr =
    476             (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
    477           *exchange_sig = sr->exchange_sig;
    478           *exchange_pub = sr->exchange_pub;
    479           finished = true;
    480           ec = TALER_EC_NONE;
    481           break;
    482         }
    483       case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
    484         if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
    485         {
    486           GNUNET_break_op (0);
    487           do_disconnect (esh);
    488           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    489         }
    490         {
    491           const struct TALER_CRYPTO_EddsaSignFailure *sf =
    492             (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
    493 
    494           finished = true;
    495           ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
    496           break;
    497         }
    498       case TALER_HELPER_EDDSA_MT_AVAIL:
    499         if (GNUNET_OK !=
    500             handle_mt_avail (esh,
    501                              hdr))
    502         {
    503           GNUNET_break_op (0);
    504           do_disconnect (esh);
    505           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    506         }
    507         break; /* while(1) loop ensures we recv() again */
    508       case TALER_HELPER_EDDSA_MT_PURGE:
    509         if (GNUNET_OK !=
    510             handle_mt_purge (esh,
    511                              hdr))
    512         {
    513           GNUNET_break_op (0);
    514           do_disconnect (esh);
    515           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    516         }
    517         break; /* while(1) loop ensures we recv() again */
    518       case TALER_HELPER_EDDSA_SYNCED:
    519         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    520                     "Synchronized add odd time with EdDSA helper!\n");
    521         esh->synced = true;
    522         break;
    523       default:
    524         GNUNET_break_op (0);
    525         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    526                     "Received unexpected message of type %u\n",
    527                     ntohs (hdr->type));
    528         do_disconnect (esh);
    529         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
    530       }
    531       memmove (buf,
    532                &buf[msize],
    533                off - msize);
    534       off -= msize;
    535       goto more;
    536     } /* while(1) */
    537     return ec;
    538   }
    539 }
    540 
    541 
    542 void
    543 TALER_CRYPTO_helper_esign_revoke (
    544   struct TALER_CRYPTO_ExchangeSignHelper *esh,
    545   const struct TALER_ExchangePublicKeyP *exchange_pub)
    546 {
    547   if (GNUNET_OK !=
    548       try_connect (esh))
    549     return; /* give up */
    550   {
    551     struct TALER_CRYPTO_EddsaRevokeRequest rr = {
    552       .header.size = htons (sizeof (rr)),
    553       .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
    554       .exchange_pub = *exchange_pub
    555     };
    556 
    557     if (GNUNET_OK !=
    558         TALER_crypto_helper_send_all (esh->sock,
    559                                       &rr,
    560                                       sizeof (rr)))
    561     {
    562       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    563                            "send");
    564       do_disconnect (esh);
    565       return;
    566     }
    567   }
    568 }
    569 
    570 
    571 void
    572 TALER_CRYPTO_helper_esign_disconnect (
    573   struct TALER_CRYPTO_ExchangeSignHelper *esh)
    574 {
    575   if (-1 != esh->sock)
    576     do_disconnect (esh);
    577   GNUNET_free (esh);
    578 }
    579 
    580 
    581 /* end of crypto_helper_esign.c */