aboutsummaryrefslogtreecommitdiff
path: root/src/util/crypto_confirmation.c
diff options
context:
space:
mode:
authorpriscilla <priscilla.huang@efrei.net>2023-03-02 09:54:54 -0500
committerpriscilla <priscilla.huang@efrei.net>2023-03-02 09:55:24 -0500
commitb46c03b2c9e7d8e5136a84b2fb3d6af8277947df (patch)
tree0618bc5b9ea058e2dcb63b31daaec4e627c83681 /src/util/crypto_confirmation.c
parent468006c60bf6bbdd4d44b80a3ac770168e5e808a (diff)
totp algorithm
Diffstat (limited to 'src/util/crypto_confirmation.c')
-rw-r--r--src/util/crypto_confirmation.c262
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;
}