LCOV - code coverage report
Current view: top level - util - crypto_helper_esign.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 61.4 % 249 153
Test Date: 2026-01-09 13:26:54 Functions: 100.0 % 9 9

            Line data    Source code
       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 "taler/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           27 : do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
      68              : {
      69           27 :   GNUNET_break (0 == close (esh->sock));
      70           27 :   esh->sock = -1;
      71           27 :   esh->synced = false;
      72           27 : }
      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         3500 : try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
      84              : {
      85         3500 :   if (-1 != esh->sock)
      86         3473 :     return GNUNET_OK;
      87           27 :   esh->sock = socket (AF_UNIX,
      88              :                       SOCK_STREAM,
      89              :                       0);
      90           27 :   if (-1 == esh->sock)
      91              :   {
      92            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
      93              :                          "socket");
      94            0 :     return GNUNET_SYSERR;
      95              :   }
      96           27 :   if (0 !=
      97           27 :       connect (esh->sock,
      98           27 :                (const struct sockaddr *) &esh->sa,
      99              :                sizeof (esh->sa)))
     100              :   {
     101            1 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     102              :                               "connect",
     103              :                               esh->sa.sun_path);
     104            1 :     do_disconnect (esh);
     105            1 :     return GNUNET_SYSERR;
     106              :   }
     107           26 :   return GNUNET_OK;
     108              : }
     109              : 
     110              : 
     111              : struct TALER_CRYPTO_ExchangeSignHelper *
     112           27 : 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           27 :   GNUNET_asprintf (&secname,
     123              :                    "%s-secmod-eddsa",
     124              :                    section);
     125              : 
     126           27 :   if (GNUNET_OK !=
     127           27 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     128              :                                                secname,
     129              :                                                "UNIXPATH",
     130              :                                                &unixpath))
     131              :   {
     132            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     133              :                                secname,
     134              :                                "UNIXPATH");
     135            0 :     GNUNET_free (secname);
     136            0 :     return NULL;
     137              :   }
     138              :   /* we use >= here because we want the sun_path to always
     139              :      be 0-terminated */
     140           27 :   if (strlen (unixpath) >= sizeof (esh->sa.sun_path))
     141              :   {
     142            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     143              :                                secname,
     144              :                                "UNIXPATH",
     145              :                                "path too long");
     146            0 :     GNUNET_free (unixpath);
     147            0 :     GNUNET_free (secname);
     148            0 :     return NULL;
     149              :   }
     150           27 :   GNUNET_free (secname);
     151           27 :   esh = GNUNET_new (struct TALER_CRYPTO_ExchangeSignHelper);
     152           27 :   esh->ekc = ekc;
     153           27 :   esh->ekc_cls = ekc_cls;
     154           27 :   esh->sa.sun_family = AF_UNIX;
     155           27 :   strncpy (esh->sa.sun_path,
     156              :            unixpath,
     157              :            sizeof (esh->sa.sun_path) - 1);
     158           27 :   GNUNET_free (unixpath);
     159           27 :   esh->sock = -1;
     160           27 :   if (GNUNET_OK !=
     161           27 :       try_connect (esh))
     162              :   {
     163            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     164              :                 "Could not connect to %s. Will keep trying\n",
     165              :                 "taler-exchange-helper-secmod-eddsa");
     166            1 :     return esh;
     167              :   }
     168           26 :   TALER_CRYPTO_helper_esign_poll (esh);
     169           26 :   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          138 : handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh,
     182              :                  const struct GNUNET_MessageHeader *hdr)
     183              : {
     184          138 :   const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan
     185              :     = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr;
     186              : 
     187          138 :   if (sizeof (*kan) != ntohs (hdr->size))
     188              :   {
     189            0 :     GNUNET_break_op (0);
     190            0 :     return GNUNET_SYSERR;
     191              :   }
     192          138 :   if (GNUNET_OK !=
     193          138 :       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            0 :     GNUNET_break_op (0);
     201            0 :     return GNUNET_SYSERR;
     202              :   }
     203          138 :   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          138 :   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            3 : handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper *esh,
     222              :                  const struct GNUNET_MessageHeader *hdr)
     223              : {
     224            3 :   const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn
     225              :     = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr;
     226              : 
     227            3 :   if (sizeof (*pn) != ntohs (hdr->size))
     228              :   {
     229            0 :     GNUNET_break_op (0);
     230            0 :     return GNUNET_SYSERR;
     231              :   }
     232            3 :   esh->ekc (esh->ekc_cls,
     233            3 :             GNUNET_TIME_UNIT_ZERO_TS,
     234            3 :             GNUNET_TIME_UNIT_ZERO,
     235              :             &pn->exchange_pub,
     236              :             NULL,
     237              :             NULL);
     238            3 :   return GNUNET_OK;
     239              : }
     240              : 
     241              : 
     242              : void
     243         1728 : TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh)
     244              : {
     245              :   char buf[UINT16_MAX];
     246         1728 :   size_t off = 0;
     247         1728 :   unsigned int retry_limit = 3;
     248         1728 :   const struct GNUNET_MessageHeader *hdr
     249              :     = (const struct GNUNET_MessageHeader *) buf;
     250              : 
     251         1728 :   if (GNUNET_OK !=
     252         1728 :       try_connect (esh))
     253            0 :     return; /* give up */
     254              :   while (1)
     255           86 :   {
     256              :     uint16_t msize;
     257              :     ssize_t ret;
     258              : 
     259         1814 :     ret = recv (esh->sock,
     260              :                 buf + off,
     261              :                 sizeof (buf) - off,
     262         1814 :                 (esh->synced && (0 == off))
     263              :                 ? MSG_DONTWAIT
     264              :                 : 0);
     265         1814 :     if (ret < 0)
     266              :     {
     267         1728 :       if (EINTR == errno)
     268            0 :         continue;
     269         1728 :       if (EAGAIN == errno)
     270              :       {
     271         1728 :         GNUNET_assert (esh->synced);
     272         1728 :         GNUNET_assert (0 == off);
     273         1728 :         break;
     274              :       }
     275            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     276              :                            "recv");
     277            0 :       do_disconnect (esh);
     278            0 :       if (0 == retry_limit)
     279            0 :         return; /* give up */
     280            0 :       if (GNUNET_OK !=
     281            0 :           try_connect (esh))
     282            0 :         return; /* give up */
     283            0 :       retry_limit--;
     284            0 :       continue;
     285              :     }
     286           86 :     if (0 == ret)
     287              :     {
     288            0 :       GNUNET_break (0 == off);
     289            0 :       return;
     290              :     }
     291           86 :     off += ret;
     292          253 : more:
     293          253 :     if (off < sizeof (struct GNUNET_MessageHeader))
     294           86 :       continue;
     295          167 :     msize = ntohs (hdr->size);
     296          167 :     if (off < msize)
     297            0 :       continue;
     298          167 :     switch (ntohs (hdr->type))
     299              :     {
     300          138 :     case TALER_HELPER_EDDSA_MT_AVAIL:
     301          138 :       if (GNUNET_OK !=
     302          138 :           handle_mt_avail (esh,
     303              :                            hdr))
     304              :       {
     305            0 :         GNUNET_break_op (0);
     306            0 :         do_disconnect (esh);
     307            0 :         return;
     308              :       }
     309          138 :       break;
     310            3 :     case TALER_HELPER_EDDSA_MT_PURGE:
     311            3 :       if (GNUNET_OK !=
     312            3 :           handle_mt_purge (esh,
     313              :                            hdr))
     314              :       {
     315            0 :         GNUNET_break_op (0);
     316            0 :         do_disconnect (esh);
     317            0 :         return;
     318              :       }
     319            3 :       break;
     320           26 :     case TALER_HELPER_EDDSA_SYNCED:
     321           26 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     322              :                   "Now synchronized with EdDSA helper\n");
     323           26 :       esh->synced = true;
     324           26 :       break;
     325            0 :     default:
     326            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     327              :                   "Received unexpected message of type %d (len: %u)\n",
     328              :                   (unsigned int) ntohs (hdr->type),
     329              :                   (unsigned int) msize);
     330            0 :       GNUNET_break_op (0);
     331            0 :       do_disconnect (esh);
     332            0 :       return;
     333              :     }
     334          167 :     memmove (buf,
     335          167 :              &buf[msize],
     336              :              off - msize);
     337          167 :     off -= msize;
     338          167 :     goto more;
     339              :   }
     340              : }
     341              : 
     342              : 
     343              : enum TALER_ErrorCode
     344         1742 : TALER_CRYPTO_helper_esign_sign_ (
     345              :   struct TALER_CRYPTO_ExchangeSignHelper *esh,
     346              :   const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
     347              :   struct TALER_ExchangePublicKeyP *exchange_pub,
     348              :   struct TALER_ExchangeSignatureP *exchange_sig)
     349              : {
     350         1742 :   uint32_t purpose_size = ntohl (purpose->size);
     351              : 
     352         1742 :   if (GNUNET_OK !=
     353         1742 :       try_connect (esh))
     354              :   {
     355            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     356              :                 "Failed to connect to helper\n");
     357            0 :     return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
     358              :   }
     359         1742 :   GNUNET_assert (purpose_size <
     360              :                  UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
     361         1742 :   {
     362         1742 :     char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
     363         1742 :              - sizeof (struct GNUNET_CRYPTO_SignaturePurpose)];
     364         1742 :     struct TALER_CRYPTO_EddsaSignRequest *sr
     365              :       = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
     366              : 
     367         1742 :     sr->header.size = htons (sizeof (buf));
     368         1742 :     sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
     369         1742 :     sr->reserved = htonl (0);
     370         1742 :     GNUNET_memcpy (&sr->purpose,
     371              :                    purpose,
     372              :                    purpose_size);
     373         1742 :     if (GNUNET_OK !=
     374         1742 :         TALER_crypto_helper_send_all (esh->sock,
     375              :                                       buf,
     376              :                                       sizeof (buf)))
     377              :     {
     378            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     379              :                                 "send",
     380              :                                 esh->sa.sun_path);
     381            0 :       do_disconnect (esh);
     382            0 :       return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
     383              :     }
     384              :   }
     385              : 
     386              :   {
     387              :     char buf[UINT16_MAX];
     388         1742 :     size_t off = 0;
     389         1742 :     const struct GNUNET_MessageHeader *hdr
     390              :       = (const struct GNUNET_MessageHeader *) buf;
     391         1742 :     bool finished = false;
     392         1742 :     enum TALER_ErrorCode ec = TALER_EC_INVALID;
     393              : 
     394              :     while (1)
     395         1742 :     {
     396              :       ssize_t ret;
     397              :       uint16_t msize;
     398              : 
     399         5226 :       ret = recv (esh->sock,
     400         3484 :                   &buf[off],
     401              :                   sizeof (buf) - off,
     402         1742 :                   (finished && (0 == off))
     403              :                   ? MSG_DONTWAIT
     404              :                   : 0);
     405         3484 :       if (ret < 0)
     406              :       {
     407         1742 :         if (EINTR == errno)
     408            0 :           continue;
     409         1742 :         if (EAGAIN == errno)
     410              :         {
     411         1742 :           GNUNET_assert (finished);
     412         1742 :           GNUNET_assert (0 == off);
     413         1742 :           break;
     414              :         }
     415            0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     416              :                              "recv");
     417            0 :         do_disconnect (esh);
     418            0 :         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
     419              :       }
     420         1742 :       if (0 == ret)
     421              :       {
     422            0 :         GNUNET_break (0 == off);
     423            0 :         if (finished)
     424            0 :           return TALER_EC_NONE;
     425            0 :         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     426              :       }
     427         1742 :       off += ret;
     428         3484 : more:
     429         3484 :       if (off < sizeof (struct GNUNET_MessageHeader))
     430         1742 :         continue;
     431         1742 :       msize = ntohs (hdr->size);
     432         1742 :       if (off < msize)
     433            0 :         continue;
     434         1742 :       switch (ntohs (hdr->type))
     435              :       {
     436         1742 :       case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
     437         1742 :         if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
     438              :         {
     439            0 :           GNUNET_break_op (0);
     440            0 :           do_disconnect (esh);
     441            0 :           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     442              :         }
     443         1742 :         if (finished)
     444              :         {
     445            0 :           GNUNET_break_op (0);
     446            0 :           do_disconnect (esh);
     447            0 :           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     448              :         }
     449              :         {
     450         1742 :           const struct TALER_CRYPTO_EddsaSignResponse *sr =
     451              :             (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
     452         1742 :           *exchange_sig = sr->exchange_sig;
     453         1742 :           *exchange_pub = sr->exchange_pub;
     454         1742 :           finished = true;
     455         1742 :           ec = TALER_EC_NONE;
     456         1742 :           break;
     457              :         }
     458            0 :       case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
     459            0 :         if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
     460              :         {
     461            0 :           GNUNET_break_op (0);
     462            0 :           do_disconnect (esh);
     463            0 :           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     464              :         }
     465              :         {
     466            0 :           const struct TALER_CRYPTO_EddsaSignFailure *sf =
     467              :             (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
     468              : 
     469            0 :           finished = true;
     470            0 :           ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
     471            0 :           break;
     472              :         }
     473            0 :       case TALER_HELPER_EDDSA_MT_AVAIL:
     474            0 :         if (GNUNET_OK !=
     475            0 :             handle_mt_avail (esh,
     476              :                              hdr))
     477              :         {
     478            0 :           GNUNET_break_op (0);
     479            0 :           do_disconnect (esh);
     480            0 :           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     481              :         }
     482            0 :         break; /* while(1) loop ensures we recv() again */
     483            0 :       case TALER_HELPER_EDDSA_MT_PURGE:
     484            0 :         if (GNUNET_OK !=
     485            0 :             handle_mt_purge (esh,
     486              :                              hdr))
     487              :         {
     488            0 :           GNUNET_break_op (0);
     489            0 :           do_disconnect (esh);
     490            0 :           return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     491              :         }
     492            0 :         break; /* while(1) loop ensures we recv() again */
     493            0 :       case TALER_HELPER_EDDSA_SYNCED:
     494            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     495              :                     "Synchronized add odd time with EdDSA helper!\n");
     496            0 :         esh->synced = true;
     497            0 :         break;
     498            0 :       default:
     499            0 :         GNUNET_break_op (0);
     500            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     501              :                     "Received unexpected message of type %u\n",
     502              :                     ntohs (hdr->type));
     503            0 :         do_disconnect (esh);
     504            0 :         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
     505              :       }
     506         1742 :       memmove (buf,
     507         1742 :                &buf[msize],
     508              :                off - msize);
     509         1742 :       off -= msize;
     510         1742 :       goto more;
     511              :     } /* while(1) */
     512         1742 :     return ec;
     513              :   }
     514              : }
     515              : 
     516              : 
     517              : void
     518            3 : TALER_CRYPTO_helper_esign_revoke (
     519              :   struct TALER_CRYPTO_ExchangeSignHelper *esh,
     520              :   const struct TALER_ExchangePublicKeyP *exchange_pub)
     521              : {
     522            3 :   if (GNUNET_OK !=
     523            3 :       try_connect (esh))
     524            0 :     return; /* give up */
     525              :   {
     526            3 :     struct TALER_CRYPTO_EddsaRevokeRequest rr = {
     527            3 :       .header.size = htons (sizeof (rr)),
     528            3 :       .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
     529              :       .exchange_pub = *exchange_pub
     530              :     };
     531              : 
     532            3 :     if (GNUNET_OK !=
     533            3 :         TALER_crypto_helper_send_all (esh->sock,
     534              :                                       &rr,
     535              :                                       sizeof (rr)))
     536              :     {
     537            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     538              :                            "send");
     539            0 :       do_disconnect (esh);
     540            0 :       return;
     541              :     }
     542              :   }
     543              : }
     544              : 
     545              : 
     546              : void
     547           27 : TALER_CRYPTO_helper_esign_disconnect (
     548              :   struct TALER_CRYPTO_ExchangeSignHelper *esh)
     549              : {
     550           27 :   if (-1 != esh->sock)
     551           26 :     do_disconnect (esh);
     552           27 :   GNUNET_free (esh);
     553           27 : }
     554              : 
     555              : 
     556              : /* end of crypto_helper_esign.c */
        

Generated by: LCOV version 2.0-1