aboutsummaryrefslogtreecommitdiff
path: root/src/reducer/anastasis_api_backup_redux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reducer/anastasis_api_backup_redux.c')
-rw-r--r--src/reducer/anastasis_api_backup_redux.c4893
1 files changed, 4893 insertions, 0 deletions
diff --git a/src/reducer/anastasis_api_backup_redux.c b/src/reducer/anastasis_api_backup_redux.c
new file mode 100644
index 0000000..cea1360
--- /dev/null
+++ b/src/reducer/anastasis_api_backup_redux.c
@@ -0,0 +1,4893 @@
1/*
2 This file is part of Anastasis
3 Copyright (C) 2020, 2021 Taler Systems SA
4
5 Anastasis is free software; you can redistribute it and/or modify it under the
6 terms of the GNU Lesser General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 Anastasis 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 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file lib/anastasis_api_backup_redux.c
18 * @brief anastasis reducer backup api
19 * @author Christian Grothoff
20 * @author Dominik Meister
21 * @author Dennis Neufeld
22 */
23
24#include "platform.h"
25#include "anastasis_redux.h"
26#include "anastasis_api_redux.h"
27#include <taler/taler_merchant_service.h>
28
29/**
30 * How long do Anastasis providers store data if the service
31 * is free? Must match #ANASTASIS_MAX_YEARS_STORAGE from
32 * anastasis-httpd.h.
33 */
34#define ANASTASIS_FREE_STORAGE GNUNET_TIME_relative_multiply ( \
35 GNUNET_TIME_UNIT_YEARS, 5)
36
37/**
38 * CPU limiter: do not evaluate more than 16k
39 * possible policy combinations to find the "best"
40 * policy.
41 */
42#define MAX_EVALUATIONS (1024 * 16)
43
44
45#define GENERATE_STRING(STRING) #STRING,
46static const char *backup_strings[] = {
47 ANASTASIS_BACKUP_STATES (GENERATE_STRING)
48};
49#undef GENERATE_STRING
50
51
52/**
53 * Linked list of costs.
54 */
55struct Costs
56{
57
58 /**
59 * Kept in a LL.
60 */
61 struct Costs *next;
62
63 /**
64 * Cost in one of the currencies.
65 */
66 struct TALER_Amount cost;
67};
68
69
70/**
71 * Add amount from @a cost to @a my_cost list.
72 *
73 * @param[in,out] my_cost pointer to list to modify
74 * @param cost amount to add
75 */
76static void
77add_cost (struct Costs **my_cost,
78 const struct TALER_Amount *cost)
79{
80 for (struct Costs *pos = *my_cost;
81 NULL != pos;
82 pos = pos->next)
83 {
84 if (GNUNET_OK !=
85 TALER_amount_cmp_currency (&pos->cost,
86 cost))
87 continue;
88 GNUNET_assert (0 <=
89 TALER_amount_add (&pos->cost,
90 &pos->cost,
91 cost));
92 return;
93 }
94 {
95 struct Costs *nc;
96
97 nc = GNUNET_new (struct Costs);
98 nc->cost = *cost;
99 nc->next = *my_cost;
100 *my_cost = nc;
101 }
102}
103
104
105enum ANASTASIS_BackupState
106ANASTASIS_backup_state_from_string_ (const char *state_string)
107{
108 for (enum ANASTASIS_BackupState i = 0;
109 i < sizeof (backup_strings) / sizeof(*backup_strings);
110 i++)
111 if (0 == strcmp (state_string,
112 backup_strings[i]))
113 return i;
114 return ANASTASIS_BACKUP_STATE_ERROR;
115}
116
117
118const char *
119ANASTASIS_backup_state_to_string_ (enum ANASTASIS_BackupState bs)
120{
121 if ( (bs < 0) ||
122 (bs >= sizeof (backup_strings) / sizeof(*backup_strings)) )
123 {
124 GNUNET_break_op (0);
125 return NULL;
126 }
127 return backup_strings[bs];
128}
129
130
131/**
132 * Update the 'backup_state' field of @a state to @a new_backup_state.
133 *
134 * @param[in,out] state the state to transition
135 * @param new_backup_state the state to transition to
136 */
137static void
138set_state (json_t *state,
139 enum ANASTASIS_BackupState new_backup_state)
140{
141 GNUNET_assert (
142 0 ==
143 json_object_set_new (
144 state,
145 "backup_state",
146 json_string (ANASTASIS_backup_state_to_string_ (new_backup_state))));
147}
148
149
150/**
151 * Returns an initial ANASTASIS backup state (CONTINENT_SELECTING).
152 *
153 * @param cfg handle for gnunet configuration
154 * @return NULL on failure
155 */
156json_t *
157ANASTASIS_backup_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
158{
159 json_t *initial_state;
160
161 (void) cfg;
162 initial_state = ANASTASIS_REDUX_load_continents_ ();
163 if (NULL == initial_state)
164 return NULL;
165 set_state (initial_state,
166 ANASTASIS_BACKUP_STATE_CONTINENT_SELECTING);
167 return initial_state;
168}
169
170
171/**
172 * Test if @a challenge_size is small enough for the provider's
173 * @a size_limit_in_mb.
174 *
175 * We add 1024 to @a challenge_size here as a "safety margin" as
176 * the encrypted challenge has some additional headers around it
177 *
178 * @param size_limit_in_mb provider's upload limit
179 * @param challenge_size actual binary size of the challenge
180 * @return true if this fits
181 */
182static bool
183challenge_size_ok (uint32_t size_limit_in_mb,
184 size_t challenge_size)
185{
186 return (size_limit_in_mb * 1024LLU * 1024LLU >=
187 challenge_size + 1024LLU);
188}
189
190
191/**
192 * DispatchHandler/Callback function which is called for a
193 * "add_authentication" action.
194 * Returns an #ANASTASIS_ReduxAction if operation is async.
195 *
196 * @param state state to operate on
197 * @param arguments arguments to use for operation on state
198 * @param cb callback to call during/after operation
199 * @param cb_cls callback closure
200 * @return NULL
201 */
202static struct ANASTASIS_ReduxAction *
203add_authentication (json_t *state,
204 const json_t *arguments,
205 ANASTASIS_ActionCallback cb,
206 void *cb_cls)
207{
208 json_t *auth_providers;
209 json_t *method;
210 const char *method_type;
211 void *challenge;
212 size_t challenge_size;
213 struct GNUNET_JSON_Specification spec[] = {
214 GNUNET_JSON_spec_string ("type",
215 &method_type),
216 GNUNET_JSON_spec_varsize ("challenge",
217 &challenge,
218 &challenge_size),
219 GNUNET_JSON_spec_end ()
220 };
221
222 auth_providers = json_object_get (state,
223 "authentication_providers");
224 if (NULL == auth_providers)
225 {
226 GNUNET_break (0);
227 ANASTASIS_redux_fail_ (cb,
228 cb_cls,
229 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
230 "'authentication_providers' missing");
231 return NULL;
232 }
233
234 method = json_object_get (arguments,
235 "authentication_method");
236 if (NULL == method)
237 {
238 GNUNET_break (0);
239 ANASTASIS_redux_fail_ (cb,
240 cb_cls,
241 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
242 "'authentication_method' required");
243 return NULL;
244 }
245 if (GNUNET_OK !=
246 GNUNET_JSON_parse (method,
247 spec,
248 NULL, NULL))
249 {
250 GNUNET_break (0);
251 json_dumpf (method,
252 stderr,
253 JSON_INDENT (2));
254 ANASTASIS_redux_fail_ (cb,
255 cb_cls,
256 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
257 "'authentication_method' content malformed");
258 return NULL;
259 }
260 /* Check we know at least one provider that supports this method */
261 {
262 bool found = false;
263 bool too_big = false;
264 json_t *details;
265 const char *url;
266
267 json_object_foreach (auth_providers, url, details)
268 {
269 json_t *methods;
270 json_t *method;
271 size_t index;
272 uint32_t size_limit_in_mb;
273 struct GNUNET_JSON_Specification ispec[] = {
274 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
275 &size_limit_in_mb),
276 GNUNET_JSON_spec_json ("methods",
277 &methods),
278 GNUNET_JSON_spec_end ()
279 };
280
281 if (GNUNET_OK !=
282 GNUNET_JSON_parse (details,
283 ispec,
284 NULL, NULL))
285 {
286 GNUNET_break (0);
287 continue;
288 }
289 json_array_foreach (methods, index, method)
290 {
291 const char *type;
292
293 type = json_string_value (json_object_get (method,
294 "type"));
295 GNUNET_break (NULL != type);
296 if ( (NULL != type) &&
297 (0 == strcmp (type,
298 method_type)) )
299 {
300 found = true;
301 break;
302 }
303 }
304 GNUNET_JSON_parse_free (ispec);
305 if (! challenge_size_ok (size_limit_in_mb,
306 challenge_size))
307 {
308 /* Challenge data too big for this provider. Try to find another one.
309 Note: we add 1024 to challenge-size here as a "safety margin" as
310 the encrypted challenge has some additional headers around it */
311 too_big = true;
312 found = false;
313 }
314 if (found)
315 break;
316 }
317 if (! found)
318 {
319 if (too_big)
320 {
321 ANASTASIS_redux_fail_ (cb,
322 cb_cls,
323 TALER_EC_ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG,
324 method_type);
325 }
326 else
327 {
328 ANASTASIS_redux_fail_ (cb,
329 cb_cls,
330 TALER_EC_ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED,
331 method_type);
332 }
333 GNUNET_JSON_parse_free (spec);
334 return NULL;
335 }
336 }
337 GNUNET_JSON_parse_free (spec);
338
339 /* append provided method to our array */
340 {
341 json_t *auth_method_arr;
342
343 auth_method_arr = json_object_get (state,
344 "authentication_methods");
345 if (NULL == auth_method_arr)
346 {
347 auth_method_arr = json_array ();
348 GNUNET_assert (0 == json_object_set_new (state,
349 "authentication_methods",
350 auth_method_arr));
351 }
352 if (! json_is_array (auth_method_arr))
353 {
354 GNUNET_break (0);
355 ANASTASIS_redux_fail_ (cb,
356 cb_cls,
357 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
358 "'authentication_methods' must be an array");
359 return NULL;
360 }
361 GNUNET_assert (0 ==
362 json_array_append (auth_method_arr,
363 method));
364 cb (cb_cls,
365 TALER_EC_NONE,
366 state);
367 }
368 return NULL;
369}
370
371
372/**
373 * DispatchHandler/Callback function which is called for a
374 * "delete_authentication" action.
375 * Returns an #ANASTASIS_ReduxAction if operation is async.
376 *
377 * @param state state to operate on
378 * @param arguments arguments to use for operation on state
379 * @param cb callback to call during/after operation
380 * @param cb_cls callback closure
381 * @return NULL
382 */
383static struct ANASTASIS_ReduxAction *
384del_authentication (json_t *state,
385 const json_t *arguments,
386 ANASTASIS_ActionCallback cb,
387 void *cb_cls)
388{
389 json_t *idx;
390 json_t *auth_method_arr;
391
392 auth_method_arr = json_object_get (state,
393 "authentication_methods");
394 if (! json_is_array (auth_method_arr))
395 {
396 ANASTASIS_redux_fail_ (cb,
397 cb_cls,
398 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
399 "'authentication_methods' must be an array");
400 return NULL;
401 }
402 if (NULL == arguments)
403 {
404 ANASTASIS_redux_fail_ (cb,
405 cb_cls,
406 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
407 "arguments missing");
408 return NULL;
409 }
410 idx = json_object_get (arguments,
411 "authentication_method");
412 if ( (NULL == idx) ||
413 (! json_is_integer (idx)) )
414 {
415 ANASTASIS_redux_fail_ (cb,
416 cb_cls,
417 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
418 "'authentication_method' must be a number");
419 return NULL;
420 }
421
422 {
423 size_t index = (size_t) json_integer_value (idx);
424
425 if (0 != json_array_remove (auth_method_arr,
426 index))
427 {
428 ANASTASIS_redux_fail_ (cb,
429 cb_cls,
430 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
431 "removal failed");
432 return NULL;
433 }
434 }
435 cb (cb_cls,
436 TALER_EC_NONE,
437 state);
438 return NULL;
439}
440
441
442/* ********************** done_authentication ******************** */
443
444/**
445 * Which provider would be used for the given challenge,
446 * and at what cost?
447 */
448struct PolicyEntry
449{
450 /**
451 * URL of the provider.
452 */
453 const char *provider_name;
454
455 /**
456 * Recovery fee.
457 */
458 struct TALER_Amount usage_fee;
459};
460
461
462/**
463 * Map from challenges to providers.
464 */
465struct PolicyMap
466{
467 /**
468 * Kept in a DLL.
469 */
470 struct PolicyMap *next;
471
472 /**
473 * Kept in a DLL.
474 */
475 struct PolicyMap *prev;
476
477 /**
478 * Array of proividers selected for each challenge,
479 * with associated costs.
480 * Length of the array will be 'req_methods'.
481 */
482 struct PolicyEntry *providers;
483
484 /**
485 * Diversity score for this policy mapping.
486 */
487 unsigned int diversity;
488
489};
490
491
492/**
493 * Array of challenges for a policy, and DLL with
494 * possible mappings of challenges to providers.
495 */
496struct Policy
497{
498
499 /**
500 * Kept in DLL of all possible policies.
501 */
502 struct Policy *next;
503
504 /**
505 * Kept in DLL of all possible policies.
506 */
507 struct Policy *prev;
508
509 /**
510 * Head of DLL.
511 */
512 struct PolicyMap *pm_head;
513
514 /**
515 * Tail of DLL.
516 */
517 struct PolicyMap *pm_tail;
518
519 /**
520 * Challenges selected for this policy.
521 * Length of the array will be 'req_methods'.
522 */
523 unsigned int *challenges;
524
525};
526
527
528/**
529 * Information for running done_authentication() logic.
530 */
531struct PolicyBuilder
532{
533 /**
534 * Authentication providers available overall, from our state.
535 */
536 json_t *providers;
537
538 /**
539 * Authentication methods available overall, from our state.
540 */
541 const json_t *methods;
542
543 /**
544 * Head of DLL of all possible policies.
545 */
546 struct Policy *p_head;
547
548 /**
549 * Tail of DLL of all possible policies.
550 */
551 struct Policy *p_tail;
552
553 /**
554 * Array of authentication policies to be computed.
555 */
556 json_t *policies;
557
558 /**
559 * Array of length @e req_methods.
560 */
561 unsigned int *m_idx;
562
563 /**
564 * Array of length @e req_methods identifying a set of providers selected
565 * for each authentication method, while we are trying to compute the
566 * 'best' allocation of providers to authentication methods.
567 * Only valid during the go_with() function.
568 */
569 const char **best_sel;
570
571 /**
572 * Error hint to return on failure. Set if @e ec is not #TALER_EC_NONE.
573 */
574 const char *hint;
575
576 /**
577 * Policy we are currently building maps for.
578 */
579 struct Policy *current_policy;
580
581 /**
582 * LL of costs associated with the currently preferred
583 * policy.
584 */
585 struct Costs *best_cost;
586
587 /**
588 * Array of 'best' policy maps found so far,
589 * ordered by policy.
590 */
591 struct PolicyMap *best_map;
592
593 /**
594 * Array of the currency policy maps under evaluation
595 * by find_best_map().
596 */
597 struct PolicyMap *curr_map;
598
599 /**
600 * How many mappings have we evaluated so far?
601 * Used to limit the computation by aborting after
602 * #MAX_EVALUATIONS trials.
603 */
604 unsigned int evaluations;
605
606 /**
607 * Overall number of challenges provided by the user.
608 */
609 unsigned int num_methods;
610
611 /**
612 * Number of challenges that must be satisfied to recover the secret.
613 * Derived from the total number of challenges entered by the user.
614 */
615 unsigned int req_methods;
616
617 /**
618 * Number of different Anastasis providers selected in @e best_sel.
619 * Only valid during the go_with() function.
620 */
621 unsigned int best_diversity;
622
623 /**
624 * Number of identical challenges duplicated at
625 * various providers in the best case. Smaller is
626 * better.
627 */
628 unsigned int best_duplicates;
629
630 /**
631 * Error code to return, #TALER_EC_NONE on success.
632 */
633 enum TALER_ErrorCode ec;
634
635};
636
637
638/**
639 * Free @a costs LL.
640 *
641 * @param[in] costs linked list to free
642 */
643static void
644free_costs (struct Costs *costs)
645{
646 if (NULL == costs)
647 return;
648 free_costs (costs->next);
649 GNUNET_free (costs);
650}
651
652
653/**
654 * Check if providers @a p1 and @a p2 have equivalent
655 * methods and cost structures.
656 *
657 * @return true if the providers are fully equivalent
658 */
659static bool
660equiv_provider (struct PolicyBuilder *pb,
661 const char *p1,
662 const char *p2)
663{
664 json_t *j1;
665 json_t *j2;
666 json_t *m1;
667 json_t *m2;
668 struct TALER_Amount uc1;
669 struct TALER_Amount uc2;
670
671 j1 = json_object_get (pb->providers,
672 p1);
673 j2 = json_object_get (pb->providers,
674 p2);
675 if ( (NULL == j1) ||
676 (NULL == j2) )
677 {
678 GNUNET_break (0);
679 return false;
680 }
681
682 {
683 struct GNUNET_JSON_Specification s1[] = {
684 GNUNET_JSON_spec_json ("methods",
685 &m1),
686 TALER_JSON_spec_amount_any ("truth_upload_fee",
687 &uc1),
688 GNUNET_JSON_spec_end ()
689 };
690
691 if (GNUNET_OK !=
692 GNUNET_JSON_parse (j1,
693 s1,
694 NULL, NULL))
695 {
696 GNUNET_break (0);
697 return false;
698 }
699 }
700
701 {
702 struct GNUNET_JSON_Specification s2[] = {
703 GNUNET_JSON_spec_json ("methods",
704 &m2),
705 TALER_JSON_spec_amount_any ("truth_upload_fee",
706 &uc2),
707 GNUNET_JSON_spec_end ()
708 };
709
710 if (GNUNET_OK !=
711 GNUNET_JSON_parse (j2,
712 s2,
713 NULL, NULL))
714 {
715 GNUNET_break (0);
716 return false;
717 }
718 }
719
720 if ( (GNUNET_OK !=
721 TALER_amount_cmp_currency (&uc1,
722 &uc2)) ||
723 (0 !=
724 TALER_amount_cmp (&uc1,
725 &uc2)) )
726 return false;
727
728 if (json_array_size (m1) != json_array_size (m2))
729 return false;
730 {
731 size_t idx1;
732 json_t *e1;
733
734 json_array_foreach (m1, idx1, e1)
735 {
736 const char *type1;
737 struct TALER_Amount fee1;
738 struct GNUNET_JSON_Specification s1[] = {
739 GNUNET_JSON_spec_string ("type",
740 &type1),
741 TALER_JSON_spec_amount_any ("usage_fee",
742 &fee1),
743 GNUNET_JSON_spec_end ()
744 };
745 bool matched = false;
746
747 if (GNUNET_OK !=
748 GNUNET_JSON_parse (e1,
749 s1,
750 NULL, NULL))
751 {
752 GNUNET_break (0);
753 return false;
754 }
755 {
756 size_t idx2;
757 json_t *e2;
758
759 json_array_foreach (m2, idx2, e2)
760 {
761 const char *type2;
762 struct TALER_Amount fee2;
763 struct GNUNET_JSON_Specification s2[] = {
764 GNUNET_JSON_spec_string ("type",
765 &type2),
766 TALER_JSON_spec_amount_any ("usage_fee",
767 &fee2),
768 GNUNET_JSON_spec_end ()
769 };
770
771 if (GNUNET_OK !=
772 GNUNET_JSON_parse (e2,
773 s2,
774 NULL, NULL))
775 {
776 GNUNET_break (0);
777 return false;
778 }
779 if ( (0 == strcmp (type1,
780 type2)) &&
781 (GNUNET_OK ==
782 TALER_amount_cmp_currency (&fee1,
783 &fee2)) &&
784 (0 == TALER_amount_cmp (&fee1,
785 &fee2)) )
786 {
787 matched = true;
788 break;
789 }
790 }
791 }
792 if (! matched)
793 return false;
794 }
795 }
796 return true;
797}
798
799
800/**
801 * Evaluate the cost/benefit of the provider selection in @a prov_sel
802 * and if it is better then the best known one in @a pb, update @a pb.
803 *
804 * @param[in,out] pb our operational context
805 * @param[in,out] prov_sel array of req_methods provider indices to complete
806 */
807static void
808eval_provider_selection (struct PolicyBuilder *pb,
809 const char *prov_sel[])
810{
811 unsigned int curr_diversity;
812 struct PolicyEntry policy_ent[pb->req_methods];
813
814 for (unsigned int i = 0; i < pb->req_methods; i++)
815 {
816 const json_t *method_obj = json_array_get (pb->methods,
817 pb->m_idx[i]);
818 const json_t *provider_cfg = json_object_get (pb->providers,
819 prov_sel[i]);
820 json_t *provider_methods;
821 const char *method_type;
822 json_t *md;
823 size_t index;
824 bool found = false;
825 uint32_t size_limit_in_mb;
826 struct TALER_Amount upload_cost;
827 struct GNUNET_JSON_Specification pspec[] = {
828 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
829 &size_limit_in_mb),
830 GNUNET_JSON_spec_json ("methods",
831 &provider_methods),
832 TALER_JSON_spec_amount_any ("truth_upload_fee",
833 &upload_cost),
834 GNUNET_JSON_spec_end ()
835 };
836 void *challenge;
837 size_t challenge_size;
838 struct GNUNET_JSON_Specification mspec[] = {
839 GNUNET_JSON_spec_string ("type",
840 &method_type),
841 GNUNET_JSON_spec_varsize ("challenge",
842 &challenge,
843 &challenge_size),
844 GNUNET_JSON_spec_end ()
845 };
846
847 policy_ent[i].provider_name = prov_sel[i];
848 if (GNUNET_OK !=
849 GNUNET_JSON_parse (method_obj,
850 mspec,
851 NULL, NULL))
852 {
853 GNUNET_break (0);
854 pb->ec = TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID;
855 pb->hint = "'authentication_method' content malformed";
856 return;
857 }
858
859 if (GNUNET_OK !=
860 GNUNET_JSON_parse (provider_cfg,
861 pspec,
862 NULL, NULL))
863 {
864 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
865 "Skipping provider %s: no suitable configuration\n",
866 prov_sel[i]);
867 GNUNET_JSON_parse_free (mspec);
868 return;
869 }
870 json_array_foreach (provider_methods, index, md)
871 {
872 const char *type;
873 struct TALER_Amount method_cost;
874 struct GNUNET_JSON_Specification spec[] = {
875 GNUNET_JSON_spec_string ("type",
876 &type),
877 TALER_JSON_spec_amount_any ("usage_fee",
878 &method_cost),
879 GNUNET_JSON_spec_end ()
880 };
881
882 if (GNUNET_OK !=
883 GNUNET_JSON_parse (md,
884 spec,
885 NULL, NULL))
886 {
887 GNUNET_break (0);
888 pb->ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID;
889 pb->hint = "'methods' of provider";
890 GNUNET_JSON_parse_free (pspec);
891 return;
892 }
893 if ( (0 == strcmp (type,
894 method_type)) &&
895 (challenge_size_ok (size_limit_in_mb,
896 challenge_size) ) )
897 {
898 found = true;
899 GNUNET_break (0 <=
900 TALER_amount_add (&policy_ent[i].usage_fee,
901 &method_cost,
902 &upload_cost));
903 }
904 }
905 if (! found)
906 {
907 /* Provider does not OFFER this method, combination not possible.
908 Cost is basically 'infinite', but we simply then skip this. */
909 GNUNET_JSON_parse_free (pspec);
910 GNUNET_JSON_parse_free (mspec);
911 return;
912 }
913 GNUNET_JSON_parse_free (mspec);
914 GNUNET_JSON_parse_free (pspec);
915 }
916
917 /* calculate provider diversity by counting number of different
918 providers selected */
919 curr_diversity = 0;
920 for (unsigned int i = 0; i < pb->req_methods; i++)
921 {
922 bool found = false;
923
924 for (unsigned int j = 0; j < i; j++)
925 {
926 if (prov_sel[i] == prov_sel[j])
927 {
928 found = true;
929 break;
930 }
931 }
932 if (! found)
933 curr_diversity++;
934 }
935#if DEBUG
936 fprintf (stderr,
937 "Diversity: %u (best: %u)\n",
938 curr_diversity,
939 pb->best_diversity);
940#endif
941 if (curr_diversity < pb->best_diversity)
942 return; /* do not allow combinations that are bad
943 for provider diversity */
944 if (curr_diversity > pb->best_diversity)
945 {
946 /* drop existing policies, they are all worse */
947 struct PolicyMap *m;
948
949 while (NULL != (m = pb->current_policy->pm_head))
950 {
951 GNUNET_CONTAINER_DLL_remove (pb->current_policy->pm_head,
952 pb->current_policy->pm_tail,
953 m);
954 GNUNET_free (m->providers);
955 GNUNET_free (m);
956 }
957 pb->best_diversity = curr_diversity;
958 }
959 if (NULL == pb->p_head)
960 {
961 /* For the first policy, check for equivalent
962 policy mapping existing: we
963 do not want to do spend CPU time investigating
964 purely equivalent permutations */
965 for (struct PolicyMap *m = pb->current_policy->pm_head;
966 NULL != m;
967 m = m->next)
968 {
969 bool equiv = true;
970 for (unsigned int i = 0; i<pb->req_methods; i++)
971 {
972 if (! equiv_provider (pb,
973 m->providers[i].provider_name,
974 policy_ent[i].provider_name))
975 {
976 equiv = false;
977 break;
978 }
979 }
980 if (equiv)
981 return; /* equivalent to known allocation */
982 }
983 }
984
985 /* Add possible mapping to result list */
986 {
987 struct PolicyMap *m;
988
989 m = GNUNET_new (struct PolicyMap);
990 m->providers = GNUNET_new_array (pb->req_methods,
991 struct PolicyEntry);
992 memcpy (m->providers,
993 policy_ent,
994 sizeof (struct PolicyEntry) * pb->req_methods);
995 m->diversity = curr_diversity;
996 GNUNET_CONTAINER_DLL_insert (pb->current_policy->pm_head,
997 pb->current_policy->pm_tail,
998 m);
999 }
1000}
1001
1002
1003/**
1004 * Recursively compute possible combination(s) of provider candidates
1005 * in @e prov_sel. The selection is complete up to index @a i. Calls
1006 * eval_provider_selection() upon a feasible provider selection for
1007 * evaluation, resulting in "better" combinations being persisted in
1008 * @a pb.
1009 *
1010 * @param[in,out] pb our operational context
1011 * @param[in,out] prov_sel array of req_methods provider URLs to complete
1012 * @param i index up to which @a prov_sel is already initialized
1013 */
1014static void
1015provider_candidate (struct PolicyBuilder *pb,
1016 const char *prov_sel[],
1017 unsigned int i)
1018{
1019 const char *url;
1020 json_t *pconfig;
1021
1022 json_object_foreach (pb->providers, url, pconfig)
1023 {
1024 prov_sel[i] = url;
1025 if (i == pb->req_methods - 1)
1026 {
1027 eval_provider_selection (pb,
1028 prov_sel);
1029 if (TALER_EC_NONE != pb->ec)
1030 break;
1031 continue;
1032 }
1033 provider_candidate (pb,
1034 prov_sel,
1035 i + 1);
1036 }
1037}
1038
1039
1040/**
1041 * Using the selection of authentication methods from @a pb in
1042 * "m_idx", compute the best choice of providers.
1043 *
1044 * @param[in,out] pb our operational context
1045 */
1046static void
1047go_with (struct PolicyBuilder *pb)
1048{
1049 const char *prov_sel[pb->req_methods];
1050 struct Policy *policy;
1051
1052 /* compute provider selection */
1053 policy = GNUNET_new (struct Policy);
1054 policy->challenges = GNUNET_new_array (pb->req_methods,
1055 unsigned int);
1056 memcpy (policy->challenges,
1057 pb->m_idx,
1058 pb->req_methods * sizeof (unsigned int));
1059 pb->current_policy = policy;
1060 pb->best_diversity = 0;
1061 provider_candidate (pb,
1062 prov_sel,
1063 0);
1064 GNUNET_CONTAINER_DLL_insert (pb->p_head,
1065 pb->p_tail,
1066 policy);
1067 pb->current_policy = NULL;
1068}
1069
1070
1071/**
1072 * Recursively computes all possible subsets of length "req_methods"
1073 * from an array of length "num_methods", calling "go_with" on each of
1074 * those subsets (in "m_idx").
1075 *
1076 * @param[in,out] pb our operational context
1077 * @param i offset up to which the "m_idx" has been computed
1078 */
1079static void
1080method_candidate (struct PolicyBuilder *pb,
1081 unsigned int i)
1082{
1083 unsigned int start;
1084 unsigned int *m_idx = pb->m_idx;
1085
1086 start = (i > 0) ? m_idx[i - 1] + 1 : 0;
1087 for (unsigned int j = start; j < pb->num_methods; j++)
1088 {
1089 m_idx[i] = j;
1090 if (i == pb->req_methods - 1)
1091 {
1092#if DEBUG
1093 fprintf (stderr,
1094 "Suggesting: ");
1095 for (unsigned int k = 0; k<pb->req_methods; k++)
1096 {
1097 fprintf (stderr,
1098 "%u ",
1099 m_idx[k]);
1100 }
1101 fprintf (stderr, "\n");
1102#endif
1103 go_with (pb);
1104 continue;
1105 }
1106 method_candidate (pb,
1107 i + 1);
1108 }
1109}
1110
1111
1112/**
1113 * Lookup @a salt of @a provider_url in @a state.
1114 *
1115 * @param state the state to inspect
1116 * @param provider_url provider to look into
1117 * @param[out] salt value to extract
1118 * @return #GNUNET_OK on success
1119 */
1120static int
1121lookup_salt (const json_t *state,
1122 const char *provider_url,
1123 struct ANASTASIS_CRYPTO_ProviderSaltP *salt)
1124{
1125 const json_t *aps;
1126 const json_t *cfg;
1127 struct GNUNET_JSON_Specification spec[] = {
1128 GNUNET_JSON_spec_fixed_auto ("salt",
1129 salt),
1130 GNUNET_JSON_spec_end ()
1131 };
1132
1133 aps = json_object_get (state,
1134 "authentication_providers");
1135 if (NULL == aps)
1136 {
1137 GNUNET_break (0);
1138 return GNUNET_SYSERR;
1139 }
1140 cfg = json_object_get (aps,
1141 provider_url);
1142 if (NULL == cfg)
1143 {
1144 GNUNET_break (0);
1145 return GNUNET_SYSERR;
1146 }
1147 if (GNUNET_OK !=
1148 GNUNET_JSON_parse (cfg,
1149 spec,
1150 NULL, NULL))
1151 {
1152 GNUNET_break (0);
1153 return GNUNET_SYSERR;
1154 }
1155 return GNUNET_OK;
1156}
1157
1158
1159/**
1160 * Compare two cost lists.
1161 *
1162 * @param my cost to compare
1163 * @param be cost to compare
1164 * @return 0 if costs are estimated equal,
1165 * 1 if @a my < @a be
1166 * -1 if @a my > @a be
1167 */
1168static int
1169compare_costs (const struct Costs *my,
1170 const struct Costs *be)
1171{
1172 int ranking = 0;
1173
1174 for (const struct Costs *cmp = be;
1175 NULL != cmp;
1176 cmp = cmp->next)
1177 {
1178 bool found = false;
1179
1180 for (const struct Costs *pos = my;
1181 NULL != pos;
1182 pos = pos->next)
1183 {
1184 if (GNUNET_OK !=
1185 TALER_amount_cmp_currency (&cmp->cost,
1186 &pos->cost))
1187 continue;
1188 found = true;
1189 }
1190 if (! found)
1191 ranking--; /* new policy has no cost in this currency */
1192 }
1193
1194 for (const struct Costs *pos = my;
1195 NULL != pos;
1196 pos = pos->next)
1197 {
1198 bool found = false;
1199
1200 for (const struct Costs *cmp = be;
1201 NULL != cmp;
1202 cmp = cmp->next)
1203 {
1204 if (GNUNET_OK !=
1205 TALER_amount_cmp_currency (&cmp->cost,
1206 &pos->cost))
1207 continue;
1208 found = true;
1209 switch (TALER_amount_cmp (&cmp->cost,
1210 &pos->cost))
1211 {
1212 case -1: /* cmp < pos */
1213 ranking--;
1214 break;
1215 case 0:
1216 break;
1217 case 1: /* cmp > pos */
1218 ranking++;
1219 break;
1220 }
1221 break;
1222 }
1223 if (! found)
1224 ranking++; /* old policy has no cost in this currency */
1225 }
1226 if (0 == ranking)
1227 return 0;
1228 return (0 > ranking) ? -1 : 1;
1229}
1230
1231
1232/**
1233 * Evaluate the combined policy map stack in the ``curr_map`` of @a pb
1234 * and compare to the current best cost. If we are better, save the
1235 * stack in the ``best_map``.
1236 *
1237 * @param[in,out] pb policy builder we evaluate for
1238 * @param num_policies length of the ``curr_map`` array
1239 */
1240static void
1241evaluate_map (struct PolicyBuilder *pb,
1242 unsigned int num_policies)
1243{
1244 struct Costs *my_cost = NULL;
1245 unsigned int i = 0;
1246 unsigned int duplicates = 0;
1247 int ccmp;
1248
1249#if DEBUG
1250 fprintf (stderr,
1251 "Checking...\n");
1252#endif
1253 /* calculate cost */
1254 for (const struct Policy *p = pb->p_head;
1255 NULL != p;
1256 p = p->next)
1257 {
1258 const struct PolicyMap *pm = &pb->curr_map[i++];
1259
1260#if DEBUG
1261 fprintf (stderr,
1262 "Evaluating %p (%u): ",
1263 p,
1264 pm->diversity);
1265 for (unsigned int k = 0; k<pb->req_methods; k++)
1266 {
1267 const struct PolicyEntry *pe = &pm->providers[k];
1268
1269 fprintf (stderr,
1270 "%u->%s ",
1271 p->challenges[k],
1272 pe->provider_name);
1273 }
1274 fprintf (stderr, "\n");
1275#endif
1276 for (unsigned int j = 0; j<pb->req_methods; j++)
1277 {
1278 const struct PolicyEntry *pe = &pm->providers[j];
1279 unsigned int cv = p->challenges[j];
1280 bool found = false;
1281 unsigned int i2 = 0;
1282
1283 /* check for duplicates */
1284 for (const struct Policy *p2 = pb->p_head;
1285 p2 != p;
1286 p2 = p2->next)
1287 {
1288 const struct PolicyMap *pm2 = &pb->curr_map[i2++];
1289
1290 for (unsigned int j2 = 0; j2<pb->req_methods; j2++)
1291 {
1292 const struct PolicyEntry *pe2 = &pm2->providers[j2];
1293 unsigned int cv2 = p2->challenges[j2];
1294
1295 if (cv != cv2)
1296 continue; /* different challenge */
1297 if (0 == strcmp (pe->provider_name,
1298 pe2->provider_name))
1299 found = true; /* same challenge&provider! */
1300 else
1301 duplicates++; /* penalty for same challenge at two providers */
1302 }
1303 }
1304 if (found)
1305 continue; /* cost already included, do not add */
1306 add_cost (&my_cost,
1307 &pe->usage_fee);
1308 }
1309 }
1310
1311 ccmp = -1; /* non-zero if 'best_duplicates' is UINT_MAX */
1312 if ( (UINT_MAX != pb->best_duplicates) &&
1313 (0 > (ccmp = compare_costs (my_cost,
1314 pb->best_cost))) )
1315 {
1316 /* new method not clearly better, do not use it */
1317 free_costs (my_cost);
1318#if DEBUG
1319 fprintf (stderr,
1320 "... useless\n");
1321#endif
1322 return;
1323 }
1324 if ( (0 == ccmp) &&
1325 (duplicates > pb->best_duplicates) )
1326 {
1327 /* new method is cost-equal, but looses on duplicates,
1328 do not use it */
1329 free_costs (my_cost);
1330#if DEBUG
1331 fprintf (stderr,
1332 "... useless\n");
1333#endif
1334 return;
1335 }
1336 /* new method is better (or first), set as best */
1337#if DEBUG
1338 fprintf (stderr,
1339 "New best: %u duplicates, %s cost\n",
1340 duplicates,
1341 TALER_amount2s (&my_cost->cost));
1342#endif
1343 free_costs (pb->best_cost);
1344 pb->best_cost = my_cost;
1345 pb->best_duplicates = duplicates;
1346 memcpy (pb->best_map,
1347 pb->curr_map,
1348 sizeof (struct PolicyMap) * num_policies);
1349}
1350
1351
1352/**
1353 * Try all policy maps for @a pos and evaluate the
1354 * resulting total cost, saving the best result in
1355 * @a pb.
1356 *
1357 * @param[in,out] pb policy builder context
1358 * @param pos policy we are currently looking at maps for
1359 * @param off index of @a pos for the policy map
1360 */
1361static void
1362find_best_map (struct PolicyBuilder *pb,
1363 struct Policy *pos,
1364 unsigned int off)
1365{
1366 if (NULL == pos)
1367 {
1368 evaluate_map (pb,
1369 off);
1370 pb->evaluations++;
1371 return;
1372 }
1373 for (struct PolicyMap *pm = pos->pm_head;
1374 NULL != pm;
1375 pm = pm->next)
1376 {
1377 pb->curr_map[off] = *pm;
1378 find_best_map (pb,
1379 pos->next,
1380 off + 1);
1381 if (pb->evaluations >= MAX_EVALUATIONS)
1382 break;
1383 }
1384}
1385
1386
1387/**
1388 * Select cheapest policy combinations and add them to the JSON ``policies``
1389 * array in @a pb
1390 *
1391 * @param[in,out] policy builder with our state
1392 */
1393static void
1394select_policies (struct PolicyBuilder *pb)
1395{
1396 unsigned int cnt = 0;
1397
1398 for (struct Policy *p = pb->p_head;
1399 NULL != p;
1400 p = p->next)
1401 cnt++;
1402 {
1403 struct PolicyMap best[cnt];
1404 struct PolicyMap curr[cnt];
1405 unsigned int i;
1406
1407 pb->best_map = best;
1408 pb->curr_map = curr;
1409 pb->best_duplicates = UINT_MAX; /* worst */
1410 find_best_map (pb,
1411 pb->p_head,
1412 0);
1413 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1414 "Assessed %u/%u policies\n",
1415 pb->evaluations,
1416 (unsigned int) MAX_EVALUATIONS);
1417 i = 0;
1418 for (struct Policy *p = pb->p_head;
1419 NULL != p;
1420 p = p->next)
1421 {
1422 struct PolicyMap *pm = &best[i++];
1423 json_t *method_arr;
1424
1425#if DEBUG
1426 fprintf (stderr,
1427 "Best map (%u): ",
1428 pm->diversity);
1429 for (unsigned int k = 0; k<pb->req_methods; k++)
1430 {
1431 fprintf (stderr,
1432 "%u->%s ",
1433 p->challenges[k],
1434 pm->providers[k].provider_name);
1435 }
1436 fprintf (stderr, "\n");
1437#endif
1438 /* Convert "best" selection into 'policies' array */
1439 method_arr = json_array ();
1440 GNUNET_assert (NULL != method_arr);
1441 for (unsigned int i = 0; i < pb->req_methods; i++)
1442 {
1443 json_t *policy_method = json_pack ("{s:I, s:s}",
1444 "authentication_method",
1445 (json_int_t) p->challenges[i],
1446 "provider",
1447 pm->providers[i].provider_name);
1448 GNUNET_assert (0 ==
1449 json_array_append_new (method_arr,
1450 policy_method));
1451 }
1452 {
1453 json_t *policy = json_pack ("{s:o}",
1454 "methods",
1455 method_arr);
1456 GNUNET_assert (NULL != policy);
1457 GNUNET_assert (0 ==
1458 json_array_append_new (pb->policies,
1459 policy));
1460 }
1461 }
1462 }
1463}
1464
1465
1466/**
1467 * Clean up @a pb, in particular the policies DLL.
1468 *
1469 * @param[in] pb builder to clean up
1470 */
1471static void
1472clean_pb (struct PolicyBuilder *pb)
1473{
1474 struct Policy *p;
1475
1476 while (NULL != (p = pb->p_head))
1477 {
1478 struct PolicyMap *pm;
1479
1480 while (NULL != (pm = p->pm_head))
1481 {
1482 GNUNET_CONTAINER_DLL_remove (p->pm_head,
1483 p->pm_tail,
1484 pm);
1485 GNUNET_free (pm->providers);
1486 GNUNET_free (pm);
1487 }
1488 GNUNET_CONTAINER_DLL_remove (pb->p_head,
1489 pb->p_tail,
1490 p);
1491 GNUNET_free (p->challenges);
1492 GNUNET_free (p);
1493 }
1494}
1495
1496
1497/**
1498 * DispatchHandler/Callback function which is called for a
1499 * "done_authentication" action. Automaticially computes policies
1500 * based on available Anastasis providers and challenges provided by
1501 * the user.
1502 *
1503 * @param state state to operate on
1504 * @param arguments arguments to use for operation on state
1505 * @param cb callback to call during/after operation
1506 * @param cb_cls callback closure
1507 * @return NULL
1508 */
1509static struct ANASTASIS_ReduxAction *
1510done_authentication (json_t *state,
1511 const json_t *arguments,
1512 ANASTASIS_ActionCallback cb,
1513 void *cb_cls)
1514{
1515 struct PolicyBuilder pb = {
1516 .ec = TALER_EC_NONE
1517 };
1518 json_t *providers;
1519 json_t *policy_providers;
1520
1521 pb.providers = json_object_get (state,
1522 "authentication_providers");
1523 if ( (NULL == pb.providers) ||
1524 (! json_is_object (pb.providers) ) )
1525 {
1526 ANASTASIS_redux_fail_ (cb,
1527 cb_cls,
1528 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1529 "'authentication_providers' must be provided");
1530 return NULL;
1531 }
1532 pb.methods = json_object_get (state,
1533 "authentication_methods");
1534 if ( (NULL == pb.methods) ||
1535 (! json_is_array (pb.methods)) )
1536 {
1537 ANASTASIS_redux_fail_ (cb,
1538 cb_cls,
1539 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1540 "'authentication_methods' must be provided");
1541 return NULL;
1542 }
1543 pb.num_methods = json_array_size (pb.methods);
1544 switch (pb.num_methods)
1545 {
1546 case 0:
1547 ANASTASIS_redux_fail_ (cb,
1548 cb_cls,
1549 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1550 "'authentication_methods' must not be empty");
1551 return NULL;
1552 case 1:
1553 case 2:
1554 pb.req_methods = pb.num_methods;
1555 break;
1556 case 3:
1557 case 4:
1558 pb.req_methods = pb.num_methods - 1;
1559 break;
1560 case 5:
1561 case 6:
1562 pb.req_methods = pb.num_methods - 2;
1563 break;
1564 case 7:
1565 pb.req_methods = pb.num_methods - 3;
1566 break;
1567 default:
1568 /* cap at 4 for auto-generation, algorithm
1569 to compute mapping gets too expensive
1570 otherwise. */
1571 pb.req_methods = 4;
1572 break;
1573 }
1574 {
1575 unsigned int m_idx[pb.req_methods];
1576
1577 /* select req_methods from num_methods. */
1578 pb.m_idx = m_idx;
1579 method_candidate (&pb,
1580 0);
1581 }
1582 pb.policies = json_array ();
1583 select_policies (&pb);
1584 clean_pb (&pb);
1585 if (TALER_EC_NONE != pb.ec)
1586 {
1587 json_decref (pb.policies);
1588 ANASTASIS_redux_fail_ (cb,
1589 cb_cls,
1590 pb.ec,
1591 pb.hint);
1592 return NULL;
1593 }
1594 GNUNET_assert (0 ==
1595 json_object_set_new (state,
1596 "policies",
1597 pb.policies));
1598 providers = json_object_get (arguments,
1599 "providers");
1600 if (NULL == providers)
1601 {
1602 /* Setup a providers array from all working providers */
1603 json_t *available = json_object_get (state,
1604 "authentication_providers");
1605 const char *url;
1606 json_t *details;
1607
1608 policy_providers = json_array ();
1609 json_object_foreach (available, url, details)
1610 {
1611 json_t *provider;
1612 struct ANASTASIS_CRYPTO_ProviderSaltP salt;
1613
1614 if (GNUNET_OK !=
1615 lookup_salt (state,
1616 url,
1617 &salt))
1618 continue; /* skip providers that are down */
1619 provider = json_pack ("{s:s}",
1620 "provider_url", url);
1621 GNUNET_assert (NULL != provider);
1622 GNUNET_assert (0 ==
1623 json_array_append_new (policy_providers,
1624 provider));
1625 }
1626 }
1627 else
1628 {
1629 /* Setup a providers array from all working providers */
1630 size_t off;
1631 json_t *url;
1632
1633 policy_providers = json_array ();
1634 json_array_foreach (providers, off, url)
1635 {
1636 json_t *provider;
1637 struct ANASTASIS_CRYPTO_ProviderSaltP salt;
1638 const char *url_str;
1639
1640 url_str = json_string_value (url);
1641 if ( (NULL == url_str) ||
1642 (GNUNET_OK !=
1643 lookup_salt (state,
1644 url_str,
1645 &salt)) )
1646 {
1647 GNUNET_break (0);
1648 ANASTASIS_redux_fail_ (cb,
1649 cb_cls,
1650 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1651 "unworkable provider requested");
1652 return NULL;
1653 }
1654 provider = json_pack ("{s:s}",
1655 "provider_url", url);
1656 GNUNET_assert (NULL != provider);
1657 GNUNET_assert (0 ==
1658 json_array_append_new (policy_providers,
1659 provider));
1660 }
1661 }
1662 if (0 == json_array_size (policy_providers))
1663 {
1664 json_decref (policy_providers);
1665 ANASTASIS_redux_fail_ (cb,
1666 cb_cls,
1667 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1668 "no workable providers in state");
1669 return NULL;
1670 }
1671 GNUNET_assert (0 ==
1672 json_object_set_new (state,
1673 "policy_providers",
1674 policy_providers));
1675 set_state (state,
1676 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING);
1677 cb (cb_cls,
1678 TALER_EC_NONE,
1679 state);
1680 return NULL;
1681}
1682
1683
1684/* ******************** add_provider ******************* */
1685
1686
1687/**
1688 * DispatchHandler/Callback function which is called for a
1689 * "add_provider" action. Adds another Anastasis provider
1690 * to the list of available providers for storing information.
1691 *
1692 * @param state state to operate on
1693 * @param arguments arguments with a provider URL to add
1694 * @param cb callback to call during/after operation
1695 * @param cb_cls callback closure
1696 */
1697static struct ANASTASIS_ReduxAction *
1698add_provider (json_t *state,
1699 const json_t *arguments,
1700 ANASTASIS_ActionCallback cb,
1701 void *cb_cls)
1702{
1703 if (ANASTASIS_add_provider_ (state,
1704 arguments,
1705 cb,
1706 cb_cls))
1707 return NULL;
1708 return ANASTASIS_REDUX_backup_begin_ (state,
1709 NULL,
1710 cb,
1711 cb_cls);
1712}
1713
1714
1715/* ******************** add_policy ******************* */
1716
1717
1718/**
1719 * DispatchHandler/Callback function which is called for a
1720 * "add_policy" action.
1721 *
1722 * @param state state to operate on
1723 * @param arguments arguments to use for operation on state
1724 * @param cb callback to call during/after operation
1725 * @param cb_cls callback closure
1726 * @return NULL
1727 */
1728static struct ANASTASIS_ReduxAction *
1729add_policy (json_t *state,
1730 const json_t *arguments,
1731 ANASTASIS_ActionCallback cb,
1732 void *cb_cls)
1733{
1734 const json_t *arg_array;
1735 json_t *policies;
1736 const json_t *auth_providers;
1737 const json_t *auth_methods;
1738 json_t *methods;
1739
1740 if (NULL == arguments)
1741 {
1742 GNUNET_break (0);
1743 ANASTASIS_redux_fail_ (cb,
1744 cb_cls,
1745 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1746 "arguments missing");
1747 return NULL;
1748 }
1749 arg_array = json_object_get (arguments,
1750 "policy");
1751 if (! json_is_array (arg_array))
1752 {
1753 GNUNET_break (0);
1754 ANASTASIS_redux_fail_ (cb,
1755 cb_cls,
1756 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1757 "'policy' not an array");
1758 return NULL;
1759 }
1760 policies = json_object_get (state,
1761 "policies");
1762 if (! json_is_array (policies))
1763 {
1764 GNUNET_break (0);
1765 ANASTASIS_redux_fail_ (cb,
1766 cb_cls,
1767 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1768 "'policies' not an array");
1769 return NULL;
1770 }
1771 auth_providers = json_object_get (state,
1772 "authentication_providers");
1773 if (! json_is_object (auth_providers))
1774 {
1775 GNUNET_break (0);
1776 ANASTASIS_redux_fail_ (cb,
1777 cb_cls,
1778 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1779 "'auth_providers' not an object");
1780 return NULL;
1781 }
1782 auth_methods = json_object_get (state,
1783 "authentication_methods");
1784 if (! json_is_array (auth_methods))
1785 {
1786 GNUNET_break (0);
1787 ANASTASIS_redux_fail_ (cb,
1788 cb_cls,
1789 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1790 "'auth_methods' not an array");
1791 return NULL;
1792 }
1793
1794 methods = json_array ();
1795 GNUNET_assert (NULL != methods);
1796
1797 /* Add all methods from 'arg_array' to 'methods' */
1798 {
1799 size_t index;
1800 json_t *method;
1801
1802 json_array_foreach (arg_array, index, method)
1803 {
1804 const char *provider_url;
1805 uint32_t method_idx;
1806 json_t *prov_methods;
1807 const char *method_type;
1808 struct GNUNET_JSON_Specification spec[] = {
1809 GNUNET_JSON_spec_string ("provider",
1810 &provider_url),
1811 GNUNET_JSON_spec_uint32 ("authentication_method",
1812 &method_idx),
1813 GNUNET_JSON_spec_end ()
1814 };
1815
1816 if (GNUNET_OK !=
1817 GNUNET_JSON_parse (method,
1818 spec,
1819 NULL, NULL))
1820 {
1821 GNUNET_break (0);
1822 json_decref (methods);
1823 ANASTASIS_redux_fail_ (cb,
1824 cb_cls,
1825 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1826 "'method' details malformed");
1827 return NULL;
1828 }
1829
1830 {
1831 const json_t *prov_cfg;
1832 uint32_t limit;
1833 struct GNUNET_JSON_Specification spec[] = {
1834 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
1835 &limit),
1836 GNUNET_JSON_spec_json ("methods",
1837 &prov_methods),
1838 GNUNET_JSON_spec_end ()
1839 };
1840
1841 prov_cfg = json_object_get (auth_providers,
1842 provider_url);
1843 if (NULL == prov_cfg)
1844 {
1845 GNUNET_break (0);
1846 json_decref (methods);
1847 ANASTASIS_redux_fail_ (cb,
1848 cb_cls,
1849 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1850 "provider URL unknown");
1851 return NULL;
1852 }
1853 if (GNUNET_OK !=
1854 GNUNET_JSON_parse (prov_cfg,
1855 spec,
1856 NULL, NULL))
1857 {
1858 GNUNET_break (0);
1859 json_decref (methods);
1860 ANASTASIS_redux_fail_ (cb,
1861 cb_cls,
1862 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1863 "provider lacks authentication methods");
1864 return NULL;
1865
1866 }
1867 if (! json_is_array (prov_methods))
1868 {
1869 GNUNET_break (0);
1870 json_decref (methods);
1871 json_decref (prov_methods);
1872 ANASTASIS_redux_fail_ (cb,
1873 cb_cls,
1874 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1875 "provider lacks authentication methods");
1876 return NULL;
1877 }
1878 }
1879
1880 {
1881 const json_t *auth_method;
1882
1883 auth_method = json_array_get (auth_methods,
1884 method_idx);
1885 if (NULL == auth_method)
1886 {
1887 GNUNET_break (0);
1888 json_decref (methods);
1889 json_decref (prov_methods);
1890 ANASTASIS_redux_fail_ (cb,
1891 cb_cls,
1892 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1893 "authentication method unknown");
1894 return NULL;
1895 }
1896 method_type = json_string_value (json_object_get (auth_method,
1897 "type"));
1898 if (NULL == method_type)
1899 {
1900 GNUNET_break (0);
1901 json_decref (methods);
1902 json_decref (prov_methods);
1903 ANASTASIS_redux_fail_ (cb,
1904 cb_cls,
1905 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
1906 "authentication method must be a string");
1907 return NULL;
1908 }
1909 }
1910
1911 {
1912 bool found = false;
1913 size_t index;
1914 json_t *pm;
1915 json_array_foreach (prov_methods, index, pm)
1916 {
1917 struct TALER_Amount method_cost;
1918 const char *type;
1919 struct GNUNET_JSON_Specification spec[] = {
1920 GNUNET_JSON_spec_string ("type",
1921 &type),
1922 TALER_JSON_spec_amount_any ("usage_fee",
1923 &method_cost),
1924 GNUNET_JSON_spec_end ()
1925 };
1926
1927 if (GNUNET_OK !=
1928 GNUNET_JSON_parse (pm,
1929 spec,
1930 NULL, NULL))
1931 {
1932 GNUNET_break (0);
1933 json_decref (methods);
1934 json_decref (prov_methods);
1935 ANASTASIS_redux_fail_ (cb,
1936 cb_cls,
1937 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1938 "provider authentication method specification invalid");
1939 return NULL;
1940 }
1941 if (0 != strcmp (type,
1942 method_type))
1943 continue;
1944 found = true;
1945 break;
1946 }
1947 if (! found)
1948 {
1949 GNUNET_break (0);
1950 json_decref (methods);
1951 json_decref (prov_methods);
1952 ANASTASIS_redux_fail_ (cb,
1953 cb_cls,
1954 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
1955 "selected provider does not support authentication method");
1956 return NULL;
1957 }
1958 }
1959 GNUNET_assert (0 ==
1960 json_array_append (methods,
1961 method));
1962 json_decref (prov_methods);
1963 } /* end of json_array_foreach (arg_array, index, method) */
1964 }
1965
1966 /* add new policy to array of existing policies */
1967 {
1968 json_t *policy;
1969 json_t *idx;
1970
1971 policy = json_pack ("{s:o}",
1972 "methods",
1973 methods);
1974 GNUNET_assert (NULL != policy);
1975 idx = json_object_get (arguments,
1976 "policy_index");
1977 if ( (NULL == idx) ||
1978 (! json_is_integer (idx)) )
1979 {
1980 GNUNET_assert (0 ==
1981 json_array_append_new (policies,
1982 policy));
1983 }
1984 else
1985 {
1986 GNUNET_assert (0 ==
1987 json_array_insert_new (policies,
1988 json_integer_value (idx),
1989 policy));
1990 }
1991 }
1992
1993 cb (cb_cls,
1994 TALER_EC_NONE,
1995 state);
1996 return NULL;
1997}
1998
1999
2000/* ******************** update_policy ******************* */
2001
2002
2003/**
2004 * DispatchHandler/Callback function which is called for a
2005 * "update_policy" action.
2006 *
2007 * @param state state to operate on
2008 * @param arguments arguments to use for operation on state
2009 * @param cb callback to call during/after operation
2010 * @param cb_cls callback closure
2011 * @return NULL
2012 */
2013static struct ANASTASIS_ReduxAction *
2014update_policy (json_t *state,
2015 const json_t *arguments,
2016 ANASTASIS_ActionCallback cb,
2017 void *cb_cls)
2018{
2019 const json_t *idx;
2020 size_t index;
2021 json_t *policy_arr;
2022
2023 if (NULL == arguments)
2024 {
2025 GNUNET_break (0);
2026 ANASTASIS_redux_fail_ (cb,
2027 cb_cls,
2028 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2029 "arguments missing");
2030 return NULL;
2031 }
2032 idx = json_object_get (arguments,
2033 "policy_index");
2034 if (! json_is_integer (idx))
2035 {
2036 GNUNET_break (0);
2037 ANASTASIS_redux_fail_ (cb,
2038 cb_cls,
2039 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2040 "'policy_index' must be an integer");
2041 return NULL;
2042 }
2043 index = json_integer_value (idx);
2044 policy_arr = json_object_get (state,
2045 "policies");
2046 if (! json_is_array (policy_arr))
2047 {
2048 GNUNET_break (0);
2049 ANASTASIS_redux_fail_ (cb,
2050 cb_cls,
2051 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
2052 "'policies' must be an array");
2053 return NULL;
2054 }
2055 if (0 != json_array_remove (policy_arr,
2056 index))
2057 {
2058 GNUNET_break (0);
2059 ANASTASIS_redux_fail_ (cb,
2060 cb_cls,
2061 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2062 "removal failed");
2063 return NULL;
2064 }
2065 return add_policy (state,
2066 arguments,
2067 cb,
2068 cb_cls);
2069}
2070
2071
2072/* ******************** del_policy ******************* */
2073
2074
2075/**
2076 * DispatchHandler/Callback function which is called for a
2077 * "delete_policy" action.
2078 *
2079 * @param state state to operate on
2080 * @param arguments arguments to use for operation on state
2081 * @param cb callback to call during/after operation
2082 * @param cb_cls callback closure
2083 * @return NULL
2084 */
2085static struct ANASTASIS_ReduxAction *
2086del_policy (json_t *state,
2087 const json_t *arguments,
2088 ANASTASIS_ActionCallback cb,
2089 void *cb_cls)
2090{
2091 const json_t *idx;
2092 size_t index;
2093 json_t *policy_arr;
2094
2095 if (NULL == arguments)
2096 {
2097 ANASTASIS_redux_fail_ (cb,
2098 cb_cls,
2099 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2100 "arguments missing");
2101 return NULL;
2102 }
2103 idx = json_object_get (arguments,
2104 "policy_index");
2105 if (! json_is_integer (idx))
2106 {
2107 ANASTASIS_redux_fail_ (cb,
2108 cb_cls,
2109 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2110 "'policy_index' must be an integer");
2111 return NULL;
2112 }
2113 index = json_integer_value (idx);
2114 policy_arr = json_object_get (state,
2115 "policies");
2116 if (! json_is_array (policy_arr))
2117 {
2118 ANASTASIS_redux_fail_ (cb,
2119 cb_cls,
2120 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
2121 "'policies' must be an array");
2122 return NULL;
2123 }
2124 if (0 != json_array_remove (policy_arr,
2125 index))
2126 {
2127 ANASTASIS_redux_fail_ (cb,
2128 cb_cls,
2129 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2130 "removal failed");
2131 return NULL;
2132 }
2133 cb (cb_cls,
2134 TALER_EC_NONE,
2135 state);
2136 return NULL;
2137}
2138
2139
2140/* ******************** del_challenge ******************* */
2141
2142
2143/**
2144 * DispatchHandler/Callback function which is called for a
2145 * "delete_challenge" action.
2146 *
2147 * @param state state to operate on
2148 * @param arguments arguments to use for operation on state
2149 * @param cb callback to call during/after operation
2150 * @param cb_cls callback closure
2151 * @return NULL
2152 */
2153static struct ANASTASIS_ReduxAction *
2154del_challenge (json_t *state,
2155 const json_t *arguments,
2156 ANASTASIS_ActionCallback cb,
2157 void *cb_cls)
2158{
2159 const json_t *pidx;
2160 const json_t *cidx;
2161 size_t index;
2162 json_t *policy_arr;
2163 json_t *policy;
2164 json_t *method_arr;
2165
2166 if (NULL == arguments)
2167 {
2168 ANASTASIS_redux_fail_ (cb,
2169 cb_cls,
2170 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2171 "arguments missing");
2172 return NULL;
2173 }
2174 pidx = json_object_get (arguments,
2175 "policy_index");
2176 cidx = json_object_get (arguments,
2177 "challenge_index");
2178 if (! json_is_integer (pidx))
2179 {
2180 ANASTASIS_redux_fail_ (cb,
2181 cb_cls,
2182 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2183 "'policy_index' must be an integer");
2184 return NULL;
2185 }
2186 if (! json_is_integer (cidx))
2187 {
2188 ANASTASIS_redux_fail_ (cb,
2189 cb_cls,
2190 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2191 "'challenge_index' must be an integer");
2192 return NULL;
2193 }
2194 index = json_integer_value (pidx);
2195 policy_arr = json_object_get (state,
2196 "policies");
2197 if (! json_is_array (policy_arr))
2198 {
2199 ANASTASIS_redux_fail_ (cb,
2200 cb_cls,
2201 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
2202 "'policies' must be an array");
2203 return NULL;
2204 }
2205 policy = json_array_get (policy_arr,
2206 index);
2207 if (NULL == policy)
2208 {
2209 ANASTASIS_redux_fail_ (cb,
2210 cb_cls,
2211 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2212 "'policy_index' out of range");
2213 return NULL;
2214 }
2215 method_arr = json_object_get (policy,
2216 "methods");
2217 if (NULL == method_arr)
2218 {
2219 ANASTASIS_redux_fail_ (cb,
2220 cb_cls,
2221 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2222 "methods missing in policy");
2223 return NULL;
2224 }
2225 index = json_integer_value (cidx);
2226 if (0 != json_array_remove (method_arr,
2227 index))
2228 {
2229 ANASTASIS_redux_fail_ (cb,
2230 cb_cls,
2231 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2232 "removal failed");
2233 return NULL;
2234 }
2235 cb (cb_cls,
2236 TALER_EC_NONE,
2237 state);
2238 return NULL;
2239}
2240
2241
2242/* ********************** done_policy_review ***************** */
2243
2244
2245/**
2246 * Calculate how many years of service we need
2247 * from the desired @a expiration time,
2248 * rounding up.
2249 *
2250 * @param expiration desired expiration time
2251 * @return number of years of service to pay for
2252*/
2253static unsigned int
2254expiration_to_years (struct GNUNET_TIME_Absolute expiration)
2255{
2256 struct GNUNET_TIME_Relative rem;
2257 unsigned int years;
2258
2259 rem = GNUNET_TIME_absolute_get_remaining (expiration);
2260 years = rem.rel_value_us / GNUNET_TIME_UNIT_YEARS.rel_value_us;
2261 if (0 != rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)
2262 years++;
2263 return years;
2264}
2265
2266
2267/**
2268 * Update @a state such that the earliest expiration for
2269 * any truth or policy is @a expiration. Recalculate
2270 * the ``upload_fees`` array with the associated costs.
2271 *
2272 * @param[in,out] state our state to update
2273 * @param expiration new expiration to enforce
2274 * @return #GNUNET_OK on success,
2275 * #GNUNET_SYSERR if the state is invalid
2276 */
2277static enum GNUNET_GenericReturnValue
2278update_expiration_cost (json_t *state,
2279 struct GNUNET_TIME_Absolute expiration)
2280{
2281 struct Costs *costs = NULL;
2282 unsigned int years;
2283 json_t *providers;
2284 bool is_free = true;
2285
2286 providers = json_object_get (state,
2287 "authentication_providers");
2288 if (! json_is_object (providers))
2289 {
2290 GNUNET_break (0);
2291 return GNUNET_SYSERR;
2292 }
2293
2294 years = expiration_to_years (expiration);
2295
2296 /* go over all providers and add up cost */
2297 {
2298 const char *url;
2299 json_t *provider;
2300
2301 json_object_foreach (providers, url, provider)
2302 {
2303 struct TALER_Amount annual_fee;
2304 struct GNUNET_JSON_Specification pspec[] = {
2305 TALER_JSON_spec_amount_any ("annual_fee",
2306 &annual_fee),
2307 GNUNET_JSON_spec_end ()
2308 };
2309 struct TALER_Amount fee;
2310
2311 if (GNUNET_OK !=
2312 GNUNET_JSON_parse (provider,
2313 pspec,
2314 NULL, NULL))
2315 {
2316 GNUNET_break (0);
2317 return GNUNET_SYSERR;
2318 }
2319 if (0 >
2320 TALER_amount_multiply (&fee,
2321 &annual_fee,
2322 years))
2323 {
2324 GNUNET_break (0);
2325 return GNUNET_SYSERR;
2326 }
2327 add_cost (&costs,
2328 &fee);
2329 }
2330 }
2331
2332 /* go over all truths and add up cost */
2333 {
2334 unsigned int off = 0;
2335 unsigned int len = 0;
2336 struct AlreadySeen
2337 {
2338 uint32_t method;
2339 const char *provider_url;
2340 } *seen = NULL;
2341 json_t *policies;
2342 size_t pidx;
2343 json_t *policy;
2344
2345 policies = json_object_get (state,
2346 "policies");
2347 json_array_foreach (policies, pidx, policy)
2348 {
2349 json_t *methods;
2350 json_t *method;
2351 size_t midx;
2352
2353 methods = json_object_get (policy,
2354 "methods");
2355 json_array_foreach (methods, midx, method)
2356 {
2357 const char *provider_url;
2358 uint32_t method_idx;
2359
2360 struct GNUNET_JSON_Specification spec[] = {
2361 GNUNET_JSON_spec_string ("provider",
2362 &provider_url),
2363 GNUNET_JSON_spec_uint32 ("authentication_method",
2364 &method_idx),
2365 GNUNET_JSON_spec_end ()
2366 };
2367
2368 if (GNUNET_OK !=
2369 GNUNET_JSON_parse (method,
2370 spec,
2371 NULL, NULL))
2372 {
2373 GNUNET_break (0);
2374 return GNUNET_SYSERR;
2375 }
2376 /* check if we have seen this one before */
2377 {
2378 bool found = false;
2379
2380 for (unsigned int i = 0; i<off; i++)
2381 if ( (seen[i].method == method_idx) &&
2382 (0 == strcmp (seen[i].provider_url,
2383 provider_url)) )
2384 found = true;
2385 if (found)
2386 continue; /* skip */
2387 }
2388 if (off == len)
2389 {
2390 GNUNET_array_grow (seen,
2391 len,
2392 4 + len * 2);
2393 }
2394 seen[off].method = method_idx;
2395 seen[off].provider_url = provider_url;
2396 off++;
2397 {
2398 struct TALER_Amount upload_cost;
2399 struct GNUNET_JSON_Specification pspec[] = {
2400 TALER_JSON_spec_amount_any ("truth_upload_fee",
2401 &upload_cost),
2402 GNUNET_JSON_spec_end ()
2403 };
2404 struct TALER_Amount fee;
2405 const json_t *provider_cfg
2406 = json_object_get (providers,
2407 provider_url);
2408
2409 if (GNUNET_OK !=
2410 GNUNET_JSON_parse (provider_cfg,
2411 pspec,
2412 NULL, NULL))
2413 {
2414 GNUNET_break (0);
2415 return GNUNET_SYSERR;
2416 }
2417 if (0 >
2418 TALER_amount_multiply (&fee,
2419 &upload_cost,
2420 years))
2421 {
2422 GNUNET_break (0);
2423 return GNUNET_SYSERR;
2424 }
2425 add_cost (&costs,
2426 &fee);
2427 }
2428 }
2429 }
2430 GNUNET_array_grow (seen,
2431 len,
2432 0);
2433 }
2434
2435 /* convert 'costs' into state */
2436 {
2437 json_t *arr;
2438
2439 arr = json_array ();
2440 GNUNET_assert (NULL != arr);
2441 while (NULL != costs)
2442 {
2443 struct Costs *nxt = costs->next;
2444
2445 if ( (0 != costs->cost.value) ||
2446 (0 != costs->cost.fraction) )
2447 {
2448 json_t *ao;
2449
2450 ao = json_pack ("{s:o}",
2451 "fee",
2452 TALER_JSON_from_amount (&costs->cost));
2453 GNUNET_assert (0 ==
2454 json_array_append_new (arr,
2455 ao));
2456 is_free = false;
2457 }
2458 GNUNET_free (costs);
2459 costs = nxt;
2460 }
2461 GNUNET_assert (0 ==
2462 json_object_set_new (state,
2463 "upload_fees",
2464 arr));
2465 }
2466
2467 if (is_free)
2468 expiration = GNUNET_TIME_relative_to_absolute (ANASTASIS_FREE_STORAGE);
2469 /* update 'expiration' in state */
2470 {
2471 json_t *eo;
2472
2473 (void) GNUNET_TIME_round_abs (&expiration);
2474 eo = GNUNET_JSON_from_time_abs (expiration);
2475 GNUNET_assert (0 ==
2476 json_object_set_new (state,
2477 "expiration",
2478 eo));
2479 }
2480
2481
2482 return GNUNET_OK;
2483}
2484
2485
2486/**
2487 * DispatchHandler/Callback function which is called for a
2488 * "done_policy_review" action.
2489 *
2490 * @param state state to operate on
2491 * @param arguments arguments to use for operation on state
2492 * @param cb callback to call during/after operation
2493 * @param cb_cls callback closure
2494 * @return NULL
2495 */
2496static struct ANASTASIS_ReduxAction *
2497done_policy_review (json_t *state,
2498 const json_t *arguments,
2499 ANASTASIS_ActionCallback cb,
2500 void *cb_cls)
2501{
2502 const json_t *policy_arr;
2503
2504 policy_arr = json_object_get (state,
2505 "policies");
2506 if (0 == json_array_size (policy_arr))
2507 {
2508 ANASTASIS_redux_fail_ (cb,
2509 cb_cls,
2510 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2511 "no policies specified");
2512 return NULL;
2513 }
2514 {
2515 struct GNUNET_TIME_Absolute exp = {0};
2516 struct GNUNET_JSON_Specification spec[] = {
2517 GNUNET_JSON_spec_mark_optional (
2518 GNUNET_JSON_spec_absolute_time ("expiration",
2519 &exp)),
2520 GNUNET_JSON_spec_end ()
2521 };
2522
2523 if (GNUNET_OK !=
2524 GNUNET_JSON_parse (state,
2525 spec,
2526 NULL, NULL))
2527 {
2528 ANASTASIS_redux_fail_ (cb,
2529 cb_cls,
2530 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2531 "invalid expiration specified");
2532 return NULL;
2533 }
2534 if (0 == exp.abs_value_us)
2535 exp = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS);
2536 if (GNUNET_OK !=
2537 update_expiration_cost (state,
2538 exp))
2539 {
2540 ANASTASIS_redux_fail_ (cb,
2541 cb_cls,
2542 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
2543 "could not calculate expiration cost");
2544 return NULL;
2545 }
2546 }
2547 set_state (state,
2548 ANASTASIS_BACKUP_STATE_SECRET_EDITING);
2549 cb (cb_cls,
2550 TALER_EC_NONE,
2551 state);
2552 return NULL;
2553}
2554
2555
2556/**
2557 * Information we keep for an upload() operation.
2558 */
2559struct UploadContext;
2560
2561
2562/**
2563 * Maps a TruthUpload to a policy and recovery method where this
2564 * truth is used.
2565 */
2566struct PolicyMethodReference
2567{
2568 /**
2569 * Offset into the "policies" array.
2570 */
2571 unsigned int policy_index;
2572
2573 /**
2574 * Offset into the "methods" array (of the policy selected
2575 * by @e policy_index).
2576 */
2577 unsigned int method_index;
2578
2579};
2580
2581
2582/**
2583 * Entry we keep per truth upload.
2584 */
2585struct TruthUpload
2586{
2587
2588 /**
2589 * Kept in a DLL.
2590 */
2591 struct TruthUpload *next;
2592
2593 /**
2594 * Kept in a DLL.
2595 */
2596 struct TruthUpload *prev;
2597
2598 /**
2599 * Handle to the actual upload operation.
2600 */
2601 struct ANASTASIS_TruthUpload *tu;
2602
2603 /**
2604 * Upload context this operation is part of.
2605 */
2606 struct UploadContext *uc;
2607
2608 /**
2609 * Truth resulting from the upload, if any.
2610 */
2611 struct ANASTASIS_Truth *t;
2612
2613 /**
2614 * A taler://pay/-URI with a request to pay the annual fee for
2615 * the service. Set if payment is required.
2616 */
2617 char *payment_request;
2618
2619 /**
2620 * Which policies and methods does this truth affect?
2621 */
2622 struct PolicyMethodReference *policies;
2623
2624 /**
2625 * Where are we uploading to?
2626 */
2627 char *provider_url;
2628
2629 /**
2630 * Which challenge object are we uploading?
2631 */
2632 uint32_t am_idx;
2633
2634 /**
2635 * Length of the @e policies array.
2636 */
2637 unsigned int policies_length;
2638
2639 /**
2640 * Status of the upload.
2641 */
2642 enum ANASTASIS_UploadStatus us;
2643
2644 /**
2645 * Taler error code of the upload.
2646 */
2647 enum TALER_ErrorCode ec;
2648
2649};
2650
2651
2652/**
2653 * Information we keep for an upload() operation.
2654 */
2655struct UploadContext
2656{
2657 /**
2658 * Recovery action returned to caller for aborting the operation.
2659 */
2660 struct ANASTASIS_ReduxAction ra;
2661
2662 /**
2663 * Function to call upon completion.
2664 */
2665 ANASTASIS_ActionCallback cb;
2666
2667 /**
2668 * Closure for @e cb.
2669 */
2670 void *cb_cls;
2671
2672 /**
2673 * Our state.
2674 */
2675 json_t *state;
2676
2677 /**
2678 * Master secret sharing operation, NULL if not yet running.
2679 */
2680 struct ANASTASIS_SecretShare *ss;
2681
2682 /**
2683 * Head of DLL of truth uploads.
2684 */
2685 struct TruthUpload *tues_head;
2686
2687 /**
2688 * Tail of DLL of truth uploads.
2689 */
2690 struct TruthUpload *tues_tail;
2691
2692 /**
2693 * Timeout to use for the operation, from the arguments.
2694 */
2695 struct GNUNET_TIME_Relative timeout;
2696
2697 /**
2698 * For how many years should we pay?
2699 */
2700 unsigned int years;
2701
2702};
2703
2704
2705/**
2706 * Function called when the #upload transition is being aborted.
2707 *
2708 * @param cls a `struct UploadContext`
2709 */
2710static void
2711upload_cancel_cb (void *cls)
2712{
2713 struct UploadContext *uc = cls;
2714 struct TruthUpload *tue;
2715
2716 while (NULL != (tue = uc->tues_head))
2717 {
2718 GNUNET_CONTAINER_DLL_remove (uc->tues_head,
2719 uc->tues_tail,
2720 tue);
2721 if (NULL != tue->tu)
2722 {
2723 ANASTASIS_truth_upload_cancel (tue->tu);
2724 tue->tu = NULL;
2725 }
2726 if (NULL != tue->t)
2727 {
2728 ANASTASIS_truth_free (tue->t);
2729 tue->t = NULL;
2730 }
2731 GNUNET_free (tue->provider_url);
2732 GNUNET_free (tue->payment_request);
2733 GNUNET_free (tue->policies);
2734 GNUNET_free (tue);
2735 }
2736 if (NULL != uc->ss)
2737 {
2738 ANASTASIS_secret_share_cancel (uc->ss);
2739 uc->ss = NULL;
2740 }
2741 json_decref (uc->state);
2742 GNUNET_free (uc);
2743}
2744
2745
2746/**
2747 * Take all of the ongoing truth uploads and serialize them into the @a uc
2748 * state.
2749 *
2750 * @param[in,out] uc context to take truth uploads from and to update state of
2751 */
2752static void
2753serialize_truth (struct UploadContext *uc)
2754{
2755 json_t *policies;
2756
2757 policies = json_object_get (uc->state,
2758 "policies");
2759 GNUNET_assert (json_is_array (policies));
2760 for (struct TruthUpload *tue = uc->tues_head;
2761 NULL != tue;
2762 tue = tue->next)
2763 {
2764 if (NULL == tue->t)
2765 continue;
2766 for (unsigned int i = 0; i<tue->policies_length; i++)
2767 {
2768 const struct PolicyMethodReference *pmr = &tue->policies[i];
2769 json_t *policy = json_array_get (policies,
2770 pmr->policy_index);
2771 json_t *methods = json_object_get (policy,
2772 "methods");
2773 json_t *auth_method = json_array_get (methods,
2774 pmr->method_index);
2775 json_t *truth = ANASTASIS_truth_to_json (tue->t);
2776
2777 GNUNET_assert (0 ==
2778 json_object_set_new (truth,
2779 "upload_status",
2780 json_integer (tue->us)));
2781 GNUNET_assert (NULL != policy);
2782 GNUNET_assert (NULL != methods);
2783 GNUNET_assert (NULL != auth_method);
2784 GNUNET_assert (NULL != truth);
2785 GNUNET_assert (0 ==
2786 json_object_set_new (auth_method,
2787 "truth",
2788 truth));
2789 }
2790 }
2791}
2792
2793
2794/**
2795 * Function called with the results of a #ANASTASIS_secret_share().
2796 *
2797 * @param cls closure with a `struct UploadContext *`
2798 * @param sr share result
2799 */
2800static void
2801secret_share_result_cb (void *cls,
2802 const struct ANASTASIS_ShareResult *sr)
2803{
2804 struct UploadContext *uc = cls;
2805
2806 uc->ss = NULL;
2807 switch (sr->ss)
2808 {
2809 case ANASTASIS_SHARE_STATUS_SUCCESS:
2810 /* Just to be safe, delete the "core_secret" so that it is not
2811 accidentally preserved anywhere */
2812 (void) json_object_del (uc->state,
2813 "core_secret");
2814 {
2815 json_t *sa = json_object ();
2816
2817 GNUNET_assert (NULL != sa);
2818 for (unsigned int i = 0; i<sr->details.success.num_providers; i++)
2819 {
2820 const struct ANASTASIS_ProviderSuccessStatus *pssi
2821 = &sr->details.success.pss[i];
2822 json_t *d;
2823
2824 d = json_pack ("{s:I, s:o}",
2825 "policy_version",
2826 pssi->policy_version,
2827 "policy_expiration",
2828 GNUNET_JSON_from_time_abs (pssi->policy_expiration));
2829 GNUNET_assert (NULL != d);
2830 GNUNET_assert (0 ==
2831 json_object_set_new (sa,
2832 pssi->provider_url,
2833 d));
2834 }
2835 GNUNET_assert (0 ==
2836 json_object_set_new (uc->state,
2837 "success_details",
2838 sa));
2839 }
2840 set_state (uc->state,
2841 ANASTASIS_BACKUP_STATE_BACKUP_FINISHED);
2842 uc->cb (uc->cb_cls,
2843 TALER_EC_NONE,
2844 uc->state);
2845 break;
2846 case ANASTASIS_SHARE_STATUS_PAYMENT_REQUIRED:
2847 {
2848 json_t *ra;
2849 json_t *providers;
2850
2851 providers = json_object_get (uc->state,
2852 "policy_providers");
2853 set_state (uc->state,
2854 ANASTASIS_BACKUP_STATE_POLICIES_PAYING);
2855 serialize_truth (uc);
2856 ra = json_array ();
2857 GNUNET_assert (NULL != ra);
2858 for (unsigned int i = 0; i<
2859 sr->details.payment_required.payment_requests_length; i++)
2860 {
2861 const struct ANASTASIS_SharePaymentRequest *spr;
2862 json_t *pr;
2863 size_t off;
2864 json_t *provider;
2865
2866 spr = &sr->details.payment_required.payment_requests[i];
2867 pr = json_pack ("{s:s, s:s}",
2868 "payto",
2869 spr->payment_request_url,
2870 "provider",
2871 spr->provider_url);
2872 GNUNET_assert (0 ==
2873 json_array_append_new (ra,
2874 pr));
2875 json_array_foreach (providers, off, provider)
2876 {
2877 const char *purl = json_string_value (json_object_get (provider,
2878 "provider_url"));
2879
2880 if (NULL == purl)
2881 {
2882 GNUNET_break (0);
2883 ANASTASIS_redux_fail_ (uc->cb,
2884 uc->cb_cls,
2885 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
2886 "policy_providers array contents are invalid");
2887 json_decref (ra);
2888 return;
2889 }
2890 if (0 == strcmp (purl,
2891 spr->provider_url))
2892 {
2893 json_t *psj;
2894
2895 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2896 "Remembering payment secret for provider `%s'\n",
2897 spr->provider_url);
2898 psj = GNUNET_JSON_from_data_auto (&spr->payment_secret);
2899 GNUNET_assert (0 ==
2900 json_object_set_new (provider,
2901 "payment_secret",
2902 psj));
2903 }
2904 }
2905 }
2906 GNUNET_assert (0 ==
2907 json_object_set_new (uc->state,
2908 "policy_payment_requests",
2909 ra));
2910 }
2911 uc->cb (uc->cb_cls,
2912 TALER_EC_NONE,
2913 uc->state);
2914 break;
2915 case ANASTASIS_SHARE_STATUS_PROVIDER_FAILED:
2916 {
2917 json_t *details;
2918
2919 details = json_pack ("{s:s, s:I, s:I, s:s}",
2920 "backup_state",
2921 "ERROR",
2922 "http_status",
2923 (json_int_t) sr->details.provider_failure.http_status,
2924 "upload_status",
2925 (json_int_t) sr->details.provider_failure.ec,
2926 "provider_url",
2927 sr->details.provider_failure.provider_url);
2928 uc->cb (uc->cb_cls,
2929 TALER_EC_ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED,
2930 details);
2931 json_decref (details);
2932 }
2933 break;
2934 default:
2935 GNUNET_break (0);
2936 ANASTASIS_redux_fail_ (uc->cb,
2937 uc->cb_cls,
2938 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2939 "unexpected share result");
2940 break;
2941 }
2942 upload_cancel_cb (uc);
2943}
2944
2945
2946/**
2947 * All truth uploads are done, begin with uploading the policy.
2948 *
2949 * @param[in,out] uc context for the operation
2950 */
2951static void
2952share_secret (struct UploadContext *uc)
2953{
2954 json_t *user_id;
2955 json_t *core_secret;
2956 json_t *jpolicies;
2957 json_t *providers = NULL;
2958 size_t policies_len;
2959 const char *secret_name = NULL;
2960 unsigned int pds_len;
2961 struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
2962 struct GNUNET_JSON_Specification spec[] = {
2963 GNUNET_JSON_spec_json ("identity_attributes",
2964 &user_id),
2965 GNUNET_JSON_spec_json ("policies",
2966 &jpolicies),
2967 GNUNET_JSON_spec_json ("policy_providers",
2968 &providers),
2969 GNUNET_JSON_spec_json ("core_secret",
2970 &core_secret),
2971 GNUNET_JSON_spec_mark_optional (
2972 GNUNET_JSON_spec_string ("secret_name",
2973 &secret_name)),
2974 GNUNET_JSON_spec_end ()
2975 };
2976
2977 if (GNUNET_OK !=
2978 GNUNET_JSON_parse (uc->state,
2979 spec,
2980 NULL, NULL))
2981 {
2982 ANASTASIS_redux_fail_ (uc->cb,
2983 uc->cb_cls,
2984 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
2985 "State parsing failed when preparing to share secret");
2986 upload_cancel_cb (uc);
2987 return;
2988 }
2989
2990 {
2991 json_t *args;
2992 struct GNUNET_JSON_Specification pspec[] = {
2993 GNUNET_JSON_spec_mark_optional (
2994 GNUNET_JSON_spec_relative_time ("timeout",
2995 &timeout)),
2996 GNUNET_JSON_spec_end ()
2997 };
2998
2999 args = json_object_get (uc->state,
3000 "pay-arguments");
3001 if ( (NULL != args) &&
3002 (GNUNET_OK !=
3003 GNUNET_JSON_parse (args,
3004 pspec,
3005 NULL, NULL)) )
3006 {
3007 json_dumpf (args,
3008 stderr,
3009 JSON_INDENT (2));
3010 GNUNET_break (0);
3011 ANASTASIS_redux_fail_ (uc->cb,
3012 uc->cb_cls,
3013 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
3014 NULL);
3015 upload_cancel_cb (uc);
3016 return;
3017 }
3018 }
3019
3020 if ( (! json_is_object (user_id)) ||
3021 (! json_is_array (jpolicies)) ||
3022 (0 == json_array_size (jpolicies)) ||
3023 ( (NULL != providers) &&
3024 (! json_is_array (providers)) ) )
3025 {
3026 ANASTASIS_redux_fail_ (uc->cb,
3027 uc->cb_cls,
3028 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3029 "State parsing failed checks when preparing to share secret");
3030 GNUNET_JSON_parse_free (spec);
3031 upload_cancel_cb (uc);
3032 return;
3033 }
3034
3035 policies_len = json_array_size (jpolicies);
3036 pds_len = json_array_size (providers);
3037
3038 if (0 == pds_len)
3039 {
3040 ANASTASIS_redux_fail_ (uc->cb,
3041 uc->cb_cls,
3042 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3043 "no workable providers in state");
3044 GNUNET_JSON_parse_free (spec);
3045 upload_cancel_cb (uc);
3046 return;
3047 }
3048
3049
3050 {
3051 struct ANASTASIS_Policy *vpolicies[policies_len];
3052 const struct ANASTASIS_Policy *policies[policies_len];
3053 struct ANASTASIS_ProviderDetails pds[GNUNET_NZL (pds_len)];
3054
3055 /* initialize policies/vpolicies arrays */
3056 memset (pds,
3057 0,
3058 sizeof (pds));
3059 for (size_t i = 0; i<policies_len; i++)
3060 {
3061 const json_t *policy = json_array_get (jpolicies,
3062 i);
3063 const json_t *jmethods = json_object_get (policy,
3064 "methods");
3065 unsigned int methods_len;
3066
3067 if ( (! json_is_array (jmethods)) ||
3068 (0 == json_array_size (jmethods)) )
3069 {
3070 GNUNET_break (0);
3071 ANASTASIS_redux_fail_ (uc->cb,
3072 uc->cb_cls,
3073 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3074 "'methods' must be an array");
3075 GNUNET_JSON_parse_free (spec);
3076 upload_cancel_cb (uc);
3077 return;
3078 }
3079 methods_len = json_array_size (jmethods);
3080 {
3081 struct ANASTASIS_Policy *p;
3082 struct ANASTASIS_Truth *truths[methods_len];
3083 const struct ANASTASIS_Truth *ctruths[methods_len];
3084
3085 for (unsigned int j = 0; j<methods_len; j++)
3086 {
3087 const json_t *jmethod = json_array_get (jmethods,
3088 j);
3089 json_t *jtruth = NULL;
3090 uint32_t truth_index;
3091 const char *provider_url;
3092 struct GNUNET_JSON_Specification ispec[] = {
3093 GNUNET_JSON_spec_mark_optional (
3094 GNUNET_JSON_spec_json ("truth",
3095 &jtruth)),
3096 GNUNET_JSON_spec_string ("provider",
3097 &provider_url),
3098 GNUNET_JSON_spec_uint32 ("authentication_method",
3099 &truth_index),
3100 GNUNET_JSON_spec_end ()
3101 };
3102
3103 GNUNET_break (NULL != jmethod);
3104 if (GNUNET_OK !=
3105 GNUNET_JSON_parse (jmethod,
3106 ispec,
3107 NULL, NULL))
3108 {
3109 GNUNET_break (0);
3110 for (unsigned int k = 0; k<j; k++)
3111 ANASTASIS_truth_free (truths[k]);
3112 ANASTASIS_redux_fail_ (uc->cb,
3113 uc->cb_cls,
3114 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3115 "'truth' failed to decode");
3116 GNUNET_JSON_parse_free (spec);
3117 upload_cancel_cb (uc);
3118 return;
3119 }
3120 if (NULL != jtruth)
3121 {
3122 /* Get truth by deserializing from state */
3123 truths[j] = ANASTASIS_truth_from_json (jtruth);
3124 if (NULL == truths[j])
3125 {
3126 GNUNET_break (0);
3127 for (unsigned int k = 0; k<j; k++)
3128 ANASTASIS_truth_free (truths[k]);
3129 ANASTASIS_redux_fail_ (uc->cb,
3130 uc->cb_cls,
3131 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3132 "'truth' failed to decode");
3133 GNUNET_JSON_parse_free (ispec);
3134 GNUNET_JSON_parse_free (spec);
3135 upload_cancel_cb (uc);
3136 return;
3137 }
3138 }
3139 else
3140 {
3141 bool found = false;
3142 /* Maybe we never serialized the truth; find it in our DLL */
3143 for (struct TruthUpload *tue = uc->tues_head;
3144 NULL != tue;
3145 tue = tue->next)
3146 {
3147 GNUNET_break (NULL != tue->t);
3148 if ( (tue->am_idx == truth_index) &&
3149 (0 == strcmp (provider_url,
3150 tue->provider_url)) )
3151 {
3152 /* Duplicate truth object */
3153 json_t *jt = ANASTASIS_truth_to_json (tue->t);
3154
3155 GNUNET_assert (NULL != jt);
3156 truths[j] = ANASTASIS_truth_from_json (jt);
3157 GNUNET_assert (NULL != truths[j]);
3158 json_decref (jt);
3159 found = true;
3160 break;
3161 }
3162 }
3163 if (! found)
3164 {
3165 GNUNET_break (0);
3166 for (unsigned int k = 0; k<j; k++)
3167 ANASTASIS_truth_free (truths[k]);
3168 ANASTASIS_redux_fail_ (uc->cb,
3169 uc->cb_cls,
3170 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3171 "'truth' failed to decode");
3172 GNUNET_JSON_parse_free (ispec);
3173 GNUNET_JSON_parse_free (spec);
3174 upload_cancel_cb (uc);
3175 return;
3176 }
3177 }
3178 GNUNET_JSON_parse_free (ispec);
3179 ctruths[j] = truths[j];
3180 }
3181 p = ANASTASIS_policy_create (ctruths,
3182 methods_len);
3183 vpolicies[i] = p;
3184 policies[i] = p;
3185 for (unsigned int k = 0; k<methods_len; k++)
3186 ANASTASIS_truth_free (truths[k]);
3187 }
3188 }
3189
3190 /* initialize 'pds' array */
3191 for (unsigned int i = 0; i<pds_len; i++)
3192 {
3193 json_t *pdj = json_array_get (providers,
3194 i);
3195 struct GNUNET_JSON_Specification ispec[] = {
3196 GNUNET_JSON_spec_mark_optional (
3197 GNUNET_JSON_spec_fixed_auto ("payment_secret",
3198 &pds[i].payment_secret)),
3199 GNUNET_JSON_spec_string ("provider_url",
3200 &pds[i].provider_url),
3201 GNUNET_JSON_spec_end ()
3202 };
3203
3204 if ( (GNUNET_OK !=
3205 GNUNET_JSON_parse (pdj,
3206 ispec,
3207 NULL, NULL)) ||
3208 (GNUNET_OK !=
3209 lookup_salt (uc->state,
3210 pds[i].provider_url,
3211 &pds[i].provider_salt)) )
3212 {
3213 GNUNET_break (0);
3214 ANASTASIS_redux_fail_ (uc->cb,
3215 uc->cb_cls,
3216 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3217 "'providers' entry malformed");
3218 for (unsigned int i = 0; i<policies_len; i++)
3219 ANASTASIS_policy_destroy (vpolicies[i]);
3220 upload_cancel_cb (uc);
3221 GNUNET_JSON_parse_free (spec);
3222 return;
3223 }
3224 }
3225
3226 {
3227 char *secret;
3228 size_t secret_size;
3229
3230 secret = json_dumps (core_secret,
3231 JSON_COMPACT | JSON_SORT_KEYS);
3232 GNUNET_assert (NULL != secret);
3233 secret_size = strlen (secret);
3234 uc->ss = ANASTASIS_secret_share (ANASTASIS_REDUX_ctx_,
3235 user_id,
3236 pds,
3237 pds_len,
3238 policies,
3239 policies_len,
3240 uc->years,
3241 timeout,
3242 &secret_share_result_cb,
3243 uc,
3244 secret_name,
3245 secret,
3246 secret_size);
3247 GNUNET_free (secret);
3248 }
3249 for (unsigned int i = 0; i<policies_len; i++)
3250 ANASTASIS_policy_destroy (vpolicies[i]);
3251 }
3252 GNUNET_JSON_parse_free (spec);
3253 if (NULL == uc->ss)
3254 {
3255 GNUNET_break (0);
3256 ANASTASIS_redux_fail_ (uc->cb,
3257 uc->cb_cls,
3258 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
3259 "Failed to begin secret sharing");
3260 upload_cancel_cb (uc);
3261 return;
3262 }
3263}
3264
3265
3266/**
3267 * Some truth uploads require payment, serialize state and
3268 * request payment to be executed by the application.
3269 *
3270 * @param[in,out] uc context for the operation
3271 */
3272static void
3273request_truth_payment (struct UploadContext *uc)
3274{
3275 json_t *payments;
3276
3277 payments = json_array ();
3278 GNUNET_assert (NULL != payments);
3279 serialize_truth (uc);
3280 for (struct TruthUpload *tue = uc->tues_head;
3281 NULL != tue;
3282 tue = tue->next)
3283 {
3284 if (NULL == tue->payment_request)
3285 continue;
3286 GNUNET_assert (
3287 0 ==
3288 json_array_append_new (payments,
3289 json_string (
3290 tue->payment_request)));
3291 }
3292 GNUNET_assert (0 ==
3293 json_object_set_new (uc->state,
3294 "payments",
3295 payments));
3296 set_state (uc->state,
3297 ANASTASIS_BACKUP_STATE_TRUTHS_PAYING);
3298 uc->cb (uc->cb_cls,
3299 TALER_EC_NONE,
3300 uc->state);
3301 upload_cancel_cb (uc);
3302}
3303
3304
3305/**
3306 * We may be finished with all (active) asynchronous operations.
3307 * Check if any are pending and continue accordingly.
3308 *
3309 * @param[in,out] uc context for the operation
3310 */
3311static void
3312check_upload_finished (struct UploadContext *uc)
3313{
3314 bool pay = false;
3315 bool active = false;
3316
3317 for (struct TruthUpload *tue = uc->tues_head;
3318 NULL != tue;
3319 tue = tue->next)
3320 {
3321 if (TALER_EC_NONE != tue->ec)
3322 {
3323 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3324 "Truth upload failed with error %d\n",
3325 (int) tue->ec);
3326 uc->cb (uc->cb_cls,
3327 tue->ec,
3328 NULL);
3329 upload_cancel_cb (uc);
3330 return;
3331 }
3332 if (NULL != tue->tu)
3333 active = true;
3334 if (NULL != tue->payment_request)
3335 pay = true;
3336 }
3337 if (active)
3338 return;
3339 if (pay)
3340 {
3341 request_truth_payment (uc);
3342 return;
3343 }
3344 share_secret (uc);
3345}
3346
3347
3348/**
3349 * Upload result information. The resulting truth object can be used
3350 * to create policies. If payment is required, the @a taler_pay_url
3351 * is returned and the operation must be retried after payment.
3352 * Callee MUST free @a t using ANASTASIS_truth_free().
3353 *
3354 * @param cls closure with a `struct TruthUpload`
3355 * @param t truth object to create policies, NULL on failure
3356 * @param ud upload details
3357 */
3358static void
3359truth_upload_cb (void *cls,
3360 struct ANASTASIS_Truth *t,
3361 const struct ANASTASIS_UploadDetails *ud)
3362{
3363 struct TruthUpload *tue = cls;
3364
3365 tue->tu = NULL;
3366 tue->t = t;
3367 tue->ec = ud->ec;
3368 tue->us = ud->us;
3369 if (ANASTASIS_US_PAYMENT_REQUIRED == ud->us)
3370 {
3371 tue->payment_request = GNUNET_strdup (
3372 ud->details.payment.payment_request);
3373 }
3374 check_upload_finished (tue->uc);
3375}
3376
3377
3378/**
3379 * Check if we still need to create a new truth object for the truth
3380 * identified by @a provider_url and @a am_idx. If so, create it from
3381 * @a truth for policy reference @a pmr. If such a truth object
3382 * already exists, append @a pmr to its list of reasons.
3383 *
3384 * @param[in,out] our upload context
3385 * @param pmr policy method combination that requires the truth
3386 * @param provider_url the URL of the Anastasis provider to upload
3387 * the truth to, used to check for existing entries
3388 * @param am_idx index of the authentication method, used to check for existing entries
3389 * @param[in] truth object representing already uploaded truth, reference captured!
3390 * @param[in,out] async_truth pointer to counter with the number of ongoing uploads,
3391 * updated
3392 * @param auth_method object with the challenge details, to generate the truth
3393 * @return #GNUNET_SYSERR error requiring abort,
3394 * #GNUNET_OK on success
3395 */
3396static int
3397add_truth_object (struct UploadContext *uc,
3398 const struct PolicyMethodReference *pmr,
3399 const char *provider_url,
3400 uint32_t am_idx,
3401 json_t *truth,
3402 unsigned int *async_truth,
3403 json_t *auth_method)
3404{
3405 /* check if we are already uploading this truth */
3406 struct TruthUpload *tue;
3407 bool must_upload = true;
3408
3409 for (tue = uc->tues_head;
3410 NULL != tue;
3411 tue = tue->next)
3412 {
3413 if ( (0 == strcmp (tue->provider_url,
3414 provider_url)) &&
3415 (am_idx == tue->am_idx) )
3416 {
3417 GNUNET_array_append (tue->policies,
3418 tue->policies_length,
3419 *pmr);
3420 break;
3421 }
3422 }
3423
3424 if (NULL == tue)
3425 {
3426 /* Create new entry */
3427 tue = GNUNET_new (struct TruthUpload);
3428
3429 GNUNET_CONTAINER_DLL_insert (uc->tues_head,
3430 uc->tues_tail,
3431 tue);
3432 tue->uc = uc;
3433 tue->policies = GNUNET_new (struct PolicyMethodReference);
3434 *tue->policies = *pmr;
3435 tue->provider_url = GNUNET_strdup (provider_url);
3436 tue->am_idx = am_idx;
3437 tue->policies_length = 1;
3438 }
3439
3440 {
3441 uint32_t status = UINT32_MAX;
3442 struct GNUNET_JSON_Specification spec[] = {
3443 GNUNET_JSON_spec_mark_optional (
3444 GNUNET_JSON_spec_uint32 ("upload_status",
3445 &status)),
3446 GNUNET_JSON_spec_end ()
3447 };
3448 if (GNUNET_OK !=
3449 GNUNET_JSON_parse (truth,
3450 spec,
3451 NULL, NULL))
3452 {
3453 GNUNET_break (0);
3454 return GNUNET_SYSERR;
3455 }
3456
3457 must_upload = (ANASTASIS_US_SUCCESS != status);
3458 }
3459
3460 if (NULL == tue->t)
3461 {
3462 tue->t = ANASTASIS_truth_from_json (truth);
3463 if (NULL == tue->t)
3464 {
3465 GNUNET_break (0);
3466 return GNUNET_SYSERR;
3467 }
3468 }
3469
3470 if ( (NULL != tue->tu) &&
3471 (! must_upload) )
3472 {
3473 ANASTASIS_truth_upload_cancel (tue->tu);
3474 (*async_truth)--;
3475 tue->tu = NULL;
3476 return GNUNET_OK;
3477 }
3478
3479 if ( (NULL == tue->tu) &&
3480 (must_upload) )
3481 {
3482 struct ANASTASIS_CRYPTO_ProviderSaltP salt;
3483 struct ANASTASIS_CRYPTO_UserIdentifierP id;
3484 void *truth_data;
3485 size_t truth_data_size;
3486 struct GNUNET_JSON_Specification spec[] = {
3487 GNUNET_JSON_spec_varsize ("challenge",
3488 &truth_data,
3489 &truth_data_size),
3490 GNUNET_JSON_spec_end ()
3491 };
3492
3493 if (GNUNET_OK !=
3494 lookup_salt (uc->state,
3495 provider_url,
3496 &salt))
3497 {
3498 GNUNET_break (0);
3499 return GNUNET_SYSERR;
3500 }
3501 if (GNUNET_OK !=
3502 GNUNET_JSON_parse (auth_method,
3503 spec,
3504 NULL, NULL))
3505 {
3506 json_dumpf (auth_method,
3507 stderr,
3508 JSON_INDENT (2));
3509 GNUNET_break (0);
3510 return GNUNET_SYSERR;
3511 }
3512 {
3513 json_t *user_id;
3514
3515 user_id = json_object_get (uc->state,
3516 "identity_attributes");
3517 if (! json_is_object (user_id))
3518 {
3519 GNUNET_break (0);
3520 return GNUNET_SYSERR;
3521 }
3522 ANASTASIS_CRYPTO_user_identifier_derive (user_id,
3523 &salt,
3524 &id);
3525 }
3526 tue->tu = ANASTASIS_truth_upload3 (ANASTASIS_REDUX_ctx_,
3527 &id,
3528 tue->t,
3529 truth_data,
3530 truth_data_size,
3531 uc->years,
3532 uc->timeout,
3533 &truth_upload_cb,
3534 tue);
3535 GNUNET_JSON_parse_free (spec);
3536 tue->t = NULL;
3537 (*async_truth)++;
3538 }
3539
3540 if ( (NULL != tue->tu) &&
3541 (NULL != tue->t) )
3542 {
3543 /* no point in having both */
3544 ANASTASIS_truth_free (tue->t);
3545 tue->t = NULL;
3546 }
3547 return GNUNET_OK;
3548}
3549
3550
3551/**
3552 * Check if we still need to upload the truth identified by
3553 * @a provider_url and @a am_idx. If so, upload it for
3554 * policy reference @a pmr. If the upload is already queued,
3555 * append @a pmr to its list of reasons.
3556 *
3557 * @param[in,out] our upload context
3558 * @param pmr policy method combination that requires the truth
3559 * @param provider_url the URL of the Anastasis provider to upload
3560 * the truth to, used to check for existing entries
3561 * @param am_idx index of the authentication method, used to check for existing entries
3562 * @param auth_method object with the challenge details, to generate the truth
3563 * @return #GNUNET_SYSERR on error requiring abort,
3564 * #GNUNET_NO if no new truth upload was generated (@a pmr was appended)
3565 * #GNUNET_OK if a new truth upload was initiated
3566 */
3567static int
3568check_truth_upload (struct UploadContext *uc,
3569 const struct PolicyMethodReference *pmr,
3570 const char *provider_url,
3571 uint32_t am_idx,
3572 json_t *auth_method)
3573{
3574 json_t *user_id;
3575 json_t *jtruth;
3576 struct TruthUpload *tue;
3577
3578 user_id = json_object_get (uc->state,
3579 "identity_attributes");
3580 if (! json_is_object (user_id))
3581 {
3582 GNUNET_break (0);
3583 return GNUNET_SYSERR;
3584 }
3585
3586 /* check if we are already uploading this truth */
3587 for (tue = uc->tues_head;
3588 NULL != tue;
3589 tue = tue->next)
3590 {
3591 if ( (0 == strcmp (tue->provider_url,
3592 provider_url)) &&
3593 (am_idx == tue->am_idx) )
3594 {
3595 GNUNET_array_append (tue->policies,
3596 tue->policies_length,
3597 *pmr);
3598 return GNUNET_NO;
3599 }
3600 }
3601
3602 /* need new upload */
3603 tue = GNUNET_new (struct TruthUpload);
3604 {
3605 json_t *policies = json_object_get (uc->state,
3606 "policies");
3607 json_t *policy = json_array_get (policies,
3608 pmr->policy_index);
3609 json_t *methods = json_object_get (policy,
3610 "methods");
3611 json_t *method = json_array_get (methods,
3612 pmr->method_index);
3613
3614 jtruth = json_object_get (method,
3615 "truth");
3616 }
3617
3618 {
3619 const char *type;
3620 const char *mime_type = NULL;
3621 const char *instructions = NULL;
3622 void *truth_data;
3623 size_t truth_data_size;
3624 struct GNUNET_JSON_Specification spec[] = {
3625 GNUNET_JSON_spec_string ("type",
3626 &type),
3627 GNUNET_JSON_spec_mark_optional (
3628 GNUNET_JSON_spec_string ("mime_type",
3629 &mime_type)),
3630 GNUNET_JSON_spec_mark_optional (
3631 GNUNET_JSON_spec_string ("instructions",
3632 &instructions)),
3633 GNUNET_JSON_spec_varsize ("challenge",
3634 &truth_data,
3635 &truth_data_size),
3636 GNUNET_JSON_spec_end ()
3637 };
3638 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
3639 struct ANASTASIS_CRYPTO_UserIdentifierP id;
3640
3641 if (GNUNET_OK !=
3642 GNUNET_JSON_parse (auth_method,
3643 spec,
3644 NULL, NULL))
3645 {
3646 json_dumpf (auth_method,
3647 stderr,
3648 JSON_INDENT (2));
3649 GNUNET_break (0);
3650 GNUNET_free (tue);
3651 return GNUNET_SYSERR;
3652 }
3653 GNUNET_CONTAINER_DLL_insert (uc->tues_head,
3654 uc->tues_tail,
3655 tue);
3656 tue->uc = uc;
3657 tue->policies = GNUNET_new (struct PolicyMethodReference);
3658 *tue->policies = *pmr;
3659 tue->provider_url = GNUNET_strdup (provider_url);
3660 tue->am_idx = am_idx;
3661 tue->policies_length = 1;
3662 if (GNUNET_OK !=
3663 lookup_salt (uc->state,
3664 provider_url,
3665 &provider_salt))
3666 {
3667 GNUNET_break (0);
3668 GNUNET_JSON_parse_free (spec);
3669 upload_cancel_cb (uc);
3670 return GNUNET_SYSERR;
3671 }
3672 ANASTASIS_CRYPTO_user_identifier_derive (user_id,
3673 &provider_salt,
3674 &id);
3675 {
3676 struct ANASTASIS_CRYPTO_TruthUUIDP uuid;
3677 struct ANASTASIS_CRYPTO_QuestionSaltP question_salt;
3678 struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
3679 struct ANASTASIS_CRYPTO_KeyShareP key_share;
3680 struct ANASTASIS_CRYPTO_NonceP nonce;
3681
3682 struct GNUNET_JSON_Specification jspec[] = {
3683 GNUNET_JSON_spec_fixed_auto ("salt",
3684 &question_salt),
3685 GNUNET_JSON_spec_fixed_auto ("truth_key",
3686 &truth_key),
3687 GNUNET_JSON_spec_fixed_auto ("nonce",
3688 &nonce),
3689 GNUNET_JSON_spec_fixed_auto ("uuid",
3690 &uuid),
3691 GNUNET_JSON_spec_fixed_auto ("key_share",
3692 &key_share),
3693 GNUNET_JSON_spec_end ()
3694 };
3695
3696 if (GNUNET_OK !=
3697 GNUNET_JSON_parse (jtruth,
3698 jspec,
3699 NULL, NULL))
3700 {
3701 tue->tu = ANASTASIS_truth_upload (ANASTASIS_REDUX_ctx_,
3702 &id,
3703 provider_url,
3704 type,
3705 instructions,
3706 mime_type,
3707 &provider_salt,
3708 truth_data,
3709 truth_data_size,
3710 uc->years,
3711 uc->timeout,
3712 &truth_upload_cb,
3713 tue);
3714 }
3715 else
3716 {
3717 tue->tu = ANASTASIS_truth_upload2 (ANASTASIS_REDUX_ctx_,
3718 &id,
3719 provider_url,
3720 type,
3721 instructions,
3722 mime_type,
3723 &provider_salt,
3724 truth_data,
3725 truth_data_size,
3726 uc->years,
3727 uc->timeout,
3728 &nonce,
3729 &uuid,
3730 &question_salt,
3731 &truth_key,
3732 &key_share,
3733 &truth_upload_cb,
3734 tue);
3735 }
3736 }
3737 if (NULL == tue->tu)
3738 {
3739 GNUNET_break (0);
3740 GNUNET_JSON_parse_free (spec);
3741 upload_cancel_cb (uc);
3742 return GNUNET_SYSERR;
3743 }
3744 GNUNET_JSON_parse_free (spec);
3745 return GNUNET_OK;
3746 }
3747}
3748
3749
3750/**
3751 * Function to upload truths and recovery document policies.
3752 * Ultimately transitions to failed state (allowing user to go back
3753 * and change providers/policies), or payment, or finished.
3754 *
3755 * @param state state to operate on
3756 * @param truth_indices indices of truths to upload explicitly
3757 * @param cb callback (#ANASTASIS_ActionCallback) to call after upload
3758 * @param cb_cls callback closure
3759 */
3760static struct ANASTASIS_ReduxAction *
3761upload (json_t *state,
3762 ANASTASIS_ActionCallback cb,
3763 void *cb_cls)
3764{
3765 struct UploadContext *uc;
3766 json_t *auth_methods;
3767 json_t *policies;
3768 struct GNUNET_TIME_Absolute expiration;
3769 struct GNUNET_JSON_Specification spec[] = {
3770 GNUNET_JSON_spec_absolute_time ("expiration",
3771 &expiration),
3772 GNUNET_JSON_spec_end ()
3773 };
3774
3775 if (GNUNET_OK !=
3776 GNUNET_JSON_parse (state,
3777 spec,
3778 NULL, NULL))
3779 {
3780 ANASTASIS_redux_fail_ (cb,
3781 cb_cls,
3782 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3783 "'expiration' missing");
3784 return NULL;
3785 }
3786 auth_methods = json_object_get (state,
3787 "authentication_methods");
3788 if ( (! json_is_array (auth_methods)) ||
3789 (0 == json_array_size (auth_methods)) )
3790 {
3791 ANASTASIS_redux_fail_ (cb,
3792 cb_cls,
3793 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3794 "'authentication_methods' must be non-empty array");
3795 return NULL;
3796 }
3797 policies = json_object_get (state,
3798 "policies");
3799 if ( (! json_is_array (policies)) ||
3800 (0 == json_array_size (policies)) )
3801 {
3802 ANASTASIS_redux_fail_ (cb,
3803 cb_cls,
3804 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3805 "'policies' must be non-empty array");
3806 return NULL;
3807 }
3808
3809 uc = GNUNET_new (struct UploadContext);
3810 uc->ra.cleanup = &upload_cancel_cb;
3811 uc->ra.cleanup_cls = uc;
3812 uc->cb = cb;
3813 uc->cb_cls = cb_cls;
3814 uc->state = json_incref (state);
3815 uc->years = expiration_to_years (expiration);
3816
3817 {
3818 json_t *args;
3819 struct GNUNET_JSON_Specification pspec[] = {
3820 GNUNET_JSON_spec_mark_optional (
3821 GNUNET_JSON_spec_relative_time ("timeout",
3822 &uc->timeout)),
3823 GNUNET_JSON_spec_end ()
3824 };
3825
3826 args = json_object_get (uc->state,
3827 "pay-arguments");
3828 if ( (NULL != args) &&
3829 (GNUNET_OK !=
3830 GNUNET_JSON_parse (args,
3831 pspec,
3832 NULL, NULL)) )
3833 {
3834 json_dumpf (args,
3835 stderr,
3836 JSON_INDENT (2));
3837 GNUNET_break (0);
3838 ANASTASIS_redux_fail_ (cb,
3839 cb_cls,
3840 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
3841 "'timeout' must be valid delay");
3842
3843 return NULL;
3844 }
3845 }
3846
3847 {
3848 json_t *policy;
3849 size_t pindex;
3850 unsigned int async_truth = 0;
3851
3852 json_array_foreach (policies, pindex, policy)
3853 {
3854 json_t *methods = json_object_get (policy,
3855 "methods");
3856 json_t *auth_method;
3857 size_t mindex;
3858
3859 if ( (! json_is_array (methods)) ||
3860 (0 == json_array_size (policies)) )
3861 {
3862 ANASTASIS_redux_fail_ (cb,
3863 cb_cls,
3864 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3865 "'policies' must be non-empty array");
3866 upload_cancel_cb (uc);
3867 return NULL;
3868 }
3869 json_array_foreach (methods, mindex, auth_method)
3870 {
3871 uint32_t am_idx;
3872 const char *provider_url;
3873 json_t *truth = NULL;
3874 struct GNUNET_JSON_Specification spec[] = {
3875 GNUNET_JSON_spec_string ("provider",
3876 &provider_url),
3877 GNUNET_JSON_spec_uint32 ("authentication_method",
3878 &am_idx),
3879 GNUNET_JSON_spec_mark_optional (
3880 GNUNET_JSON_spec_json ("truth",
3881 &truth)),
3882 GNUNET_JSON_spec_end ()
3883 };
3884
3885 if (GNUNET_OK !=
3886 GNUNET_JSON_parse (auth_method,
3887 spec,
3888 NULL, NULL))
3889 {
3890 ANASTASIS_redux_fail_ (cb,
3891 cb_cls,
3892 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3893 "'method' data malformed");
3894 upload_cancel_cb (uc);
3895 return NULL;
3896 }
3897 {
3898 struct PolicyMethodReference pmr = {
3899 .policy_index = pindex,
3900 .method_index = mindex
3901 };
3902 json_t *amj;
3903
3904 amj = json_array_get (auth_methods,
3905 am_idx);
3906 if (NULL == amj)
3907 {
3908 ANASTASIS_redux_fail_ (cb,
3909 cb_cls,
3910 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3911 "'authentication_method' refers to invalid authorization index malformed");
3912 upload_cancel_cb (uc);
3913 GNUNET_JSON_parse_free (spec);
3914 return NULL;
3915 }
3916 if (NULL == truth)
3917 {
3918 int ret;
3919
3920 ret = check_truth_upload (uc,
3921 &pmr,
3922 provider_url,
3923 am_idx,
3924 amj);
3925 if (GNUNET_SYSERR == ret)
3926 {
3927 GNUNET_JSON_parse_free (spec);
3928 ANASTASIS_redux_fail_ (cb,
3929 cb_cls,
3930 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3931 NULL);
3932 return NULL;
3933 }
3934 if (GNUNET_OK == ret)
3935 async_truth++;
3936 }
3937 else
3938 {
3939 int ret;
3940
3941 ret = add_truth_object (uc,
3942 &pmr,
3943 provider_url,
3944 am_idx,
3945 truth,
3946 &async_truth,
3947 amj);
3948 if (GNUNET_SYSERR == ret)
3949 {
3950 GNUNET_JSON_parse_free (spec);
3951 ANASTASIS_redux_fail_ (cb,
3952 cb_cls,
3953 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
3954 NULL);
3955 return NULL;
3956 }
3957 }
3958 }
3959 GNUNET_JSON_parse_free (spec);
3960 } /* end for all methods of policy */
3961 } /* end for all policies */
3962 if (async_truth > 0)
3963 return &uc->ra;
3964 }
3965 share_secret (uc);
3966 if (NULL == uc->ss)
3967 return NULL;
3968 return &uc->ra;
3969}
3970
3971
3972/**
3973 * Test if the core secret @a secret_size is small enough to be stored
3974 * at all providers, which have a minimum upload limit of @a min_limit_in_mb.
3975 *