aboutsummaryrefslogtreecommitdiff
path: root/src/mint/taler-mint-httpd_keys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint/taler-mint-httpd_keys.c')
-rw-r--r--src/mint/taler-mint-httpd_keys.c474
1 files changed, 27 insertions, 447 deletions
diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c
index 359357da9..579112749 100644
--- a/src/mint/taler-mint-httpd_keys.c
+++ b/src/mint/taler-mint-httpd_keys.c
@@ -19,10 +19,6 @@
19 * @author Florian Dold 19 * @author Florian Dold
20 * @author Benedikt Mueller 20 * @author Benedikt Mueller
21 * @author Christian Grothoff 21 * @author Christian Grothoff
22 *
23 * TODO:
24 * - separate key management into a file separate from
25 * /keys handling (to fit other handlers)
26 */ 22 */
27#include "platform.h" 23#include "platform.h"
28#include <gnunet/gnunet_util_lib.h> 24#include <gnunet/gnunet_util_lib.h>
@@ -35,373 +31,7 @@
35#include "taler_json_lib.h" 31#include "taler_json_lib.h"
36#include "taler-mint-httpd_parsing.h" 32#include "taler-mint-httpd_parsing.h"
37#include "taler-mint-httpd_keys.h" 33#include "taler-mint-httpd_keys.h"
38 34#include "taler-mint-httpd_keystate.h"
39
40
41/**
42 * Mint key state. Never use directly, instead access via
43 * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release.
44 */
45static struct MintKeyState *internal_key_state;
46
47/**
48 * Mutex protecting access to #internal_key_state.
49 */
50static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER;
51
52/**
53 * Pipe used for signaling reloading of our key state.
54 */
55static int reload_pipe[2];
56
57
58/**
59 * Convert the public part of a denomination key
60 * issue to a JSON object.
61 *
62 * @param dki the denomination key issue
63 * @return a JSON object describing the denomination key isue (public part)
64 */
65static json_t *
66denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki)
67{
68 char *buf;
69 size_t buf_len;
70 json_t *dk_json = json_object ();
71
72 json_object_set_new (dk_json, "master_sig",
73 TALER_JSON_from_data (&dki->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
74 json_object_set_new (dk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start)));
75 json_object_set_new (dk_json, "stamp_expire_withdraw", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw)));
76 json_object_set_new (dk_json, "stamp_expire_deposit", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend)));
77
78
79 buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dki->denom_pub,
80 &buf);
81 json_object_set_new (dk_json, "denom_pub",
82 TALER_JSON_from_data (buf,
83 buf_len));
84 GNUNET_free (buf);
85 json_object_set_new (dk_json, "value",
86 TALER_JSON_from_amount (TALER_amount_ntoh (dki->value)));
87 json_object_set_new (dk_json,
88 "fee_withdraw",
89 TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_withdraw)));
90 json_object_set_new (dk_json,
91 "fee_deposit",
92 TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_deposit)));
93 json_object_set_new (dk_json,
94 "fee_refresh",
95 TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_refresh)));
96 return dk_json;
97}
98
99
100/**
101 * Convert the public part of a sign key
102 * issue to a JSON object.
103 *
104 * @param ski the sign key issue
105 * @return a JSON object describing the sign key isue (public part)
106 */
107static json_t *
108sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski)
109{
110 json_t *sk_json = json_object ();
111 json_object_set_new (sk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start)));
112 json_object_set_new (sk_json, "stamp_expire", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire)));
113 json_object_set_new (sk_json, "master_sig",
114 TALER_JSON_from_data (&ski->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
115 json_object_set_new (sk_json, "key",
116 TALER_JSON_from_data (&ski->signkey_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)));
117 return sk_json;
118}
119
120
121/**
122 * Get the relative time value that describes how
123 * far in the future do we want to provide coin keys.
124 *
125 * @return the provide duration
126 */
127static struct GNUNET_TIME_Relative
128TALER_MINT_conf_duration_provide ()
129{
130 struct GNUNET_TIME_Relative rel;
131
132 if (GNUNET_OK !=
133 GNUNET_CONFIGURATION_get_value_time (cfg,
134 "mint_keys",
135 "lookahead_provide",
136 &rel))
137 {
138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139 "mint_keys.lookahead_provide not valid or not given\n");
140 GNUNET_abort ();
141 }
142 return rel;
143}
144
145
146/**
147 * Iterator for denomination keys.
148 *
149 * @param cls closure
150 * @param dki the denomination key issue
151 * @param alias coin alias
152 * @return #GNUNET_OK to continue to iterate,
153 * #GNUNET_NO to stop iteration with no error,
154 * #GNUNET_SYSERR to abort iteration with error!
155 */
156static int
157reload_keys_denom_iter (void *cls,
158 const char *alias,
159 const struct TALER_MINT_DenomKeyIssuePriv *dki)
160{
161 struct MintKeyState *ctx = cls;
162 struct GNUNET_TIME_Absolute stamp_provide;
163 struct GNUNET_HashCode denom_key_hash;
164 int res;
165
166 stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time,
167 TALER_MINT_conf_duration_provide ());
168
169 if (GNUNET_TIME_absolute_ntoh (dki->issue.expire_spend).abs_value_us < ctx->reload_time.abs_value_us)
170 {
171 // this key is expired
172 return GNUNET_OK;
173 }
174 if (GNUNET_TIME_absolute_ntoh (dki->issue.start).abs_value_us > stamp_provide.abs_value_us)
175 {
176 // we are to early for this key
177 return GNUNET_OK;
178 }
179
180 GNUNET_CRYPTO_hash (&dki->issue.denom_pub,
181 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
182 &denom_key_hash);
183
184 res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map,
185 &denom_key_hash,
186 GNUNET_memdup (dki, sizeof (struct TALER_MINT_DenomKeyIssuePriv)),
187 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
188 if (GNUNET_OK != res)
189 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Duplicate denomination key\n");
190
191 json_array_append_new (ctx->denom_keys_array,
192 denom_key_issue_to_json (&dki->issue));
193
194 return GNUNET_OK;
195}
196
197
198/**
199 * Iterator for sign keys.
200 *
201 * @param cls closure
202 * @param ski the sign key issue
203 * @return #GNUNET_OK to continue to iterate,
204 * #GNUNET_NO to stop iteration with no error,
205 * #GNUNET_SYSERR to abort iteration with error!
206 */
207static int
208reload_keys_sign_iter (void *cls,
209 const struct TALER_MINT_SignKeyIssuePriv *ski)
210{
211 struct MintKeyState *ctx = cls;
212 struct GNUNET_TIME_Absolute stamp_provide;
213
214 stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, TALER_MINT_conf_duration_provide (cfg));
215
216 if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < ctx->reload_time.abs_value_us)
217 {
218 // this key is expired
219 return GNUNET_OK;
220 }
221
222 if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > stamp_provide.abs_value_us)
223 {
224 // we are to early for this key
225 return GNUNET_OK;
226 }
227
228 // the signkey is valid for now, check
229 // if it's more recent than the current one!
230 if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us >
231 GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us)
232 ctx->current_sign_key_issue = *ski;
233
234
235 ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload,
236 GNUNET_TIME_absolute_ntoh (ski->issue.expire));
237
238 json_array_append_new (ctx->sign_keys_array,
239 sign_key_issue_to_json (&ski->issue));
240
241 return GNUNET_OK;
242}
243
244
245/**
246 * Load the mint's key state from disk.
247 *
248 * @return fresh key state (with reference count 1)
249 */
250static struct MintKeyState *
251reload_keys ()
252{
253 struct MintKeyState *key_state;
254 json_t *keys;
255
256 key_state = GNUNET_new (struct MintKeyState);
257 key_state->refcnt = 1;
258
259 key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS;
260
261 key_state->denom_keys_array = json_array ();
262 GNUNET_assert (NULL != key_state->denom_keys_array);
263
264 key_state->sign_keys_array = json_array ();
265 GNUNET_assert (NULL != key_state->sign_keys_array);
266
267 key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
268 GNUNET_assert (NULL != key_state->denomkey_map);
269
270 key_state->reload_time = GNUNET_TIME_absolute_get ();
271
272 TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state);
273 TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state);
274
275 keys = json_pack ("{s:o, s:o, s:o, s:o}",
276 "master_pub", TALER_JSON_from_data (&master_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)),
277 "signkeys", key_state->sign_keys_array,
278 "denoms", key_state->denom_keys_array,
279 "list_issue_date", TALER_JSON_from_abs (key_state->reload_time));
280
281 key_state->keys_json = json_dumps (keys, JSON_INDENT(2));
282
283 return key_state;
284}
285
286
287/**
288 * Release key state, free if necessary (if reference count gets to zero).
289 *
290 * @param key_state the key state to release
291 */
292void
293TALER_MINT_key_state_release (struct MintKeyState *key_state)
294{
295 GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
296 GNUNET_assert (0 != key_state->refcnt);
297 key_state->refcnt += 1;
298 if (key_state->refcnt == 0) {
299 GNUNET_free (key_state);
300 }
301 GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
302}
303
304
305/**
306 * Acquire the key state of the mint. Updates keys if necessary.
307 * For every call to #TALER_MINT_key_state_acquire, a matching call
308 * to #TALER_MINT_key_state_release must be made.
309 *
310 * @return the key state
311 */
312struct MintKeyState *
313TALER_MINT_key_state_acquire (void)
314{
315 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
316 struct MintKeyState *key_state;
317
318 // FIXME: the locking we have is very coarse-grained,
319 // using multiple locks might be nicer ...
320
321 GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
322 if (NULL == internal_key_state)
323 {
324 internal_key_state = reload_keys ();
325 }
326 else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us)
327 {
328 GNUNET_assert (0 != internal_key_state->refcnt);
329 internal_key_state->refcnt--;
330 if (0 == internal_key_state->refcnt)
331 GNUNET_free (internal_key_state);
332 internal_key_state = reload_keys ();
333 }
334 key_state = internal_key_state;
335 key_state->refcnt += 1;
336 GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
337
338 return key_state;
339}
340
341
342/**
343 * Look up the issue for a denom public key.
344 *
345 * @param key state to look in
346 * @param denom_pub denomination public key
347 * @return the denomination key issue,
348 * or NULL if denom_pub could not be found
349 */
350struct TALER_MINT_DenomKeyIssuePriv *
351TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
352 const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub)
353{
354 struct TALER_MINT_DenomKeyIssuePriv *issue;
355 struct GNUNET_HashCode hash;
356 char *buf;
357 size_t buf_len;
358
359 buf_len = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub,
360 &buf);
361 GNUNET_CRYPTO_hash (buf,
362 buf_len,
363 &hash);
364 GNUNET_free (buf);
365 issue = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, &hash);
366 return issue;
367}
368
369
370/**
371 * Check if a coin is valid; that is, whether the denomination key exists,
372 * is not expired, and the signature is correct.
373 *
374 * @param key_state the key state to use for checking the coin's validity
375 * @param coin_public_info the coin public info to check for validity
376 * @return #GNUNET_YES if the coin is valid,
377 * #GNUNET_NO if it is invalid
378 * #GNUNET_SYSERROR if an internal error occured
379 */
380int
381TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
382 const struct TALER_CoinPublicInfo *coin_public_info)
383{
384 struct TALER_MINT_DenomKeyIssuePriv *dki;
385 struct GNUNET_HashCode c_hash;
386
387 dki = TALER_MINT_get_denom_key (key_state, coin_public_info->denom_pub);
388 if (NULL == dki)
389 return GNUNET_NO;
390 /* FIXME: we had envisioned a more complex scheme... */
391 GNUNET_CRYPTO_hash (&coin_public_info->coin_pub,
392 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
393 &c_hash);
394 if (GNUNET_OK !=
395 GNUNET_CRYPTO_rsa_verify (&c_hash,
396 coin_public_info->denom_sig,
397 dki->issue.denom_pub))
398 {
399 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
400 "coin signature is invalid\n");
401 return GNUNET_NO;
402 }
403 return GNUNET_YES;
404}
405 35
406 36
407/** 37/**
@@ -448,89 +78,39 @@ TALER_MINT_handler_keys (struct RequestHandler *rh,
448 78
449 79
450/** 80/**
451 * Handle a signal, writing relevant signal numbers 81 * Check if a coin is valid; that is, whether the denomination key exists,
452 * (currently just SIGUSR1) to a pipe. 82 * is not expired, and the signature is correct.
453 * 83 *
454 * @param signal_number the signal number 84 * @param key_state the key state to use for checking the coin's validity
455 */ 85 * @param coin_public_info the coin public info to check for validity
456static void 86 * @return #GNUNET_YES if the coin is valid,
457handle_signal (int signal_number) 87 * #GNUNET_NO if it is invalid
458{ 88 * #GNUNET_SYSERROR if an internal error occured
459 size_t res;
460 char c = signal_number;
461
462 if (SIGUSR1 == signal_number)
463 {
464 errno = 0;
465 res = write (reload_pipe[1], &c, 1);
466 if ((res < 0) && (EINTR != errno))
467 {
468 GNUNET_break (0);
469 return;
470 }
471 if (0 == res)
472 {
473 GNUNET_break (0);
474 return;
475 }
476 }
477}
478
479
480/**
481 * Read signals from a pipe in a loop, and reload keys from disk if
482 * SIGUSR1 is read from the pipe.
483 */ 89 */
484int 90int
485TALER_MINT_key_reload_loop (void) 91TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
92 const struct TALER_CoinPublicInfo *coin_public_info)
486{ 93{
487 struct sigaction act; 94 struct TALER_MINT_DenomKeyIssuePriv *dki;
488 95 struct GNUNET_HashCode c_hash;
489 if (0 != pipe (reload_pipe))
490 {
491 fprintf (stderr,
492 "Failed to create pipe.\n");
493 return GNUNET_SYSERR;
494 }
495 memset (&act, 0, sizeof (struct sigaction));
496 act.sa_handler = &handle_signal;
497
498 if (0 != sigaction (SIGUSR1, &act, NULL))
499 {
500 fprintf (stderr,
501 "Failed to set signal handler.\n");
502 return GNUNET_SYSERR;
503 }
504 96
505 while (1) 97 dki = TALER_MINT_get_denom_key (key_state, coin_public_info->denom_pub);
98 if (NULL == dki)
99 return GNUNET_NO;
100 /* FIXME: we had envisioned a more complex scheme... */
101 GNUNET_CRYPTO_hash (&coin_public_info->coin_pub,
102 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
103 &c_hash);
104 if (GNUNET_OK !=
105 GNUNET_CRYPTO_rsa_verify (&c_hash,
106 coin_public_info->denom_sig,
107 dki->issue.denom_pub))
506 { 108 {
507 char c; 109 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
508 ssize_t res; 110 "coin signature is invalid\n");
509 111 return GNUNET_NO;
510 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
511 "(re-)loading keys\n");
512 GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
513 if (NULL != internal_key_state)
514 {
515 GNUNET_assert (0 != internal_key_state->refcnt);
516 internal_key_state->refcnt -= 1;
517 if (0 == internal_key_state->refcnt)
518 GNUNET_free (internal_key_state);
519 }
520 internal_key_state = reload_keys ();
521 GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
522read_again:
523 errno = 0;
524 res = read (reload_pipe[0], &c, 1);
525 if ((res < 0) && (EINTR != errno))
526 {
527 GNUNET_break (0);
528 return GNUNET_SYSERR;
529 }
530 if (EINTR == errno)
531 goto read_again;
532 } 112 }
533 return GNUNET_OK; 113 return GNUNET_YES;
534} 114}
535 115
536 116