diff options
Diffstat (limited to 'src/util/crypto_confirmation.c')
-rw-r--r-- | src/util/crypto_confirmation.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/util/crypto_confirmation.c b/src/util/crypto_confirmation.c new file mode 100644 index 000000000..99552f150 --- /dev/null +++ b/src/util/crypto_confirmation.c @@ -0,0 +1,293 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_confirmation.c + * @brief confirmation computation + * @author Christian Grothoff + * @author Priscilla Huang + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_mhd_lib.h" +#include <gnunet/gnunet_db_lib.h> +#include <gcrypt.h> + +/** + * How long is a TOTP code valid? + */ +#define TOTP_VALIDITY_PERIOD GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * Range of time we allow (plus-minus). + */ +#define TIME_INTERVAL_RANGE 2 + + +/** + * Compute TOTP code at current time with offset + * @a time_off for the @a key. + * + * @param ts current time + * @param time_off offset to apply when computing the code + * @param key pos_key in binary + * @param key_size number of bytes in @a key + */ +static uint64_t +compute_totp (struct GNUNET_TIME_Timestamp ts, + int time_off, + const void *key, + size_t key_size) +{ + struct GNUNET_TIME_Absolute now; + time_t t; + uint64_t ctr; + uint8_t hmac[20]; /* SHA1: 20 bytes */ + + now = ts.abs_time; + while (time_off < 0) + { + now = GNUNET_TIME_absolute_subtract (now, + TOTP_VALIDITY_PERIOD); + time_off++; + } + while (time_off > 0) + { + now = GNUNET_TIME_absolute_add (now, + TOTP_VALIDITY_PERIOD); + time_off--; + } + t = now.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us; + ctr = GNUNET_htonll (t / 30LLU); + + { + gcry_md_hd_t md; + const unsigned char *mc; + + GNUNET_assert (GPG_ERR_NO_ERROR == + gcry_md_open (&md, + GCRY_MD_SHA1, + GCRY_MD_FLAG_HMAC)); + GNUNET_assert (GPG_ERR_NO_ERROR == + gcry_md_setkey (md, + key, + key_size)); + gcry_md_write (md, + &ctr, + sizeof (ctr)); + mc = gcry_md_read (md, + GCRY_MD_SHA1); + GNUNET_assert (NULL != mc); + GNUNET_memcpy (hmac, + mc, + sizeof (hmac)); + gcry_md_close (md); + } + + { + uint32_t code = 0; + int offset; + + offset = hmac[sizeof (hmac) - 1] & 0x0f; + for (int count = 0; count < 4; count++) + code |= ((uint32_t) hmac[offset + 3 - count]) << (8 * count); + code &= 0x7fffffff; + /* always use 8 digits (maximum) */ + code = code % 100000000; + return code; + } +} + + +int +TALER_rfc3548_base32decode (const char *val, + size_t val_size, + void *key, + size_t key_len) +{ + /** + * 32 characters for decoding, using RFC 3548. + */ + static const char *decTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + unsigned char *udata = key; + unsigned int wpos = 0; + unsigned int rpos = 0; + unsigned int bits = 0; + unsigned int vbit = 0; + + while ((rpos < val_size) || (vbit >= 8)) + { + if ((rpos < val_size) && (vbit < 8)) + { + char c = val[rpos++]; + + if (c == '=') + { + /* padding character */ + if (rpos == val_size) + break; /* Ok, 1x '=' padding is allowed */ + if ( ('=' == val[rpos]) && + (rpos + 1 == val_size) ) + break; /* Ok, 2x '=' padding is allowed */ + return -1; /* invalid padding */ + } + const char *p = strchr (decTable__, toupper (c)); + if (! p) + { + /* invalid character */ + return -1; + } + bits = (bits << 5) | (p - decTable__); + vbit += 5; + } + if (vbit >= 8) + { + udata[wpos++] = (bits >> (vbit - 8)) & 0xFF; + vbit -= 8; + } + } + return wpos; +} + + +/** + * @brief Builds POS confirmation to verify payment. + * + * @param h_key opaque key for the totp operation + * @param h_key_len size of h_key in bytes + * @param ts current time + * @return Token on success, NULL of failure + */ +static char * +executive_totp (void *h_key, + size_t h_key_len, + struct GNUNET_TIME_Timestamp ts) +{ + uint64_t code; /* totp code */ + char *ret; + ret = NULL; + + for (int i = -TIME_INTERVAL_RANGE; i<= TIME_INTERVAL_RANGE; i++) + { + code = compute_totp (ts, + i, + h_key, + h_key_len); + if (NULL == ret) + { + GNUNET_asprintf (&ret, + "%08llu", + (unsigned long long) code); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s\n%08llu", + ret, + (unsigned long long) code); + GNUNET_free (ret); + ret = tmp; + } + } + return ret; + +} + + +char * +TALER_build_pos_confirmation (const char *pos_key, + enum TALER_MerchantConfirmationAlgorithm pos_alg, + const struct TALER_Amount *total, + struct GNUNET_TIME_Timestamp ts) +{ + size_t pos_key_length = strlen (pos_key); + void *key; /* pos_key in binary */ + size_t key_len; /* length of the key */ + char *ret; + int dret; + + if (TALER_MCA_NONE == pos_alg) + return NULL; + key_len = pos_key_length * 5 / 8; + key = GNUNET_malloc (key_len); + dret = TALER_rfc3548_base32decode (pos_key, + pos_key_length, + key, + key_len); + if (-1 == dret) + { + GNUNET_free (key); + GNUNET_break_op (0); + return NULL; + } + GNUNET_assert (dret <= key_len); + key_len = (size_t) dret; + switch (pos_alg) + { + case TALER_MCA_NONE: + GNUNET_break (0); + GNUNET_free (key); + return NULL; + case TALER_MCA_WITHOUT_PRICE: /* and 30s */ + /* Return all T-OTP codes in range separated by new lines, e.g. + "12345678 + 24522552 + 25262425 + 42543525 + 25253552" + */ + ret = executive_totp (key, + key_len, + ts); + GNUNET_free (key); + return ret; + case TALER_MCA_WITH_PRICE: + { + struct GNUNET_HashCode hkey; + struct TALER_AmountNBO ntotal; + + if ( (NULL == total) || + (GNUNET_YES != + TALER_amount_is_valid (total) ) ) + { + GNUNET_break_op (0); + return NULL; + } + TALER_amount_hton (&ntotal, + total); + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (&hkey, + sizeof (hkey), + &ntotal, + sizeof (ntotal), + key, + key_len, + NULL, + 0)); + GNUNET_free (key); + ret = executive_totp (&hkey, + sizeof(hkey), + ts); + GNUNET_free (key); + return ret; + } + } + GNUNET_free (key); + GNUNET_break (0); + return NULL; +} |