aboutsummaryrefslogtreecommitdiff
path: root/src/mint/taler-mint-httpd_keystate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint/taler-mint-httpd_keystate.c')
-rw-r--r--src/mint/taler-mint-httpd_keystate.c459
1 files changed, 459 insertions, 0 deletions
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 */