aboutsummaryrefslogtreecommitdiff
path: root/src/mint/taler-mint-keyup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint/taler-mint-keyup.c')
-rw-r--r--src/mint/taler-mint-keyup.c657
1 files changed, 657 insertions, 0 deletions
diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c
new file mode 100644
index 000000000..8a1a77882
--- /dev/null
+++ b/src/mint/taler-mint-keyup.c
@@ -0,0 +1,657 @@
1/*
2 This file is part of TALER
3 (C) 2014 Christian Grothoff (and other contributing authors)
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, If not, see <http://www.gnu.org/licenses/>
15*/
16
17/**
18 * @file taler-mint-keyup.c
19 * @brief Update the mint's keys for coins and signatures,
20 * using the mint's offline master key.
21 * @author Florian Dold
22 * @author Benedikt Mueller
23 */
24
25#include <platform.h>
26#include <gnunet/gnunet_util_lib.h>
27#include "taler_util.h"
28#include "taler_signatures.h"
29#include "mint.h"
30
31#define HASH_CUTOFF 20
32
33/**
34 * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
35 */
36#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000);
37
38
39GNUNET_NETWORK_STRUCT_BEGIN
40
41struct CoinTypeNBO
42{
43 struct GNUNET_TIME_RelativeNBO duration_spend;
44 struct GNUNET_TIME_RelativeNBO duration_withdraw;
45 struct TALER_AmountNBO value;
46 struct TALER_AmountNBO fee_withdraw;
47 struct TALER_AmountNBO fee_deposit;
48 struct TALER_AmountNBO fee_refresh;
49};
50
51GNUNET_NETWORK_STRUCT_END
52
53struct CoinTypeParams
54{
55 struct GNUNET_TIME_Relative duration_spend;
56 struct GNUNET_TIME_Relative duration_withdraw;
57 struct GNUNET_TIME_Relative duration_overlap;
58 struct TALER_Amount value;
59 struct TALER_Amount fee_withdraw;
60 struct TALER_Amount fee_deposit;
61 struct TALER_Amount fee_refresh;
62 struct GNUNET_TIME_Absolute anchor;
63};
64
65
66/**
67 * Filename of the master private key.
68 */
69static char *masterkeyfile;
70
71/**
72 * Director of the mint, containing the keys.
73 */
74static char *mintdir;
75
76/**
77 * Time to pretend when the key update is executed.
78 */
79static char *pretend_time_str;
80
81/**
82 * Handle to the mint's configuration
83 */
84static struct GNUNET_CONFIGURATION_Handle *kcfg;
85
86/**
87 * Time when the key update is executed. Either the actual current time, or a
88 * pretended time.
89 */
90static struct GNUNET_TIME_Absolute now;
91
92/**
93 * Master private key of the mint.
94 */
95static struct GNUNET_CRYPTO_EddsaPrivateKey *master_priv;
96
97/**
98 * Master public key of the mint.
99 */
100static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub;
101
102/**
103 * Until what time do we provide keys?
104 */
105static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
106
107
108int
109config_get_denom (const char *section, const char *option, struct TALER_Amount *denom)
110{
111 char *str;
112 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, section, option, &str))
113 return GNUNET_NO;
114 if (GNUNET_OK != TALER_string_to_amount (str, denom))
115 return GNUNET_SYSERR;
116 return GNUNET_OK;
117}
118
119
120char *
121get_signkey_dir ()
122{
123 char *dir;
124 size_t len;
125 len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mintdir);
126 GNUNET_assert (len > 0);
127 return dir;
128}
129
130
131char *
132get_signkey_file (struct GNUNET_TIME_Absolute start)
133{
134 char *dir;
135 size_t len;
136 len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu"),
137 mintdir, (long long) start.abs_value_us);
138 GNUNET_assert (len > 0);
139 return dir;
140}
141
142
143
144/**
145 * Hash the data defining the coin type.
146 * Exclude information that may not be the same for all
147 * instances of the coin type (i.e. the anchor, overlap).
148 */
149void
150hash_coin_type (const struct CoinTypeParams *p, struct GNUNET_HashCode *hash)
151{
152 struct CoinTypeNBO p_nbo;
153
154 memset (&p_nbo, 0, sizeof (struct CoinTypeNBO));
155
156 p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
157 p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
158 p_nbo.value = TALER_amount_hton (p->value);
159 p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw);
160 p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit);
161 p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh);
162
163 GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), hash);
164}
165
166
167static const char *
168get_cointype_dir (const struct CoinTypeParams *p)
169{
170 static char dir[4096];
171 size_t len;
172 struct GNUNET_HashCode hash;
173 char *hash_str;
174 char *val_str;
175 unsigned int i;
176
177 hash_coin_type (p, &hash);
178 hash_str = TALER_data_to_string_alloc (&hash, sizeof (struct GNUNET_HashCode));
179 GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
180 GNUNET_assert (NULL != hash_str);
181 hash_str[HASH_CUTOFF] = 0;
182
183 val_str = TALER_amount_to_string (p->value);
184 for (i = 0; i < strlen (val_str); i++)
185 if (':' == val_str[i] || '.' == val_str[i])
186 val_str[i] = '_';
187
188 len = GNUNET_snprintf (dir, sizeof (dir),
189 ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s"),
190 mintdir, val_str, hash_str);
191 GNUNET_assert (len > 0);
192 GNUNET_free (hash_str);
193 return dir;
194}
195
196
197static const char *
198get_cointype_file (struct CoinTypeParams *p,
199 struct GNUNET_TIME_Absolute start)
200{
201 const char *dir;
202 static char filename[4096];
203 size_t len;
204 dir = get_cointype_dir (p);
205 len = GNUNET_snprintf (filename, sizeof (filename), ("%s" DIR_SEPARATOR_STR "%llu"),
206 dir, (unsigned long long) start.abs_value_us);
207 GNUNET_assert (len > 0);
208 return filename;
209}
210
211
212/**
213 * Get the latest key file from the past.
214 *
215 * @param cls closure
216 * @param filename complete filename (absolute path)
217 * @return #GNUNET_OK to continue to iterate,
218 * #GNUNET_NO to stop iteration with no error,
219 * #GNUNET_SYSERR to abort iteration with error!
220 */
221static int
222get_anchor_iter (void *cls,
223 const char *filename)
224{
225 struct GNUNET_TIME_Absolute stamp;
226 struct GNUNET_TIME_Absolute *anchor = cls;
227 const char *base;
228 char *end = NULL;
229
230 base = GNUNET_STRINGS_get_short_name (filename);
231 stamp.abs_value_us = strtol (base, &end, 10);
232
233 if ((NULL == end) || (0 != *end))
234 {
235 fprintf(stderr, "Ignoring unexpected file '%s'.\n", filename);
236 return GNUNET_OK;
237 }
238
239 // TODO: check if it's actually a valid key file
240
241 if ((stamp.abs_value_us <= now.abs_value_us) && (stamp.abs_value_us > anchor->abs_value_us))
242 *anchor = stamp;
243
244 return GNUNET_OK;
245}
246
247
248/**
249 * Get the timestamp where the first new key should be generated.
250 * Relies on correctly named key files.
251 *
252 * @param dir directory with the signed stuff
253 * @param duration how long is one key valid?
254 * @param overlap what's the overlap between the keys validity period?
255 * @param[out] anchor the timestamp where the first new key should be generated
256 */
257void
258get_anchor (const char *dir,
259 struct GNUNET_TIME_Relative duration,
260 struct GNUNET_TIME_Relative overlap,
261 struct GNUNET_TIME_Absolute *anchor)
262{
263 GNUNET_assert (0 == duration.rel_value_us % 1000000);
264 GNUNET_assert (0 == overlap.rel_value_us % 1000000);
265 if (GNUNET_YES != GNUNET_DISK_directory_test (dir, GNUNET_YES))
266 {
267 *anchor = now;
268 printf ("Can't look for anchor (%s)\n", dir);
269 return;
270 }
271
272 *anchor = GNUNET_TIME_UNIT_ZERO_ABS;
273 if (-1 == GNUNET_DISK_directory_scan (dir, &get_anchor_iter, anchor))
274 {
275 *anchor = now;
276 return;
277 }
278
279 if ((GNUNET_TIME_absolute_add (*anchor, duration)).abs_value_us < now.abs_value_us)
280 {
281 // there's no good anchor, start from now
282 // (existing keys are too old)
283 *anchor = now;
284 }
285 else if (anchor->abs_value_us != now.abs_value_us)
286 {
287 // we have a good anchor
288 *anchor = GNUNET_TIME_absolute_add (*anchor, duration);
289 *anchor = GNUNET_TIME_absolute_subtract (*anchor, overlap);
290 }
291 // anchor is now the stamp where we need to create a new key
292}
293
294static void
295create_signkey_issue (struct GNUNET_TIME_Absolute start,
296 struct GNUNET_TIME_Relative duration,
297 struct TALER_MINT_SignKeyIssue *issue)
298{
299 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
300
301 priv = GNUNET_CRYPTO_eddsa_key_create ();
302 GNUNET_assert (NULL != priv);
303 issue->signkey_priv = *priv;
304 GNUNET_free (priv);
305 issue->master_pub = *master_pub;
306 issue->start = GNUNET_TIME_absolute_hton (start);
307 issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, duration));
308
309 GNUNET_CRYPTO_eddsa_key_get_public (&issue->signkey_priv, &issue->signkey_pub);
310
311 issue->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY);
312 issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose));
313
314 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (master_priv, &issue->purpose, &issue->signature))
315 {
316 GNUNET_abort ();
317 }
318}
319
320
321static int
322check_signkey_valid (const char *signkey_filename)
323{
324 // FIXME: do real checks
325 return GNUNET_OK;
326}
327
328
329int
330mint_keys_update_signkeys ()
331{
332 struct GNUNET_TIME_Relative signkey_duration;
333 struct GNUNET_TIME_Absolute anchor;
334 char *signkey_dir;
335
336 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "signkey_duration", &signkey_duration))
337 {
338 fprintf (stderr, "Can't read config value mint_keys.signkey_duration\n");
339 return GNUNET_SYSERR;
340 }
341 ROUND_TO_SECS (signkey_duration, rel_value_us);
342 signkey_dir = get_signkey_dir ();
343 // make sure the directory exists
344 if (GNUNET_OK != GNUNET_DISK_directory_create (signkey_dir))
345 {
346 fprintf (stderr, "Cant create signkey dir\n");
347 return GNUNET_SYSERR;
348 }
349
350 get_anchor (signkey_dir, signkey_duration, GNUNET_TIME_UNIT_ZERO, &anchor);
351
352 while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) {
353 char *skf;
354 skf = get_signkey_file (anchor);
355 if (GNUNET_YES != GNUNET_DISK_file_test (skf))
356 {
357 struct TALER_MINT_SignKeyIssue signkey_issue;
358 ssize_t nwrite;
359 printf ("Generating signing key for %s.\n", GNUNET_STRINGS_absolute_time_to_string (anchor));
360 create_signkey_issue (anchor, signkey_duration, &signkey_issue);
361 nwrite = GNUNET_DISK_fn_write (skf, &signkey_issue, sizeof (struct TALER_MINT_SignKeyIssue),
362 (GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ));
363 if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue))
364 {
365 fprintf (stderr, "Can't write to file '%s'\n", skf);
366 return GNUNET_SYSERR;
367 }
368 }
369 else if (GNUNET_OK != check_signkey_valid (skf))
370 {
371 return GNUNET_SYSERR;
372 }
373 anchor = GNUNET_TIME_absolute_add (anchor, signkey_duration);
374 }
375 return GNUNET_OK;
376}
377
378
379int
380get_cointype_params (const char *ct, struct CoinTypeParams *params)
381{
382 const char *dir;
383 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_withdraw", ct, &params->duration_withdraw))
384 {
385 fprintf (stderr, "Withdraw duration not given for coin type '%s'\n", ct);
386 return GNUNET_SYSERR;
387 }
388 ROUND_TO_SECS (params->duration_withdraw, rel_value_us);
389 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_spend", ct, &params->duration_spend))
390 {
391 fprintf (stderr, "Spend duration not given for coin type '%s'\n", ct);
392 return GNUNET_SYSERR;
393 }
394 ROUND_TO_SECS (params->duration_spend, rel_value_us);
395 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_overlap", ct, &params->duration_overlap))
396 {
397 fprintf (stderr, "Overlap duration not given for coin type '%s'\n", ct);
398 return GNUNET_SYSERR;
399 }
400 ROUND_TO_SECS (params->duration_overlap, rel_value_us);
401
402 if (GNUNET_OK != config_get_denom ("mint_denom_value", ct, &params->value))
403 {
404 fprintf (stderr, "Value not given for coin type '%s'\n", ct);
405 return GNUNET_SYSERR;
406 }
407
408 if (GNUNET_OK != config_get_denom ("mint_denom_fee_withdraw", ct, &params->fee_withdraw))
409 {
410 fprintf (stderr, "Withdraw fee not given for coin type '%s'\n", ct);
411 return GNUNET_SYSERR;
412 }
413
414 if (GNUNET_OK != config_get_denom ("mint_denom_fee_deposit", ct, &params->fee_deposit))
415 {
416 fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct);
417 return GNUNET_SYSERR;
418 }
419
420 if (GNUNET_OK != config_get_denom ("mint_denom_fee_refresh", ct, &params->fee_refresh))
421 {
422 fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct);
423 return GNUNET_SYSERR;
424 }
425
426 dir = get_cointype_dir (params);
427 get_anchor (dir, params->duration_spend, params->duration_overlap, &params->anchor);
428 return GNUNET_OK;
429}
430
431
432static void
433create_denomkey_issue (struct CoinTypeParams *params, struct TALER_MINT_DenomKeyIssue *dki)
434{
435 GNUNET_assert (NULL != (dki->denom_priv = TALER_RSA_key_create ()));
436 TALER_RSA_key_get_public (dki->denom_priv, &dki->denom_pub);
437 dki->master = *master_pub;
438 dki->start = GNUNET_TIME_absolute_hton (params->anchor);
439 dki->expire_withdraw =
440 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
441 params->duration_withdraw));
442 dki->expire_spend =
443 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
444 params->duration_spend));
445 dki->value = TALER_amount_hton (params->value);
446 dki->fee_withdraw = TALER_amount_hton (params->fee_withdraw);
447 dki->fee_deposit = TALER_amount_hton (params->fee_deposit);
448 dki->fee_refresh = TALER_amount_hton (params->fee_refresh);
449
450 dki->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM);
451 dki->purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssue) - offsetof (struct TALER_MINT_DenomKeyIssue, purpose));
452
453 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (master_priv, &dki->purpose, &dki->signature))
454 {
455 GNUNET_abort ();
456 }
457}
458
459
460static int
461check_cointype_valid (const char *filename, struct CoinTypeParams *params)
462{
463 // FIXME: add real checks
464 return GNUNET_OK;
465}
466
467
468int
469mint_keys_update_cointype (const char *coin_alias)
470{
471 struct CoinTypeParams p;
472 const char *cointype_dir;
473
474 if (GNUNET_OK != get_cointype_params (coin_alias, &p))
475 return GNUNET_SYSERR;
476
477 cointype_dir = get_cointype_dir (&p);
478 if (GNUNET_OK != GNUNET_DISK_directory_create (cointype_dir))
479 return GNUNET_SYSERR;
480
481 while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) {
482 const char *dkf;
483 dkf = get_cointype_file (&p, p.anchor);
484
485 if (GNUNET_YES != GNUNET_DISK_file_test (dkf))
486 {
487 struct TALER_MINT_DenomKeyIssue denomkey_issue;
488 int ret;
489 printf ("Generating denomination key for type '%s', start %s.\n",
490 coin_alias, GNUNET_STRINGS_absolute_time_to_string (p.anchor));
491 printf ("Target path: %s\n", dkf);
492 create_denomkey_issue (&p, &denomkey_issue);
493 ret = TALER_MINT_write_denom_key (dkf, &denomkey_issue);
494 TALER_RSA_key_free (denomkey_issue.denom_priv);
495 if (GNUNET_OK != ret)
496 {
497 fprintf (stderr, "Can't write to file '%s'\n", dkf);
498 return GNUNET_SYSERR;
499 }
500 }
501 else if (GNUNET_OK != check_cointype_valid (dkf, &p))
502 {
503 return GNUNET_SYSERR;
504 }
505 p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend);
506 p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, p.duration_overlap);
507 }
508 return GNUNET_OK;
509}
510
511
512int
513mint_keys_update_denomkeys ()
514{
515 char *coin_types;
516 char *ct;
517 char *tok_ctx;
518
519 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint_keys", "coin_types", &coin_types))
520 {
521 fprintf (stderr, "mint_keys.coin_types not in configuration\n");
522 return GNUNET_SYSERR;
523 }
524
525 for (ct = strtok_r (coin_types, " ", &tok_ctx);
526 ct != NULL;
527 ct = strtok_r (NULL, " ", &tok_ctx))
528 {
529 if (GNUNET_OK != mint_keys_update_cointype (ct))
530 {
531 GNUNET_free (coin_types);
532 return GNUNET_SYSERR;
533 }
534 }
535 GNUNET_free (coin_types);
536 return GNUNET_OK;
537}
538
539
540static int
541mint_keys_update ()
542{
543 int ret;
544 struct GNUNET_TIME_Relative lookahead_sign;
545 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "lookahead_sign", &lookahead_sign))
546 {
547 fprintf (stderr, "mint_keys.lookahead_sign not found\n");
548 return GNUNET_SYSERR;
549 }
550 if (lookahead_sign.rel_value_us == 0)
551 {
552 fprintf (stderr, "mint_keys.lookahead_sign must not be zero\n");
553 return GNUNET_SYSERR;
554 }
555 ROUND_TO_SECS (lookahead_sign, rel_value_us);
556 lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, lookahead_sign);
557
558 ret = mint_keys_update_signkeys ();
559 if (GNUNET_OK != ret)
560 return GNUNET_SYSERR;
561
562 return mint_keys_update_denomkeys ();
563}
564
565
566/**
567 * The main function of the keyup tool
568 *
569 * @param argc number of arguments from the command line
570 * @param argv command line arguments
571 * @return 0 ok, 1 on error
572 */
573int
574main (int argc, char *const *argv)
575{
576 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
577 GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
578 {'m', "master-key", "FILE",
579 "master key file (private key)", 1,
580 &GNUNET_GETOPT_set_filename, &masterkeyfile},
581 {'d', "mint-dir", "DIR",
582 "mint directory with keys to update", 1,
583 &GNUNET_GETOPT_set_filename, &mintdir},
584 {'t', "time", "TIMESTAMP",
585 "pretend it is a different time for the update", 0,
586 &GNUNET_GETOPT_set_string, &pretend_time_str},
587 GNUNET_GETOPT_OPTION_END
588 };
589
590 GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keyup", "WARNING", NULL));
591
592 if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
593 return 1;
594 if (NULL == mintdir)
595 {
596 fprintf (stderr, "mint directory not given\n");
597 return 1;
598 }
599
600 if (NULL != pretend_time_str)
601 {
602 if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, &now))
603 {
604 fprintf (stderr, "timestamp invalid\n");
605 return 1;
606 }
607 }
608 else
609 {
610 now = GNUNET_TIME_absolute_get ();
611 }
612 ROUND_TO_SECS (now, abs_value_us);
613
614 kcfg = TALER_MINT_config_load (mintdir);
615 if (NULL == kcfg)
616 {
617 fprintf (stderr, "can't load mint configuration\n");
618 return 1;
619 }
620
621 if (NULL == masterkeyfile)
622 {
623 fprintf (stderr, "master key file not given\n");
624 return 1;
625 }
626 master_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
627 if (NULL == master_priv)
628 {
629 fprintf (stderr, "master key invalid\n");
630 return 1;
631 }
632
633 master_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
634 GNUNET_CRYPTO_eddsa_key_get_public (master_priv, master_pub);
635
636 // check if key from file matches the one from the configuration
637 {
638 struct GNUNET_CRYPTO_EddsaPublicKey master_pub_from_cfg;
639 if (GNUNET_OK != TALER_configuration_get_data (kcfg, "mint", "master_pub",
640 &master_pub_from_cfg,
641 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
642 {
643 fprintf (stderr, "master key missing in configuration (mint.master_pub)\n");
644 return 1;
645 }
646 if (0 != memcmp (master_pub, &master_pub_from_cfg, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
647 {
648 fprintf (stderr, "Mismatch between key from mint configuration and master private key file from command line.\n");
649 return 1;
650 }
651 }
652
653 if (GNUNET_OK != mint_keys_update ())
654 return 1;
655 return 0;
656}
657