crypto_confirmation.c (7713B)
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 "taler/platform.h" 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 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 now = ts.abs_time; 61 while (time_off < 0) 62 { 63 now = GNUNET_TIME_absolute_subtract (now, 64 TOTP_VALIDITY_PERIOD); 65 time_off++; 66 } 67 while (time_off > 0) 68 { 69 now = GNUNET_TIME_absolute_add (now, 70 TOTP_VALIDITY_PERIOD); 71 time_off--; 72 } 73 t = now.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us; 74 ctr = GNUNET_htonll (t / 30LLU); 75 76 { 77 gcry_md_hd_t md; 78 const unsigned char *mc; 79 80 GNUNET_assert (GPG_ERR_NO_ERROR == 81 gcry_md_open (&md, 82 GCRY_MD_SHA1, 83 GCRY_MD_FLAG_HMAC)); 84 GNUNET_assert (GPG_ERR_NO_ERROR == 85 gcry_md_setkey (md, 86 key, 87 key_size)); 88 gcry_md_write (md, 89 &ctr, 90 sizeof (ctr)); 91 mc = gcry_md_read (md, 92 GCRY_MD_SHA1); 93 GNUNET_assert (NULL != mc); 94 GNUNET_memcpy (hmac, 95 mc, 96 sizeof (hmac)); 97 gcry_md_close (md); 98 } 99 100 { 101 uint32_t code = 0; 102 int offset; 103 104 offset = hmac[sizeof (hmac) - 1] & 0x0f; 105 for (int count = 0; count < 4; count++) 106 code |= ((uint32_t) hmac[offset + 3 - count]) << (8 * count); 107 code &= 0x7fffffff; 108 /* always use 8 digits (maximum) */ 109 code = code % 100000000; 110 return code; 111 } 112 } 113 114 115 int 116 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 unsigned char *udata = key; 126 unsigned int wpos = 0; 127 unsigned int rpos = 0; 128 unsigned int bits = 0; 129 unsigned int vbit = 0; 130 131 while ((rpos < val_size) || (vbit >= 8)) 132 { 133 if ((rpos < val_size) && (vbit < 8)) 134 { 135 const char *p; 136 char c = val[rpos++]; 137 138 if (c == '=') 139 { 140 /* padding character */ 141 if (rpos == val_size) 142 break; /* Ok, 1x '=' padding is allowed */ 143 if ( ('=' == val[rpos]) && 144 (rpos + 1 == val_size) ) 145 break; /* Ok, 2x '=' padding is allowed */ 146 return -1; /* invalid padding */ 147 } 148 p = strchr (decTable__, toupper (c)); 149 if (! p) 150 { 151 /* invalid character */ 152 return -1; 153 } 154 bits = (bits << 5) | (p - decTable__); 155 vbit += 5; 156 } 157 if (vbit >= 8) 158 { 159 udata[wpos++] = (bits >> (vbit - 8)) & 0xFF; 160 vbit -= 8; 161 } 162 } 163 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 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 ret = NULL; 183 184 for (int i = -TIME_INTERVAL_RANGE; i<= TIME_INTERVAL_RANGE; i++) 185 { 186 code = compute_totp (ts, 187 i, 188 h_key, 189 h_key_len); 190 if (NULL == ret) 191 { 192 GNUNET_asprintf (&ret, 193 "%08llu", 194 (unsigned long long) code); 195 } 196 else 197 { 198 char *tmp; 199 200 GNUNET_asprintf (&tmp, 201 "%s\n%08llu", 202 ret, 203 (unsigned long long) code); 204 GNUNET_free (ret); 205 ret = tmp; 206 } 207 } 208 return ret; 209 210 } 211 212 213 char * 214 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 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 if (TALER_MCA_NONE == pos_alg) 226 return NULL; 227 key_len = pos_key_length * 5 / 8; 228 key = GNUNET_malloc (key_len); 229 dret = TALER_rfc3548_base32decode (pos_key, 230 pos_key_length, 231 key, 232 key_len); 233 if (-1 == dret) 234 { 235 GNUNET_free (key); 236 GNUNET_break_op (0); 237 return NULL; 238 } 239 GNUNET_assert (dret <= key_len); 240 key_len = (size_t) dret; 241 switch (pos_alg) 242 { 243 case TALER_MCA_NONE: 244 GNUNET_break (0); 245 GNUNET_free (key); 246 return NULL; 247 case TALER_MCA_WITHOUT_PRICE: /* and 30s */ 248 /* Return all T-OTP codes in range separated by new lines, e.g. 249 "12345678 250 24522552 251 25262425 252 42543525 253 25253552" 254 */ 255 ret = executive_totp (key, 256 key_len, 257 ts); 258 GNUNET_free (key); 259 return ret; 260 case TALER_MCA_WITH_PRICE: 261 { 262 struct GNUNET_HashCode hkey; 263 struct TALER_AmountNBO ntotal; 264 265 if ( (NULL == total) || 266 (GNUNET_YES != 267 TALER_amount_is_valid (total) ) ) 268 { 269 GNUNET_break_op (0); 270 return NULL; 271 } 272 TALER_amount_hton (&ntotal, 273 total); 274 GNUNET_assert (GNUNET_YES == 275 GNUNET_CRYPTO_kdf (&hkey, 276 sizeof (hkey), 277 &ntotal, 278 sizeof (ntotal), 279 key, 280 key_len, 281 NULL, 282 0)); 283 GNUNET_free (key); 284 ret = executive_totp (&hkey, 285 sizeof(hkey), 286 ts); 287 GNUNET_free (key); 288 return ret; 289 } 290 } 291 GNUNET_free (key); 292 GNUNET_break (0); 293 return NULL; 294 }