diff options
author | priscilla <priscilla.huang@efrei.net> | 2023-03-02 09:54:54 -0500 |
---|---|---|
committer | priscilla <priscilla.huang@efrei.net> | 2023-03-02 09:55:24 -0500 |
commit | b46c03b2c9e7d8e5136a84b2fb3d6af8277947df (patch) | |
tree | 0618bc5b9ea058e2dcb63b31daaec4e627c83681 /src/util | |
parent | 468006c60bf6bbdd4d44b80a3ac770168e5e808a (diff) |
totp algorithm
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/crypto_confirmation.c | 262 |
1 files changed, 261 insertions, 1 deletions
diff --git a/src/util/crypto_confirmation.c b/src/util/crypto_confirmation.c index 61a73f4c2..e47a61a69 100644 --- a/src/util/crypto_confirmation.c +++ b/src/util/crypto_confirmation.c @@ -17,23 +17,283 @@ * @file util/crypto_confirmation.c * @brief confirmation computation * @author Christian Grothoff + * @author Priscilla Huang */ #include "platform.h" #include "taler_util.h" +#include <taler/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)); + 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); + 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 |= hmac[offset + 3 - count] << (8 * count); + code &= 0x7fffffff; + /* always use 8 digits (maximum) */ + code = code % 100000000; + return code; + } +} + + +/** + * Compute RFC 3548 base32 decoding of @a val and write + * result to @a udata. + * + * @param val value to decode + * @param val_size number of bytes in @a val + * @param key is the val in bits + * @param key_len is the size of @a key + */ +static int +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 int wpos; + unsigned int rpos; + unsigned int bits; + unsigned int vbit; + unsigned char *udata; + + udata = val; + vbit = 0; + wpos = 0; + rpos = 0; + bits = 0; + while ((rpos < val_size) || (vbit >= 8)) + { + if ((rpos < val_size) && (vbit < 8)) + { + char c = val[rpos++]; + if (c == '=') { // padding character + break; + } + 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; +} + +/** + * It is build pos confirmation to verify payment. + * + * @param pos_key base32 (RFC 3548, not Crockford!) encoded key for verification payment + * @param pos_alg algorithm to compute the payment verification + * @param total of the order paid + * @param ts is the current time given + */ 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; /* lengh of the key */ + uint64_t code; /* totp code */ + 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 = 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 */ + ret = NULL; + /* Return all T-OTP codes in range separated by new lines, e.g. + "12345678 + 24522552 + 25262425 + 42543525 + 25253552" + */ + for (int i = -TIME_INTERVAL_RANGE; i<= TIME_INTERVAL_RANGE; i++) + { + code = compute_totp (ts, + i, + key, + key_len); + if (NULL == ret) + { + GNUNET_asprintf (&ret, + "%llu", + (unsigned long long) code); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s\n%llu", + ret, + (unsigned long long) code); + GNUNET_free (ret); + ret = tmp; + } + } + GNUNET_free (key); + return ret; + case TALER_MCA_WITH_PRICE: + { + struct GNUNET_HashCode hkey; + struct TALER_AmountNBO ntotal; + ret = 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); + + for (int i = -TIME_INTERVAL_RANGE; i<= TIME_INTERVAL_RANGE; i++) + { + code = compute_totp (ts, + i, + &hkey, + sizeof (hkey)); + if (NULL == ret) + { + GNUNET_asprintf (&ret, + "%llu", + (unsigned long long) code); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s\n%llu", + ret, + (unsigned long long) code); + GNUNET_free (ret); + ret = tmp; + } + } + GNUNET_free (key); + return ret; + } } - GNUNET_break (0); // FIXME: not implemented + GNUNET_free (key); + GNUNET_break (0); return NULL; } |