LCOV - code coverage report
Current view: top level - util - crypto_confirmation.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 77.6 % 98 76
Test Date: 2026-09-11 18:55:36 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023 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_confirmation.c
      18              :  * @brief confirmation computation
      19              :  * @author Christian Grothoff
      20              :  * @author Priscilla Huang
      21              :  */
      22              : #include "platform.h"  /* UNNECESSARY? */
      23              : #include "taler/taler_util.h"
      24              : #include "taler/taler_mhd_lib.h"
      25              : #include <gnunet/gnunet_db_lib.h>
      26              : #include <gcrypt.h>
      27              : 
      28              : /**
      29              :  * How long is a TOTP code valid?
      30              :  */
      31              : #define TOTP_VALIDITY_PERIOD GNUNET_TIME_relative_multiply ( \
      32              :           GNUNET_TIME_UNIT_SECONDS, 30)
      33              : 
      34              : /**
      35              :  * Range of time we allow (plus-minus).
      36              :  */
      37              : #define TIME_INTERVAL_RANGE 2
      38              : 
      39              : 
      40              : /**
      41              :  * Compute TOTP code at current time with offset
      42              :  * @a time_off for the @a key.
      43              :  *
      44              :  * @param ts current time
      45              :  * @param time_off offset to apply when computing the code
      46              :  * @param key pos_key in binary
      47              :  * @param key_size number of bytes in @a key
      48              :  */
      49              : static uint64_t
      50           10 : compute_totp (struct GNUNET_TIME_Timestamp ts,
      51              :               int time_off,
      52              :               const void *key,
      53              :               size_t key_size)
      54              : {
      55              :   struct GNUNET_TIME_Absolute now;
      56              :   time_t t;
      57              :   uint64_t ctr;
      58              :   uint8_t hmac[20]; /* SHA1: 20 bytes */
      59              : 
      60           10 :   now = ts.abs_time;
      61           16 :   while (time_off < 0)
      62              :   {
      63            6 :     now = GNUNET_TIME_absolute_subtract (now,
      64              :                                          TOTP_VALIDITY_PERIOD);
      65            6 :     time_off++;
      66              :   }
      67           16 :   while (time_off > 0)
      68              :   {
      69            6 :     now = GNUNET_TIME_absolute_add (now,
      70              :                                     TOTP_VALIDITY_PERIOD);
      71            6 :     time_off--;
      72              :   }
      73           10 :   t = now.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
      74           10 :   ctr = GNUNET_htonll (t / 30LLU);
      75              : 
      76              :   {
      77              :     gcry_md_hd_t md;
      78              :     const unsigned char *mc;
      79              : 
      80           10 :     GNUNET_assert (GPG_ERR_NO_ERROR ==
      81              :                    gcry_md_open (&md,
      82              :                                  GCRY_MD_SHA1,
      83              :                                  GCRY_MD_FLAG_HMAC));
      84           10 :     GNUNET_assert (GPG_ERR_NO_ERROR ==
      85              :                    gcry_md_setkey (md,
      86              :                                    key,
      87              :                                    key_size));
      88           10 :     gcry_md_write (md,
      89              :                    &ctr,
      90              :                    sizeof (ctr));
      91           10 :     mc = gcry_md_read (md,
      92              :                        GCRY_MD_SHA1);
      93           10 :     GNUNET_assert (NULL != mc);
      94           10 :     GNUNET_memcpy (hmac,
      95              :                    mc,
      96              :                    sizeof (hmac));
      97           10 :     gcry_md_close (md);
      98              :   }
      99              : 
     100              :   {
     101           10 :     uint32_t code = 0;
     102              :     int offset;
     103              : 
     104           10 :     offset = hmac[sizeof (hmac) - 1] & 0x0f;
     105           50 :     for (int count = 0; count < 4; count++)
     106           40 :       code |= ((uint32_t) hmac[offset + 3 - count]) << (8 * count);
     107           10 :     code &= 0x7fffffff;
     108              :     /* always use 8 digits (maximum) */
     109           10 :     code = code % 100000000;
     110           10 :     return code;
     111              :   }
     112              : }
     113              : 
     114              : 
     115              : int
     116            3 : TALER_rfc3548_base32decode (const char *val,
     117              :                             size_t val_size,
     118              :                             void *key,
     119              :                             size_t key_len)
     120              : {
     121              :   /**
     122              :    * 32 characters for decoding, using RFC 3548.
     123              :    */
     124              :   static const char *decTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
     125            3 :   unsigned char *udata = key;
     126            3 :   unsigned int wpos = 0;
     127            3 :   unsigned int rpos = 0;
     128            3 :   unsigned int bits = 0;
     129            3 :   unsigned int vbit = 0;
     130              : 
     131           51 :   while ((rpos < val_size) || (vbit >= 8))
     132              :   {
     133           48 :     if ((rpos < val_size) && (vbit < 8))
     134              :     {
     135              :       const char *p;
     136           48 :       char c = val[rpos++];
     137              : 
     138           48 :       if (c == '=')
     139              :       {
     140              :         /* padding character */
     141            0 :         if (rpos == val_size)
     142            0 :           break; /* Ok, 1x '=' padding is allowed */
     143            0 :         if ( ('=' == val[rpos]) &&
     144            0 :              (rpos + 1 == val_size) )
     145            0 :           break; /* Ok, 2x '=' padding is allowed */
     146            0 :         return -1; /* invalid padding */
     147              :       }
     148           48 :       p = strchr (decTable__, toupper (c));
     149           48 :       if (! p)
     150              :       {
     151              :         /* invalid character */
     152            0 :         return -1;
     153              :       }
     154           48 :       bits = (bits << 5) | (p - decTable__);
     155           48 :       vbit += 5;
     156              :     }
     157           48 :     if (vbit >= 8)
     158              :     {
     159           30 :       udata[wpos++] = (bits >> (vbit - 8)) & 0xFF;
     160           30 :       vbit -= 8;
     161              :     }
     162              :   }
     163            3 :   return wpos;
     164              : }
     165              : 
     166              : 
     167              : /**
     168              :  * @brief Builds POS confirmation to verify payment.
     169              :  *
     170              :  * @param h_key opaque key for the totp operation
     171              :  * @param h_key_len size of h_key in bytes
     172              :  * @param ts current time
     173              :  * @return Token on success, NULL of failure
     174              :  */
     175              : static char *
     176            2 : executive_totp (void *h_key,
     177              :                 size_t h_key_len,
     178              :                 struct GNUNET_TIME_Timestamp ts)
     179              : {
     180              :   uint64_t code; /* totp code */
     181              :   char *ret;
     182            2 :   ret = NULL;
     183              : 
     184           12 :   for (int i = -TIME_INTERVAL_RANGE; i<= TIME_INTERVAL_RANGE; i++)
     185              :   {
     186           10 :     code = compute_totp (ts,
     187              :                          i,
     188              :                          h_key,
     189              :                          h_key_len);
     190           10 :     if (NULL == ret)
     191              :     {
     192            2 :       GNUNET_asprintf (&ret,
     193              :                        "%08llu",
     194              :                        (unsigned long long) code);
     195              :     }
     196              :     else
     197              :     {
     198              :       char *tmp;
     199              : 
     200            8 :       GNUNET_asprintf (&tmp,
     201              :                        "%s\n%08llu",
     202              :                        ret,
     203              :                        (unsigned long long) code);
     204            8 :       GNUNET_free (ret);
     205            8 :       ret = tmp;
     206              :     }
     207              :   }
     208            2 :   return ret;
     209              : 
     210              : }
     211              : 
     212              : 
     213              : char *
     214            3 : TALER_build_pos_confirmation (const char *pos_key,
     215              :                               enum TALER_MerchantConfirmationAlgorithm pos_alg,
     216              :                               const struct TALER_Amount *total,
     217              :                               struct GNUNET_TIME_Timestamp ts)
     218              : {
     219            3 :   size_t pos_key_length = strlen (pos_key);
     220              :   void *key; /* pos_key in binary */
     221              :   size_t key_len; /* length of the key */
     222              :   char *ret;
     223              :   int dret;
     224              : 
     225            3 :   if (TALER_MCA_NONE == pos_alg)
     226            0 :     return NULL;
     227            3 :   key_len = pos_key_length * 5 / 8;
     228            3 :   key = GNUNET_malloc (key_len);
     229            3 :   dret = TALER_rfc3548_base32decode (pos_key,
     230              :                                      pos_key_length,
     231              :                                      key,
     232              :                                      key_len);
     233            3 :   if (-1 == dret)
     234              :   {
     235            0 :     GNUNET_free (key);
     236            0 :     GNUNET_break_op (0);
     237            0 :     return NULL;
     238              :   }
     239            3 :   GNUNET_assert (dret <= key_len);
     240            3 :   key_len = (size_t) dret;
     241            3 :   switch (pos_alg)
     242              :   {
     243            0 :   case TALER_MCA_NONE:
     244            0 :     GNUNET_break (0);
     245            0 :     GNUNET_free (key);
     246            0 :     return NULL;
     247            0 :   case TALER_MCA_ECDSA_CHALLENGE:
     248              :   case TALER_MCA_EDDSA_CHALLENGE:
     249              :     /* Challenge-signature confirmations are not time-based; they
     250              :        are computed in crypto_signatures.c. */
     251            0 :     GNUNET_break (0);
     252            0 :     GNUNET_free (key);
     253            0 :     return NULL;
     254            1 :   case TALER_MCA_WITHOUT_PRICE: /* and 30s */
     255              :     /* Return all T-OTP codes in range separated by new lines, e.g.
     256              :        "12345678
     257              :         24522552
     258              :         25262425
     259              :         42543525
     260              :         25253552"
     261              :     */
     262            1 :     ret = executive_totp (key,
     263              :                           key_len,
     264              :                           ts);
     265            1 :     GNUNET_free (key);
     266            1 :     return ret;
     267            2 :   case TALER_MCA_WITH_PRICE:
     268              :     {
     269              :       struct GNUNET_HashCode hkey;
     270              :       struct TALER_AmountNBO ntotal;
     271              : 
     272            3 :       if ( (NULL == total) ||
     273              :            (GNUNET_YES !=
     274            1 :             TALER_amount_is_valid (total) ) )
     275              :       {
     276            1 :         GNUNET_break_op (0);
     277            1 :         GNUNET_free (key);
     278            1 :         return NULL;
     279              :       }
     280            1 :       TALER_amount_hton (&ntotal,
     281              :                          total);
     282            1 :       GNUNET_assert (GNUNET_YES ==
     283              :                      GNUNET_CRYPTO_hkdf_gnunet (
     284              :                        &hkey,
     285              :                        sizeof (hkey),
     286              :                        &ntotal,
     287              :                        sizeof (ntotal),
     288              :                        key,
     289              :                        key_len));
     290            1 :       GNUNET_free (key);
     291            1 :       return executive_totp (&hkey,
     292              :                              sizeof(hkey),
     293              :                              ts);
     294              :     }
     295              :   }
     296            0 :   GNUNET_free (key);
     297            0 :   GNUNET_break (0);
     298            0 :   return NULL;
     299              : }
     300              : 
     301              : 
     302              : /* end of crypto_confirmation.c */
        

Generated by: LCOV version 2.0-1