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