aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2015-01-28 20:10:59 +0100
committerChristian Grothoff <christian@grothoff.org>2015-01-28 20:10:59 +0100
commit172505bd0f16f5be18d64e86c4ad7ed01e920c1d (patch)
tree0058ad97077416e71780be266e8e78eed78e3671
parent554ee3d4da95a9fcfd408f1085586d0b6d1834f4 (diff)
downloadexchange-172505bd0f16f5be18d64e86c4ad7ed01e920c1d.tar.gz
exchange-172505bd0f16f5be18d64e86c4ad7ed01e920c1d.zip
splitting up /keys and keystate handling
-rw-r--r--src/mint/Makefile.am3
-rw-r--r--src/mint/taler-mint-httpd_keys.c474
-rw-r--r--src/mint/taler-mint-httpd_keys.h107
-rw-r--r--src/mint/taler-mint-httpd_keystate.c459
-rw-r--r--src/mint/taler-mint-httpd_keystate.h129
5 files changed, 626 insertions, 546 deletions
diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am
index 17b82dc51..e2c44d21f 100644
--- a/src/mint/Makefile.am
+++ b/src/mint/Makefile.am
@@ -61,9 +61,10 @@ taler_mint_reservemod_LDFLAGS = \
61 61
62taler_mint_httpd_SOURCES = \ 62taler_mint_httpd_SOURCES = \
63 taler-mint-httpd.c \ 63 taler-mint-httpd.c \
64 taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
65 taler-mint-httpd_db.c taler-mint-httpd_db.h \
64 taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ 66 taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
65 taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ 67 taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
66 taler-mint-httpd_db.c taler-mint-httpd_db.h \
67 taler-mint-httpd_mhd.c \ 68 taler-mint-httpd_mhd.c \
68 taler-mint-httpd_keys.c \ 69 taler-mint-httpd_keys.c \
69 taler-mint-httpd_deposit.c \ 70 taler-mint-httpd_deposit.c \
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
diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h
index eeeb21ef3..7dc5bc0eb 100644
--- a/src/mint/taler-mint-httpd_keys.h
+++ b/src/mint/taler-mint-httpd_keys.h
@@ -29,88 +29,7 @@
29#include "taler-mint-httpd.h" 29#include "taler-mint-httpd.h"
30#include "mint.h" 30#include "mint.h"
31#include "key_io.h" 31#include "key_io.h"
32 32#include "taler-mint-httpd_keystate.h"
33
34/**
35 * Snapshot of the (coin and signing)
36 * keys (including private keys) of the mint.
37 */
38struct MintKeyState
39{
40 /**
41 * When did we initiate the key reloading?
42 */
43 struct GNUNET_TIME_Absolute reload_time;
44
45 /**
46 * JSON array with denomination keys.
47 */
48 json_t *denom_keys_array;
49
50 /**
51 * JSON array with signing keys.
52 */
53 json_t *sign_keys_array;
54
55 /**
56 * Mapping from denomination keys to denomination key issue struct.
57 */
58 struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
59
60 /**
61 * When is the next key invalid and we have to reload?
62 */
63 struct GNUNET_TIME_Absolute next_reload;
64
65 /**
66 * Mint signing key that should be used currently.
67 */
68 struct TALER_MINT_SignKeyIssuePriv current_sign_key_issue;
69
70 /**
71 * Cached JSON text that the mint will send for
72 * a /keys request.
73 */
74 char *keys_json;
75
76 /**
77 * Reference count.
78 */
79 unsigned int refcnt;
80};
81
82
83/**
84 * Release key state, free if necessary (if reference count gets to zero).
85 *
86 * @param key_state the key state to release
87 */
88void
89TALER_MINT_key_state_release (struct MintKeyState *key_state);
90
91
92/**
93 * Acquire the key state of the mint. Updates keys if necessary.
94 * For every call to #TALER_MINT_key_state_acquire, a matching call
95 * to #TALER_MINT_key_state_release must be made.
96 *
97 * @return the key state
98 */
99struct MintKeyState *
100TALER_MINT_key_state_acquire (void);
101
102
103/**
104 * Look up the issue for a denom public key.
105 *
106 * @param key state to look in
107 * @param denom_pub denomination public key
108 * @return the denomination key issue,
109 * or NULL if denom_pub could not be found
110 */
111struct TALER_MINT_DenomKeyIssuePriv *
112TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
113 const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub);
114 33
115 34
116/** 35/**
@@ -129,13 +48,16 @@ TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
129 48
130 49
131/** 50/**
132 * Read signals from a pipe in a loop, and reload keys from disk if 51 * Sign the message in @a purpose with the mint's signing
133 * SIGUSR1 is read from the pipe. 52 * key.
134 * 53 *
135 * @return #GNUNET_OK if we terminated normally, #GNUNET_SYSERR on error 54 * @param purpose the message to sign
55 * @param[OUT] sig signature over purpose using current signing key
136 */ 56 */
137int 57void
138TALER_MINT_key_reload_loop (void); 58TALER_MINT_keys_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
59 struct GNUNET_CRYPTO_EddsaSignature *sig);
60
139 61
140 62
141/** 63/**
@@ -156,16 +78,5 @@ TALER_MINT_handler_keys (struct RequestHandler *rh,
156 size_t *upload_data_size); 78 size_t *upload_data_size);
157 79
158 80
159/**
160 * Sign the message in @a purpose with the mint's signing
161 * key.
162 *
163 * @param purpose the message to sign
164 * @param[OUT] sig signature over purpose using current signing key
165 */
166void
167TALER_MINT_keys_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
168 struct GNUNET_CRYPTO_EddsaSignature *sig);
169
170 81
171#endif 82#endif
diff --git a/src/mint/taler-mint-httpd_keystate.c b/src/mint/taler-mint-httpd_keystate.c
new file mode 100644
index 000000000..dd1855889
--- /dev/null
+++ b/src/mint/taler-mint-httpd_keystate.c
@@ -0,0 +1,459 @@
1/*
2 This file is part of TALER
3 (C) 2014 GNUnet e.V.
4
5 TALER is free software; you can redistribute it and/or modify it under the
6 terms of the GNU Affero 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 Affero General Public License for more details.
12
13 You should have received a copy of the GNU Affero General Public License along with
14 TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file taler-mint-httpd_keystate.c
18 * @brief management of our coin signing keys
19 * @author Florian Dold
20 * @author Benedikt Mueller
21 * @author Christian Grothoff
22 */
23#include "platform.h"
24#include <gnunet/gnunet_util_lib.h>
25#include <pthread.h>
26#include "mint.h"
27#include "taler_signatures.h"
28#include "taler-mint-httpd_keystate.h"
29#include "taler_json_lib.h"
30#include "taler-mint-httpd_parsing.h"
31
32
33/**
34 * Mint key state. Never use directly, instead access via
35 * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release.
36 */
37static struct MintKeyState *internal_key_state;
38
39/**
40 * Mutex protecting access to #internal_key_state.
41 */
42static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER;
43
44/**
45 * Pipe used for signaling reloading of our key state.
46 */
47static int reload_pipe[2];
48
49
50/**
51 * Convert the public part of a denomination key
52 * issue to a JSON object.
53 *
54 * @param dki the denomination key issue
55 * @return a JSON object describing the denomination key isue (public part)
56 */
57static json_t *
58denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki)
59{
60 char *buf;
61 size_t buf_len;
62 json_t *dk_json = json_object ();
63
64 json_object_set_new (dk_json, "master_sig",
65 TALER_JSON_from_data (&dki->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
66 json_object_set_new (dk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start)));
67 json_object_set_new (dk_json, "stamp_expire_withdraw", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw)));
68 json_object_set_new (dk_json, "stamp_expire_deposit", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend)));
69
70
71 buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dki->denom_pub,
72 &buf);
73 json_object_set_new (dk_json, "denom_pub",
74 TALER_JSON_from_data (buf,
75 buf_len));
76 GNUNET_free (buf);
77 json_object_set_new (dk_json, "value",
78 TALER_JSON_from_amount (TALER_amount_ntoh (dki->value)));
79 json_object_set_new (dk_json,
80 "fee_withdraw",
81 TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_withdraw)));
82 json_object_set_new (dk_json,
83 "fee_deposit",
84 TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_deposit)));
85 json_object_set_new (dk_json,
86 "fee_refresh",
87 TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_refresh)));
88 return dk_json;
89}
90
91
92/**
93 * Convert the public part of a sign key
94 * issue to a JSON object.
95 *
96 * @param ski the sign key issue
97 * @return a JSON object describing the sign key isue (public part)
98 */
99static json_t *
100sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski)
101{
102 json_t *sk_json = json_object ();
103
104 json_object_set_new (sk_json,
105 "stamp_start",
106 TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start)));
107 json_object_set_new (sk_json,
108 "stamp_expire",
109 TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire)));
110 json_object_set_new (sk_json,
111 "master_sig",
112 TALER_JSON_from_data (&ski->signature,
113 sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
114 json_object_set_new (sk_json, "key",
115 TALER_JSON_from_data (&ski->signkey_pub,
116 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,
190 "Duplicate denomination key\n");
191
192 json_array_append_new (ctx->denom_keys_array,
193 denom_key_issue_to_json (&dki->issue));
194
195 return GNUNET_OK;
196}
197
198
199/**
200 * Iterator for sign keys.
201 *
202 * @param cls closure
203 * @param ski the sign key issue
204 * @return #GNUNET_OK to continue to iterate,
205 * #GNUNET_NO to stop iteration with no error,
206 * #GNUNET_SYSERR to abort iteration with error!
207 */
208static int
209reload_keys_sign_iter (void *cls,
210 const struct TALER_MINT_SignKeyIssuePriv *ski)
211{
212 struct MintKeyState *ctx = cls;
213 struct GNUNET_TIME_Absolute stamp_provide;
214
215 stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time,
216 TALER_MINT_conf_duration_provide (cfg));
217
218 if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < ctx->reload_time.abs_value_us)
219 {
220 // this key is expired
221 return GNUNET_OK;
222 }
223
224 if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > stamp_provide.abs_value_us)
225 {
226 // we are to early for this key
227 return GNUNET_OK;
228 }
229
230 // the signkey is valid for now, check
231 // if it's more recent than the current one!
232 if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us >
233 GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us)
234 ctx->current_sign_key_issue = *ski;
235
236
237 ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload,
238 GNUNET_TIME_absolute_ntoh (ski->issue.expire));
239
240 json_array_append_new (ctx->sign_keys_array,
241 sign_key_issue_to_json (&ski->issue));
242
243 return GNUNET_OK;
244}
245
246
247/**
248 * Load the mint's key state from disk.
249 *
250 * @return fresh key state (with reference count 1)
251 */
252static struct MintKeyState *
253reload_keys ()
254{
255 struct MintKeyState *key_state;
256 json_t *keys;
257
258 key_state = GNUNET_new (struct MintKeyState);
259 key_state->refcnt = 1;
260
261 key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS;
262
263 key_state->denom_keys_array = json_array ();
264 GNUNET_assert (NULL != key_state->denom_keys_array);
265
266 key_state->sign_keys_array = json_array ();
267 GNUNET_assert (NULL != key_state->sign_keys_array);
268
269 key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
270 GNUNET_assert (NULL != key_state->denomkey_map);
271
272 key_state->reload_time = GNUNET_TIME_absolute_get ();
273
274 TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state);
275 TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state);
276
277 keys = json_pack ("{s:o, s:o, s:o, s:o}",
278 "master_pub", TALER_JSON_from_data (&master_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)),
279 "signkeys", key_state->sign_keys_array,
280 "denoms", key_state->denom_keys_array,
281 "list_issue_date", TALER_JSON_from_abs (key_state->reload_time));
282
283 key_state->keys_json = json_dumps (keys, JSON_INDENT(2));
284
285 return key_state;
286}
287
288
289/**
290 * Release key state, free if necessary (if reference count gets to zero).
291 *
292 * @param key_state the key state to release
293 */
294void
295TALER_MINT_key_state_release (struct MintKeyState *key_state)
296{
297 GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
298 GNUNET_assert (0 != key_state->refcnt);
299 key_state->refcnt += 1;
300 if (key_state->refcnt == 0) {
301 GNUNET_free (key_state);
302 }
303 GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
304}
305
306
307/**
308 * Acquire the key state of the mint. Updates keys if necessary.
309 * For every call to #TALER_MINT_key_state_acquire, a matching call
310 * to #TALER_MINT_key_state_release must be made.
311 *
312 * @return the key state
313 */
314struct MintKeyState *
315TALER_MINT_key_state_acquire (void)
316{
317 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
318 struct MintKeyState *key_state;
319
320 // FIXME: the locking we have is very coarse-grained,
321 // using multiple locks might be nicer ...
322
323 GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
324 if (NULL == internal_key_state)
325 {
326 internal_key_state = reload_keys ();
327 }
328 else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us)
329 {
330 GNUNET_assert (0 != internal_key_state->refcnt);
331 internal_key_state->refcnt--;
332 if (0 == internal_key_state->refcnt)
333 GNUNET_free (internal_key_state);
334 internal_key_state = reload_keys ();
335 }
336 key_state = internal_key_state;
337 key_state->refcnt += 1;
338 GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
339
340 return key_state;
341}
342
343
344/**
345 * Look up the issue for a denom public key.
346 *
347 * @param key state to look in
348 * @param denom_pub denomination public key
349 * @return the denomination key issue,
350 * or NULL if denom_pub could not be found
351 */
352struct TALER_MINT_DenomKeyIssuePriv *
353TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
354 const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub)
355{
356 struct TALER_MINT_DenomKeyIssuePriv *issue;
357 struct GNUNET_HashCode hash;
358 char *buf;
359 size_t buf_len;
360
361 buf_len = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub,
362 &buf);
363 GNUNET_CRYPTO_hash (buf,
364 buf_len,
365 &hash);
366 GNUNET_free (buf);
367 issue = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, &hash);
368 return issue;
369}
370
371
372/**
373 * Handle a signal, writing relevant signal numbers
374 * (currently just SIGUSR1) to a pipe.
375 *
376 * @param signal_number the signal number
377 */
378static void
379handle_signal (int signal_number)
380{
381 size_t res;
382 char c = signal_number;
383
384 if (SIGUSR1 == signal_number)
385 {
386 errno = 0;
387 res = write (reload_pipe[1], &c, 1);
388 if ((res < 0) && (EINTR != errno))
389 {
390 GNUNET_break (0);
391 return;
392 }
393 if (0 == res)
394 {
395 GNUNET_break (0);
396 return;
397 }
398 }
399}
400
401
402/**
403 * Read signals from a pipe in a loop, and reload keys from disk if
404 * SIGUSR1 is read from the pipe.
405 */
406int
407TALER_MINT_key_reload_loop (void)
408{
409 struct sigaction act;
410
411 if (0 != pipe (reload_pipe))
412 {
413 fprintf (stderr,
414 "Failed to create pipe.\n");
415 return GNUNET_SYSERR;
416 }
417 memset (&act, 0, sizeof (struct sigaction));
418 act.sa_handler = &handle_signal;
419
420 if (0 != sigaction (SIGUSR1, &act, NULL))
421 {
422 fprintf (stderr,
423 "Failed to set signal handler.\n");
424 return GNUNET_SYSERR;
425 }
426
427 while (1)
428 {
429 char c;
430 ssize_t res;
431
432 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
433 "(re-)loading keys\n");
434 GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
435 if (NULL != internal_key_state)
436 {
437 GNUNET_assert (0 != internal_key_state->refcnt);
438 internal_key_state->refcnt -= 1;
439 if (0 == internal_key_state->refcnt)
440 GNUNET_free (internal_key_state);
441 }
442 internal_key_state = reload_keys ();
443 GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
444read_again:
445 errno = 0;
446 res = read (reload_pipe[0], &c, 1);
447 if ((res < 0) && (EINTR != errno))
448 {
449 GNUNET_break (0);
450 return GNUNET_SYSERR;
451 }
452 if (EINTR == errno)
453 goto read_again;
454 }
455 return GNUNET_OK;
456}
457
458
459/* end of taler-mint-httpd_keystate.c */
diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h
new file mode 100644
index 000000000..6f83b0861
--- /dev/null
+++ b/src/mint/taler-mint-httpd_keystate.h
@@ -0,0 +1,129 @@
1/*
2 This file is part of TALER
3 (C) 2014 GNUnet e.V.
4
5 TALER is free software; you can redistribute it and/or modify it under the
6 terms of the GNU Affero 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 Affero General Public License for more details.
12
13 You should have received a copy of the GNU Affero General Public License along with
14 TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file taler-mint-httpd_keystate.h
18 * @brief management of our private signing keys (denomination keys)
19 * @author Florian Dold
20 * @author Benedikt Mueller
21 * @author Christian Grothoff
22 */
23#ifndef TALER_MINT_HTTPD_KEYSTATE_H
24#define TALER_MINT_HTTPD_KEYSTATE_H
25
26
27#include <gnunet/gnunet_util_lib.h>
28#include <microhttpd.h>
29#include <jansson.h>
30#include "taler-mint-httpd.h"
31#include "mint.h"
32#include "key_io.h"
33
34
35/**
36 * Snapshot of the (coin and signing)
37 * keys (including private keys) of the mint.
38 */
39struct MintKeyState
40{
41 /**
42 * When did we initiate the key reloading?
43 */
44 struct GNUNET_TIME_Absolute reload_time;
45
46 /**
47 * JSON array with denomination keys.
48 */
49 json_t *denom_keys_array;
50
51 /**
52 * JSON array with signing keys.
53 */
54 json_t *sign_keys_array;
55
56 /**
57 * Mapping from denomination keys to denomination key issue struct.
58 */
59 struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
60
61 /**
62 * When is the next key invalid and we have to reload?
63 */
64 struct GNUNET_TIME_Absolute next_reload;
65
66 /**
67 * Mint signing key that should be used currently.
68 */
69 struct TALER_MINT_SignKeyIssuePriv current_sign_key_issue;
70
71 /**
72 * Cached JSON text that the mint will send for
73 * a /keys request.
74 */
75 char *keys_json;
76
77 /**
78 * Reference count.
79 */
80 unsigned int refcnt;
81};
82
83
84/**
85 * Acquire the key state of the mint. Updates keys if necessary.
86 * For every call to #TALER_MINT_key_state_acquire(), a matching call
87 * to #TALER_MINT_key_state_release() must be made.
88 *
89 * @return the key state
90 */
91struct MintKeyState *
92TALER_MINT_key_state_acquire (void);
93
94
95/**
96 * Release key state, free if necessary (if reference count gets to zero).
97 *
98 * @param key_state the key state to release
99 */
100void
101TALER_MINT_key_state_release (struct MintKeyState *key_state);
102
103
104/**
105 * Look up the issue for a denom public key.
106 *
107 * @param key state to look in
108 * @param denom_pub denomination public key
109 * @return the denomination key issue,
110 * or NULL if denom_pub could not be found
111 */
112struct TALER_MINT_DenomKeyIssuePriv *
113TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
114 const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub);
115
116
117/**
118 * Read signals from a pipe in a loop, and reload keys from disk if
119 * SIGUSR1 is read from the pipe.
120 *
121 * @return #GNUNET_OK if we terminated normally, #GNUNET_SYSERR on error
122 */
123int
124TALER_MINT_key_reload_loop (void);
125
126
127
128
129#endif