aboutsummaryrefslogtreecommitdiff
path: root/src/lib/anastasis_recovery.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/anastasis_recovery.c')
-rw-r--r--src/lib/anastasis_recovery.c1425
1 files changed, 1425 insertions, 0 deletions
diff --git a/src/lib/anastasis_recovery.c b/src/lib/anastasis_recovery.c
new file mode 100644
index 0000000..5b0726f
--- /dev/null
+++ b/src/lib/anastasis_recovery.c
@@ -0,0 +1,1425 @@
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 * @brief anastasis client api
18 * @author Christian Grothoff
19 * @author Dominik Meister
20 * @author Dennis Neufeld
21 */
22#include "platform.h"
23#include "anastasis.h"
24#include <taler/taler_json_lib.h>
25#include <gnunet/gnunet_util_lib.h>
26#include <taler/taler_merchant_service.h>
27#include <zlib.h>
28
29
30/**
31 * Challenge struct contains the uuid and public key's needed for the
32 * recovery process and a reference to ANASTASIS_Recovery.
33 */
34struct ANASTASIS_Challenge
35{
36
37 /**
38 * Information exported to clients about this challenge.
39 */
40 struct ANASTASIS_ChallengeDetails ci;
41
42 /**
43 * Key used to encrypt the truth passed to the server
44 */
45 struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
46
47 /**
48 * Salt; used to derive hash from security question answers.
49 */
50 struct ANASTASIS_CRYPTO_QuestionSaltP salt;
51
52 /**
53 * Provider salt; used to derive our key material from our identity
54 * key.
55 */
56 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
57
58 /**
59 * Decrypted key share for this challenge. Set once the
60 * challenge was @e ri.solved.
61 */
62 struct ANASTASIS_CRYPTO_KeyShareP key_share;
63
64 /**
65 * Callback which gives back the instructions and a status code of
66 * the request to the user when answering a challenge was initiated.
67 */
68 ANASTASIS_AnswerFeedback af;
69
70 /**
71 * Closure for the challenge callback
72 */
73 void *af_cls;
74
75 /**
76 * Defines the base URL of the Anastasis provider used for the challenge.
77 */
78 char *url;
79
80 /**
81 * What is the type of this challenge (E-Mail, Security Question, SMS...)
82 */
83 char *type;
84
85 /**
86 * Instructions for solving the challenge (generic, set client-side
87 * when challenge was established).
88 */
89 char *instructions;
90
91 /**
92 * Answer to the security question, if @a type is "question". Otherwise NULL.
93 */
94 char *answer;
95
96 /**
97 * Reference to the recovery process which is ongoing
98 */
99 struct ANASTASIS_Recovery *recovery;
100
101 /**
102 * keyshare lookup operation
103 */
104 struct ANASTASIS_KeyShareLookupOperation *kslo;
105
106};
107
108
109/**
110 * Defines a decryption policy with multiple escrow methods
111 */
112struct DecryptionPolicy
113{
114
115 /**
116 * Publicly visible details about a decryption policy.
117 */
118 struct ANASTASIS_DecryptionPolicy pub_details;
119
120 /**
121 * Encrypted masterkey (encrypted with the policy key).
122 */
123 struct ANASTASIS_CRYPTO_EncryptedMasterKeyP emk;
124
125 /**
126 * Salt used to decrypt master key.
127 */
128 struct ANASTASIS_CRYPTO_MasterSaltP salt;
129
130};
131
132
133/**
134 * stores provider URLs, identity key material, decrypted recovery document (internally!)
135 */
136struct ANASTASIS_Recovery
137{
138
139 /**
140 * Identity key material used for the derivation of keys
141 */
142 struct ANASTASIS_CRYPTO_UserIdentifierP id;
143
144 /**
145 * Recovery information which is given to the user
146 */
147 struct ANASTASIS_RecoveryInformation ri;
148
149 /**
150 * Internal of @e ri.dps_len policies that would allow recovery of the core secret.
151 */
152 struct DecryptionPolicy *dps;
153
154 /**
155 * Array of @e ri.cs_len challenges to be solved (for any of the policies).
156 */
157 struct ANASTASIS_Challenge *cs;
158
159 /**
160 * Identity data to user id from.
161 */
162 json_t *id_data;
163
164 /**
165 * Callback to send back a recovery document with the policies and the version
166 */
167 ANASTASIS_PolicyCallback pc;
168
169 /**
170 * closure for the Policy callback
171 */
172 void *pc_cls;
173
174 /**
175 * Callback to send back the core secret which was saved by
176 * anastasis, after all challenges are completed
177 */
178 ANASTASIS_CoreSecretCallback csc;
179
180 /**
181 * Closure for the core secret callback
182 */
183 void *csc_cls;
184
185 /**
186 * Curl context
187 */
188 struct GNUNET_CURL_Context *ctx;
189
190 /**
191 * Reference to the policy lookup operation which is executed
192 */
193 struct ANASTASIS_PolicyLookupOperation *plo;
194
195 /**
196 * Array of challenges that have been solved.
197 * Valid entries up to @e solved_challenge_pos.
198 * Length matches the total number of challenges in @e ri.
199 */
200 struct ANASTASIS_Challenge **solved_challenges;
201
202 /**
203 * Our provider URL.
204 */
205 char *provider_url;
206
207 /**
208 * Name of the secret, can be NULL.
209 */
210 char *secret_name;
211
212 /**
213 * Task to run @e pc asynchronously.
214 */
215 struct GNUNET_SCHEDULER_Task *do_async;
216
217 /**
218 * Retrieved encrypted core secret from policy
219 */
220 void *enc_core_secret;
221
222 /**
223 * Size of the @e enc_core_secret
224 */
225 size_t enc_core_secret_size;
226
227 /**
228 * Current offset in the @e solved_challenges array.
229 */
230 unsigned int solved_challenge_pos;
231
232};
233
234
235/**
236 * Function called with the results of a #ANASTASIS_keyshare_lookup().
237 *
238 * @param cls closure
239 * @param http_status HTTP status of the request
240 * @param ud details about the lookup operation
241 */
242static void
243keyshare_lookup_cb (void *cls,
244 const struct ANASTASIS_KeyShareDownloadDetails *dd)
245{
246 struct ANASTASIS_Challenge *c = cls;
247 struct ANASTASIS_Recovery *recovery = c->recovery;
248 struct ANASTASIS_CRYPTO_UserIdentifierP id;
249 struct DecryptionPolicy *rdps;
250
251 c->kslo = NULL;
252 switch (dd->status)
253 {
254 case ANASTASIS_KSD_SUCCESS:
255 break;
256 case ANASTASIS_KSD_PAYMENT_REQUIRED:
257 {
258 struct ANASTASIS_ChallengeStartResponse csr = {
259 .cs = ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED,
260 .challenge = c,
261 .details.payment_required.taler_pay_uri
262 = dd->details.payment_required.taler_pay_uri,
263 .details.payment_required.payment_secret
264 = dd->details.payment_required.payment_secret
265 };
266
267 c->af (c->af_cls,
268 &csr);
269 return;
270 }
271 case ANASTASIS_KSD_INVALID_ANSWER:
272 {
273 struct ANASTASIS_ChallengeStartResponse csr = {
274 .cs = ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS,
275 .challenge = c,
276 .details.open_challenge.body
277 = dd->details.open_challenge.body,
278 .details.open_challenge.content_type
279 = dd->details.open_challenge.content_type,
280 .details.open_challenge.body_size
281 = dd->details.open_challenge.body_size,
282 .details.open_challenge.http_status
283 = dd->details.open_challenge.http_status
284 };
285
286 c->af (c->af_cls,
287 &csr);
288 return;
289 }
290 case ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION:
291 {
292 struct ANASTASIS_ChallengeStartResponse csr = {
293 .cs = ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION,
294 .challenge = c,
295 .details.redirect_url
296 = dd->details.redirect_url
297 };
298
299 c->af (c->af_cls,
300 &csr);
301 return;
302 }
303 case ANASTASIS_KSD_TRUTH_UNKNOWN:
304 {
305 struct ANASTASIS_ChallengeStartResponse csr = {
306 .cs = ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN,
307 .challenge = c
308 };
309
310 c->af (c->af_cls,
311 &csr);
312 return;
313 }
314 case ANASTASIS_KSD_RATE_LIMIT_EXCEEDED:
315 {
316 struct ANASTASIS_ChallengeStartResponse csr = {
317 .cs = ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED,
318 .challenge = c
319 };
320
321 c->af (c->af_cls,
322 &csr);
323 return;
324 }
325 case ANASTASIS_KSD_SERVER_ERROR:
326 case ANASTASIS_KSD_CLIENT_FAILURE:
327 {
328 struct ANASTASIS_ChallengeStartResponse csr = {
329 .cs = ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE,
330 .challenge = c,
331 .details.server_failure.ec
332 = dd->details.server_failure.ec,
333 .details.server_failure.http_status
334 = dd->details.server_failure.http_status
335 };
336
337 c->af (c->af_cls,
338 &csr);
339 return;
340 }
341 }
342
343 GNUNET_assert (NULL != dd);
344 ANASTASIS_CRYPTO_user_identifier_derive (recovery->id_data,
345 &c->provider_salt,
346 &id);
347 ANASTASIS_CRYPTO_keyshare_decrypt (&dd->details.eks,
348 &id,
349 c->answer,
350 &c->key_share);
351 recovery->solved_challenges[recovery->solved_challenge_pos++] = c;
352
353 {
354 struct ANASTASIS_ChallengeStartResponse csr = {
355 .cs = ANASTASIS_CHALLENGE_STATUS_SOLVED,
356 .challenge = c
357 };
358
359 c->ci.solved = true;
360 c->af (c->af_cls,
361 &csr);
362 }
363
364
365 /* Check if there is a policy for which all challenges have
366 been satisfied, if so, store it in 'rdps'. */
367 rdps = NULL;
368 for (unsigned int i = 0; i < recovery->ri.dps_len; i++)
369 {
370 struct DecryptionPolicy *dps = &recovery->dps[i];
371 bool missing = false;
372
373 for (unsigned int j = 0; j < dps->pub_details.challenges_length; j++)
374 {
375 bool found = false;
376
377 for (unsigned int k = 0; k < recovery->solved_challenge_pos; k++)
378 {
379 if (dps->pub_details.challenges[j] == recovery->solved_challenges[k])
380 {
381 found = true;
382 break;
383 }
384 }
385 if (! found)
386 {
387 missing = true;
388 break;
389 }
390 }
391 if (! missing)
392 {
393 rdps = dps;
394 break;
395 }
396 }
397 if (NULL == rdps)
398 return;
399
400 {
401 void *core_secret;
402 size_t core_secret_size;
403 struct ANASTASIS_CRYPTO_KeyShareP
404 key_shares[rdps->pub_details.challenges_length];
405 struct ANASTASIS_CRYPTO_PolicyKeyP policy_key;
406
407 for (unsigned int l = 0; l < rdps->pub_details.challenges_length; l++)
408 for (unsigned int m = 0; m < recovery->solved_challenge_pos; m++)
409 if (rdps->pub_details.challenges[l] == recovery->solved_challenges[m])
410 key_shares[l] = recovery->solved_challenges[m]->key_share;
411 ANASTASIS_CRYPTO_policy_key_derive (key_shares,
412 rdps->pub_details.challenges_length,
413 &rdps->salt,
414 &policy_key);
415 ANASTASIS_CRYPTO_core_secret_recover (&rdps->emk,
416 &policy_key,
417 recovery->enc_core_secret,
418 recovery->enc_core_secret_size,
419 &core_secret,
420 &core_secret_size);
421 recovery->csc (recovery->csc_cls,
422 ANASTASIS_RS_SUCCESS,
423 core_secret,
424 core_secret_size);
425 GNUNET_free (core_secret);
426 ANASTASIS_recovery_abort (recovery);
427 }
428}
429
430
431const struct ANASTASIS_ChallengeDetails *
432ANASTASIS_challenge_get_details (struct ANASTASIS_Challenge *challenge)
433{
434 return &challenge->ci;
435}
436
437
438int
439ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
440 const struct ANASTASIS_PaymentSecretP *psp,
441 struct GNUNET_TIME_Relative timeout,
442 const struct GNUNET_HashCode *hashed_answer,
443 ANASTASIS_AnswerFeedback af,
444 void *af_cls)
445{
446 if (c->ci.solved)
447 {
448 GNUNET_break (0);
449 return GNUNET_NO; /* already solved */
450 }
451 if (NULL != c->kslo)
452 {
453 GNUNET_break (0);
454 return GNUNET_NO; /* already solving */
455 }
456 c->af = af;
457 c->af_cls = af_cls;
458 c->kslo = ANASTASIS_keyshare_lookup (c->recovery->ctx,
459 c->url,
460 &c->ci.uuid,
461 &c->truth_key,
462 psp,
463 timeout,
464 hashed_answer,
465 &keyshare_lookup_cb,
466 c);
467 if (NULL == c->kslo)
468 {
469 GNUNET_break (0);
470 return GNUNET_SYSERR;
471 }
472 return GNUNET_OK;
473}
474
475
476int
477ANASTASIS_challenge_answer (
478 struct ANASTASIS_Challenge *c,
479 const struct ANASTASIS_PaymentSecretP *psp,
480 struct GNUNET_TIME_Relative timeout,
481 const char *answer_str,
482 ANASTASIS_AnswerFeedback af,
483 void *af_cls)
484{
485 struct GNUNET_HashCode hashed_answer;
486
487 GNUNET_free (c->answer);
488 c->answer = GNUNET_strdup (answer_str);
489 ANASTASIS_CRYPTO_secure_answer_hash (answer_str,
490 &c->ci.uuid,
491 &c->salt,
492 &hashed_answer);
493 return ANASTASIS_challenge_start (c,
494 psp,
495 timeout,
496 &hashed_answer,
497 af,
498 af_cls);
499}
500
501
502int
503ANASTASIS_challenge_answer2 (struct ANASTASIS_Challenge *c,
504 const struct ANASTASIS_PaymentSecretP *psp,
505 struct GNUNET_TIME_Relative timeout,
506 uint64_t answer,
507 ANASTASIS_AnswerFeedback af,
508 void *af_cls)
509{
510 struct GNUNET_HashCode answer_s;
511
512 ANASTASIS_hash_answer (answer,
513 &answer_s);
514 return ANASTASIS_challenge_start (c,
515 psp,
516 timeout,
517 &answer_s,
518 af,
519 af_cls);
520}
521
522
523void
524ANASTASIS_challenge_abort (struct ANASTASIS_Challenge *c)
525{
526 if (NULL == c->kslo)
527 {
528 GNUNET_break (0);
529 return;
530 }
531 ANASTASIS_keyshare_lookup_cancel (c->kslo);
532 c->kslo = NULL;
533 c->af = NULL;
534 c->af_cls = NULL;
535}
536
537
538/**
539 * Function called with the results of a ANASTASIS_policy_lookup
540 *
541 * @param cls closure
542 * @param http_status HTTP status of the request
543 * @param ud details about the lookup operation
544 */
545static void
546policy_lookup_cb (void *cls,
547 unsigned int http_status,
548 const struct ANASTASIS_DownloadDetails *dd)
549{
550 struct ANASTASIS_Recovery *r = cls;
551 void *plaintext;
552 size_t size_plaintext;
553 json_error_t json_error;
554 json_t *dec_policies;
555 json_t *esc_methods;
556
557 r->plo = NULL;
558 switch (http_status)
559 {
560 case MHD_HTTP_OK:
561 break;
562 case MHD_HTTP_NOT_FOUND:
563 r->csc (r->csc_cls,
564 ANASTASIS_RS_POLICY_UNKNOWN,
565 NULL,
566 0);
567 ANASTASIS_recovery_abort (r);
568 return;
569 case MHD_HTTP_NO_CONTENT:
570 /* Account known, policy expired */
571 r->csc (r->csc_cls,
572 ANASTASIS_RS_POLICY_GONE,
573 NULL,
574 0);
575 ANASTASIS_recovery_abort (r);
576 return;
577 case MHD_HTTP_INTERNAL_SERVER_ERROR:
578 /* Bad server... */
579 r->csc (r->csc_cls,
580 ANASTASIS_RS_POLICY_SERVER_ERROR,
581 NULL,
582 0);
583 ANASTASIS_recovery_abort (r);
584 return;
585 case MHD_HTTP_NOT_MODIFIED:
586 /* Should not be possible, we do not cache, fall-through! */
587 default:
588 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
589 "Unexpected response code %u in %s:%u\n",
590 http_status,
591 __FILE__,
592 __LINE__);
593 r->csc (r->csc_cls,
594 ANASTASIS_RS_POLICY_DOWNLOAD_FAILED,
595 NULL,
596 0);
597 ANASTASIS_recovery_abort (r);
598 return;
599 }
600 if (NULL == dd->policy)
601 {
602 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
603 "No recovery data available");
604 r->csc (r->csc_cls,
605 ANASTASIS_RS_POLICY_DOWNLOAD_NO_POLICY,
606 NULL,
607 0);
608 ANASTASIS_recovery_abort (r);
609 return;
610 }
611 ANASTASIS_CRYPTO_recovery_document_decrypt (&r->id,
612 dd->policy,
613 dd->policy_size,
614 &plaintext,
615 &size_plaintext);
616 if (size_plaintext < sizeof (uint32_t))
617 {
618 GNUNET_break_op (0);
619 r->csc (r->csc_cls,
620 ANASTASIS_RS_POLICY_DOWNLOAD_INVALID_COMPRESSION,
621 NULL,
622 0);
623 ANASTASIS_recovery_abort (r);
624 GNUNET_free (plaintext);
625 return;
626 }
627 {
628 json_t *recovery_document;
629 uint32_t be_size;
630 uLongf pt_size;
631 char *pt;
632
633 memcpy (&be_size,
634 plaintext,
635 sizeof (uint32_t));
636 pt_size = ntohl (be_size);
637 pt = GNUNET_malloc_large (pt_size);
638 if (NULL == pt)
639 {
640 GNUNET_break_op (0);
641 r->csc (r->csc_cls,
642 ANASTASIS_RS_POLICY_DOWNLOAD_TOO_BIG,
643 NULL,
644 0);
645 ANASTASIS_recovery_abort (r);
646 GNUNET_free (plaintext);
647 return;
648 }
649 if (Z_OK !=
650 uncompress ((Bytef *) pt,
651 &pt_size,
652 (const Bytef *) plaintext + sizeof (uint32_t),
653 size_plaintext - sizeof (uint32_t)))
654 {
655 GNUNET_break_op (0);
656 r->csc (r->csc_cls,
657 ANASTASIS_RS_POLICY_DOWNLOAD_INVALID_COMPRESSION,
658 NULL,
659 0);
660 GNUNET_free (plaintext);
661 GNUNET_free (pt);
662 ANASTASIS_recovery_abort (r);
663 return;
664 }
665 GNUNET_free (plaintext);
666 recovery_document = json_loadb ((char *) pt,
667 pt_size,
668 JSON_DECODE_ANY,
669 &json_error);
670 GNUNET_free (pt);
671 if (NULL == recovery_document)
672 {
673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
674 "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
675 json_error.text,
676 json_error.line,
677 json_error.source,
678 json_error.position);
679 GNUNET_break_op (0);
680 r->csc (r->csc_cls,
681 ANASTASIS_RS_POLICY_DOWNLOAD_NO_JSON,
682 NULL,
683 0);
684 ANASTASIS_recovery_abort (r);
685 return;
686 }
687
688 {
689 const char *secret_name = NULL;
690 struct GNUNET_JSON_Specification spec[] = {
691 GNUNET_JSON_spec_json ("policies",
692 &dec_policies),
693 GNUNET_JSON_spec_json ("escrow_methods",
694 &esc_methods),
695 GNUNET_JSON_spec_mark_optional (
696 GNUNET_JSON_spec_string ("secret_name",
697 &secret_name)),
698 GNUNET_JSON_spec_varsize ("encrypted_core_secret",
699 &r->enc_core_secret,
700 &r->enc_core_secret_size),
701 GNUNET_JSON_spec_end ()
702 };
703
704 if (GNUNET_OK !=
705 GNUNET_JSON_parse (recovery_document,
706 spec,
707 NULL, NULL))
708 {
709 GNUNET_break_op (0);
710 json_dumpf (recovery_document,
711 stderr,
712 0);
713 json_decref (recovery_document);
714 r->csc (r->csc_cls,
715 ANASTASIS_RS_POLICY_MALFORMED_JSON,
716 NULL,
717 0);
718 ANASTASIS_recovery_abort (r);
719 return;
720 }
721 if (NULL != secret_name)
722 {
723 GNUNET_break (NULL == r->secret_name);
724 r->secret_name = GNUNET_strdup (secret_name);
725 r->ri.secret_name = r->secret_name;
726 }
727 }
728 json_decref (recovery_document);
729 }
730
731 r->ri.version = dd->version;
732 r->ri.cs_len = json_array_size (esc_methods);
733 r->ri.dps_len = json_array_size (dec_policies);
734 r->ri.dps = GNUNET_new_array (r->ri.dps_len,
735 struct ANASTASIS_DecryptionPolicy *);
736 r->dps = GNUNET_new_array (r->ri.dps_len,
737 struct DecryptionPolicy);
738 r->solved_challenges = GNUNET_new_array (r->ri.cs_len,
739 struct ANASTASIS_Challenge *);
740 r->ri.cs = GNUNET_new_array (r->ri.cs_len,
741 struct ANASTASIS_Challenge *);
742 r->cs = GNUNET_new_array (r->ri.cs_len,
743 struct ANASTASIS_Challenge);
744 for (unsigned int i = 0; i < r->ri.cs_len; i++)
745 {
746 struct ANASTASIS_Challenge *cs = &r->cs[i];
747 const char *instructions;
748 const char *url;
749 const char *escrow_type;
750 struct GNUNET_JSON_Specification spec[] = {
751 GNUNET_JSON_spec_fixed_auto ("uuid",
752 &cs->ci.uuid),
753 GNUNET_JSON_spec_string ("url",
754 &url),
755 GNUNET_JSON_spec_string ("instructions",
756 &instructions),
757 GNUNET_JSON_spec_fixed_auto ("truth_key",
758 &cs->truth_key),
759 GNUNET_JSON_spec_fixed_auto ("salt",
760 &cs->salt),
761 GNUNET_JSON_spec_fixed_auto ("provider_salt",
762 &cs->provider_salt),
763 GNUNET_JSON_spec_string ("escrow_type",
764 &escrow_type),
765 GNUNET_JSON_spec_end ()
766 };
767
768 r->ri.cs[i] = cs;
769 cs->recovery = r;
770 if (GNUNET_OK !=
771 GNUNET_JSON_parse (json_array_get (esc_methods,
772 i),
773 spec,
774 NULL, NULL))
775 {
776 GNUNET_break_op (0);
777 json_decref (esc_methods);
778 json_decref (dec_policies);
779 r->csc (r->csc_cls,
780 ANASTASIS_RS_POLICY_MALFORMED_JSON,
781 NULL,
782 0);
783 ANASTASIS_recovery_abort (r);
784 return;
785 }
786 cs->url = GNUNET_strdup (url);
787 cs->type = GNUNET_strdup (escrow_type);
788 cs->ci.type = cs->type;
789 cs->ci.provider_url = cs->url;
790 cs->instructions = GNUNET_strdup (instructions);
791 cs->ci.instructions = cs->instructions;
792 }
793 json_decref (esc_methods);
794
795 for (unsigned int j = 0; j < r->ri.dps_len; j++)
796 {
797 struct DecryptionPolicy *dp = &r->dps[j];
798 json_t *uuids = NULL;
799 json_t *uuid;
800 size_t n_index;
801 struct GNUNET_JSON_Specification spec[] = {
802 GNUNET_JSON_spec_fixed_auto ("master_key",
803 &dp->emk),
804 GNUNET_JSON_spec_fixed_auto ("salt",
805 &dp->salt),
806 GNUNET_JSON_spec_json ("uuids",
807 &uuids),
808 GNUNET_JSON_spec_end ()
809 };
810
811 r->ri.dps[j] = &r->dps[j].pub_details;
812 if ( (GNUNET_OK !=
813 GNUNET_JSON_parse (json_array_get (dec_policies,
814 j),
815 spec,
816 NULL, NULL)) ||
817 (! json_is_array (uuids)) )
818 {
819 GNUNET_break_op (0);
820 json_decref (uuids);
821 json_decref (dec_policies);
822 r->csc (r->csc_cls,
823 ANASTASIS_RS_POLICY_MALFORMED_JSON,
824 NULL,
825 0);
826 ANASTASIS_recovery_abort (r);
827 return;
828 }
829
830 dp->pub_details.challenges_length = json_array_size (uuids);
831 dp->pub_details.challenges
832 = GNUNET_new_array (dp->pub_details.challenges_length,
833 struct ANASTASIS_Challenge *);
834 json_array_foreach (uuids, n_index, uuid)
835 {
836 const char *uuid_str = json_string_value (uuid);
837 struct ANASTASIS_CRYPTO_TruthUUIDP uuid;
838 bool found = false;
839
840 if ( (NULL == uuid_str) ||
841 (GNUNET_OK !=
842 GNUNET_STRINGS_string_to_data (
843 uuid_str,
844 strlen (uuid_str),
845 &uuid,
846 sizeof (uuid))) )
847 {
848 GNUNET_break_op (0);
849 json_decref (dec_policies);
850 json_decref (uuids);
851 r->csc (r->csc_cls,
852 ANASTASIS_RS_POLICY_MALFORMED_JSON,
853 NULL,
854 0);
855 ANASTASIS_recovery_abort (r);
856 return;
857 }
858 for (unsigned int i = 0; i<r->ri.cs_len; i++)
859 {
860 if (0 !=
861 GNUNET_memcmp (&uuid,
862 &r->cs[i].ci.uuid))
863 continue;
864 found = true;
865 dp->pub_details.challenges[n_index] = &r->cs[i];
866 break;
867 }
868 if (! found)
869 {
870 GNUNET_break_op (0);
871 json_decref (dec_policies);
872 json_decref (uuids);
873 r->csc (r->csc_cls,
874 ANASTASIS_RS_POLICY_MALFORMED_JSON,
875 NULL,
876 0);
877 ANASTASIS_recovery_abort (r);
878 return;
879 }
880 }
881 json_decref (uuids);
882 }
883 json_decref (dec_policies);
884 r->pc (r->pc_cls,
885 &r->ri);
886}
887
888
889struct ANASTASIS_Recovery *
890ANASTASIS_recovery_begin (
891 struct GNUNET_CURL_Context *ctx,
892 const json_t *id_data,
893 unsigned int version,
894 const char *anastasis_provider_url,
895 const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
896 ANASTASIS_PolicyCallback pc,
897 void *pc_cls,
898 ANASTASIS_CoreSecretCallback csc,
899 void *csc_cls)
900{
901 struct ANASTASIS_Recovery *r;
902 struct ANASTASIS_CRYPTO_AccountPublicKeyP pub_key;
903
904 r = GNUNET_new (struct ANASTASIS_Recovery);
905 r->csc = csc;
906 r->csc_cls = csc_cls;
907 r->pc = pc;
908 r->pc_cls = pc_cls;
909 r->ctx = ctx;
910 r->id_data = json_incref ((json_t *) id_data);
911 r->provider_url = GNUNET_strdup (anastasis_provider_url);
912 ANASTASIS_CRYPTO_user_identifier_derive (id_data,
913 provider_salt,
914 &r->id);
915 ANASTASIS_CRYPTO_account_public_key_derive (&r->id,
916 &pub_key);
917 r->ri.version = version;
918 if (0 != version)
919 {
920 r->plo = ANASTASIS_policy_lookup_version (r->ctx,
921 anastasis_provider_url,
922 &pub_key,
923 &policy_lookup_cb,
924 r,
925 version);
926 }
927 else
928 {
929 r->plo = ANASTASIS_policy_lookup (r->ctx,
930 anastasis_provider_url,
931 &pub_key,
932 &policy_lookup_cb,
933 r);
934 }
935 if (NULL == r->plo)
936 {
937 GNUNET_break (0);
938 ANASTASIS_recovery_abort (r);
939 return NULL;
940 }
941 return r;
942}
943
944
945json_t *
946ANASTASIS_recovery_serialize (const struct ANASTASIS_Recovery *r)
947{
948 json_t *dps_arr;
949 json_t *cs_arr;
950
951 dps_arr = json_array ();
952 GNUNET_assert (NULL != dps_arr);
953 for (unsigned int i = 0; i<r->ri.dps_len; i++)
954 {
955 const struct DecryptionPolicy *dp = &r->dps[i];
956 json_t *c_arr;
957 json_t *dps;
958
959 c_arr = json_array ();
960 GNUNET_assert (NULL != c_arr);
961 for (unsigned int j = 0; j < dp->pub_details.challenges_length; j++)
962 {
963 const struct ANASTASIS_Challenge *c = dp->pub_details.challenges[j];
964 json_t *cs;
965
966 cs = json_pack ("{s:o}",
967 "uuid",
968 GNUNET_JSON_from_data_auto (&c->ci.uuid));
969 GNUNET_assert (NULL != cs);
970 GNUNET_assert (0 ==
971 json_array_append_new (c_arr,
972 cs));
973 }
974 dps = json_pack ("{s:o, s:o, s:o}",
975 "emk",
976 GNUNET_JSON_from_data_auto (&dp->emk),
977 "salt",
978 GNUNET_JSON_from_data_auto (&dp->salt),
979 "challenges",
980 c_arr);
981 GNUNET_assert (NULL != dps);
982 GNUNET_assert (0 ==
983 json_array_append_new (dps_arr,
984 dps));
985 }
986 cs_arr = json_array ();
987 GNUNET_assert (NULL != cs_arr);
988 for (unsigned int i = 0; i<r->ri.cs_len; i++)
989 {
990 const struct ANASTASIS_Challenge *c = &r->cs[i];
991 json_t *cs;
992
993 cs = json_pack ("{s:o,s:o,s:o,s:o,s:o?,"
994 " s:s,s:s,s:s,s:b}",
995 "uuid",
996 GNUNET_JSON_from_data_auto (&c->ci.uuid),
997 "truth_key",
998 GNUNET_JSON_from_data_auto (&c->truth_key),
999 "salt",
1000 GNUNET_JSON_from_data_auto (&c->salt),
1001 "provider_salt",
1002 GNUNET_JSON_from_data_auto (&c->provider_salt),
1003 "key_share",
1004 c->ci.solved
1005 ? GNUNET_JSON_from_data_auto (&c->key_share)
1006 : NULL,
1007 "url",
1008 c->url,
1009 "type",
1010 c->type,
1011 "instructions",
1012 c->instructions,
1013 "solved",
1014 c->ci.solved);
1015 GNUNET_assert (NULL != cs);
1016 GNUNET_assert (0 ==
1017 json_array_append_new (cs_arr,
1018 cs));
1019 }
1020
1021 return json_pack ("{s:o, s:o, s:o, s:I, s:O, "
1022 " s:s, s:s?, s:o}",
1023 "id",
1024 GNUNET_JSON_from_data_auto (&r->id),
1025 "dps",
1026 dps_arr,
1027 "cs",
1028 cs_arr,
1029 "version",
1030 (json_int_t) r->ri.version,
1031 "id_data",
1032 r->id_data,
1033 "provider_url",
1034 r->provider_url,
1035 "secret_name",
1036 r->secret_name,
1037 "core_secret",
1038 GNUNET_JSON_from_data (r->enc_core_secret,
1039 r->enc_core_secret_size));
1040}
1041
1042
1043/**
1044 * Parse the @a cs_array and update @a r accordingly
1045 *
1046 * @param[in,out] r recovery information to update
1047 * @param cs_arr serialized data to parse
1048 * @return #GNUNET_OK on success
1049 */
1050static int
1051parse_cs_array (struct ANASTASIS_Recovery *r,
1052 json_t *cs_arr)
1053{
1054 json_t *cs;
1055 unsigned int n_index;
1056
1057 if (! json_is_array (cs_arr))
1058 {
1059 GNUNET_break_op (0);
1060 return GNUNET_SYSERR;
1061 }
1062 r->ri.cs_len = json_array_size (cs_arr);
1063 r->solved_challenges = GNUNET_new_array (r->ri.cs_len,
1064 struct ANASTASIS_Challenge *);
1065 r->ri.cs = GNUNET_new_array (r->ri.cs_len,
1066 struct ANASTASIS_Challenge *);
1067 r->cs = GNUNET_new_array (r->ri.cs_len,
1068 struct ANASTASIS_Challenge);
1069 json_array_foreach (cs_arr, n_index, cs)
1070 {
1071 struct ANASTASIS_Challenge *c = &r->cs[n_index];
1072 const char *instructions;
1073 const char *url;
1074 const char *escrow_type;
1075 struct GNUNET_JSON_Specification spec[] = {
1076 GNUNET_JSON_spec_fixed_auto ("uuid",
1077 &c->ci.uuid),
1078 GNUNET_JSON_spec_string ("url",
1079 &url),
1080 GNUNET_JSON_spec_string ("instructions",
1081 &instructions),
1082 GNUNET_JSON_spec_fixed_auto ("truth_key",
1083 &c->truth_key),
1084 GNUNET_JSON_spec_fixed_auto ("salt",
1085 &c->salt),
1086 GNUNET_JSON_spec_fixed_auto ("provider_salt",
1087 &c->provider_salt),
1088 GNUNET_JSON_spec_string ("type",
1089 &escrow_type),
1090 GNUNET_JSON_spec_mark_optional (
1091 GNUNET_JSON_spec_fixed_auto ("key_share",
1092 &c->key_share)),
1093 GNUNET_JSON_spec_end ()
1094 };
1095
1096 r->ri.cs[n_index] = c;
1097 c->recovery = r;
1098 if (GNUNET_OK !=
1099 GNUNET_JSON_parse (cs,
1100 spec,
1101 NULL, NULL))
1102 {
1103 GNUNET_break_op (0);
1104 return GNUNET_SYSERR;
1105 }
1106 c->url = GNUNET_strdup (url);
1107 c->type = GNUNET_strdup (escrow_type);
1108 c->ci.type = c->type;
1109 c->instructions = GNUNET_strdup (instructions);
1110 c->ci.instructions = c->instructions;
1111 c->ci.provider_url = c->url;
1112 {
1113 json_t *ks;
1114
1115 ks = json_object_get (cs,
1116 "key_share");
1117 if ( (NULL != ks) &&
1118 (! json_is_null (ks)) )
1119 {
1120 c->ci.solved = true;
1121 r->solved_challenges[r->solved_challenge_pos++] = c;
1122 }
1123 }
1124 }
1125
1126 return GNUNET_OK;
1127}
1128
1129
1130/**
1131 * Parse the @a dps_array and update @a r accordingly
1132 *
1133 * @param[in,out] r recovery information to update
1134 * @param dps_arr serialized data to parse
1135 * @return #GNUNET_OK on success
1136 */
1137static int
1138parse_dps_array (struct ANASTASIS_Recovery *r,
1139 json_t *dps_arr)
1140{
1141 json_t *dps;
1142 unsigned int n_index;
1143
1144 if (! json_is_array (dps_arr))
1145 {
1146 GNUNET_break_op (0);
1147 return GNUNET_SYSERR;
1148 }
1149 r->ri.dps_len = json_array_size (dps_arr);
1150 r->dps = GNUNET_new_array (r->ri.dps_len,
1151 struct DecryptionPolicy);
1152 r->ri.dps = GNUNET_new_array (r->ri.dps_len,
1153 struct ANASTASIS_DecryptionPolicy *);
1154
1155 json_array_foreach (dps_arr, n_index, dps)
1156 {
1157 struct DecryptionPolicy *dp = &r->dps[n_index];
1158 json_t *challenges;
1159 struct GNUNET_JSON_Specification spec[] = {
1160 GNUNET_JSON_spec_fixed_auto ("emk",
1161 &dp->emk),
1162 GNUNET_JSON_spec_fixed_auto ("salt",
1163 &dp->salt),
1164 GNUNET_JSON_spec_json ("challenges",
1165 &challenges),
1166 GNUNET_JSON_spec_end ()
1167 };
1168 const char *err_json_name;
1169 unsigned int err_line;
1170
1171 r->ri.dps[n_index] = &dp->pub_details;
1172 if (GNUNET_OK !=
1173 GNUNET_JSON_parse (dps,
1174 spec,
1175 &err_json_name,
1176 &err_line))
1177 {
1178 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1179 "Failed to parse decryption policy JSON entry `%s'\n",
1180 err_json_name);
1181 json_dumpf (dps,
1182 stderr,
1183 JSON_INDENT (2));
1184 return GNUNET_SYSERR;
1185 }
1186 if (! json_is_array (challenges))
1187 {
1188 GNUNET_break_op (0);
1189 GNUNET_JSON_parse_free (spec);
1190 return GNUNET_SYSERR;
1191 }
1192 dp->pub_details.challenges_length = json_array_size (challenges);
1193 dp->pub_details.challenges = GNUNET_new_array (
1194 dp->pub_details.challenges_length,
1195 struct ANASTASIS_Challenge *);
1196
1197 {
1198 json_t *challenge;
1199 unsigned int c_index;
1200 json_array_foreach (challenges, c_index, challenge)
1201 {
1202 struct ANASTASIS_CRYPTO_TruthUUIDP uuid;
1203 struct GNUNET_JSON_Specification ispec[] = {
1204 GNUNET_JSON_spec_fixed_auto ("uuid",
1205 &uuid),
1206 GNUNET_JSON_spec_end ()
1207 };
1208 bool found = false;
1209
1210 if (GNUNET_OK !=
1211 GNUNET_JSON_parse (challenge,
1212 ispec,
1213 NULL, NULL))
1214 {
1215 GNUNET_break_op (0);
1216 GNUNET_JSON_parse_free (spec);
1217 return GNUNET_SYSERR;
1218 }
1219 for (unsigned int i = 0; i<r->ri.cs_len; i++)
1220 {
1221 if (0 !=
1222 GNUNET_memcmp (&uuid,
1223 &r->cs[i].ci.uuid))
1224 continue;
1225 dp->pub_details.challenges[c_index] = &r->cs[i];
1226 found = true;
1227 }
1228 if (! found)
1229 {
1230 GNUNET_break_op (0);
1231 GNUNET_JSON_parse_free (spec);
1232 return GNUNET_SYSERR;
1233 }
1234 }
1235 }
1236 GNUNET_JSON_parse_free (spec);
1237 }
1238 return GNUNET_OK;
1239}
1240
1241
1242/**
1243 * Asynchronously call "pc" on the recovery information.
1244 *
1245 * @param cls a `struct ANASTASIS_Recovery *`
1246 */
1247static void
1248run_async_pc (void *cls)
1249{
1250 struct ANASTASIS_Recovery *r = cls;
1251
1252 r->do_async = NULL;
1253 r->pc (r->pc_cls,
1254 &r->ri);
1255}
1256
1257
1258struct ANASTASIS_Recovery *
1259ANASTASIS_recovery_deserialize (struct GNUNET_CURL_Context *ctx,
1260 const json_t *input,
1261 ANASTASIS_PolicyCallback pc,
1262 void *pc_cls,
1263 ANASTASIS_CoreSecretCallback csc,
1264 void *csc_cls)
1265{
1266 struct ANASTASIS_Recovery *r;
1267
1268 r = GNUNET_new (struct ANASTASIS_Recovery);
1269 r->csc = csc;
1270 r->csc_cls = csc_cls;
1271 r->pc = pc;
1272 r->pc_cls = pc_cls;
1273 r->ctx = ctx;
1274 {
1275 const char *err_json_name;
1276 unsigned int err_line;
1277 uint32_t version;
1278 json_t *dps_arr;
1279 json_t *cs_arr;
1280 json_t *id_data;
1281 const char *provider_url;
1282 const char *secret_name;
1283 void *ecs;
1284 size_t ecs_size;
1285 struct GNUNET_JSON_Specification spec[] = {
1286 GNUNET_JSON_spec_fixed_auto ("id",
1287 &r->id),
1288 GNUNET_JSON_spec_string ("provider_url",
1289 &provider_url),
1290 GNUNET_JSON_spec_mark_optional (
1291 GNUNET_JSON_spec_string ("secret_name",
1292 &secret_name)),
1293 GNUNET_JSON_spec_uint32 ("version",
1294 &version),
1295 GNUNET_JSON_spec_json ("dps",
1296 &dps_arr),
1297 GNUNET_JSON_spec_json ("cs",
1298 &cs_arr),
1299 GNUNET_JSON_spec_json ("id_data",
1300 &id_data),
1301 GNUNET_JSON_spec_varsize ("core_secret",
1302 &ecs,
1303 &ecs_size),
1304 GNUNET_JSON_spec_end ()
1305 };
1306
1307 if (GNUNET_OK !=
1308 GNUNET_JSON_parse (input,
1309 spec,
1310 &err_json_name,
1311 &err_line))
1312 {
1313 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1314 "Failed to parse recovery document JSON entry `%s'\n",
1315 err_json_name);
1316 json_dumpf (input,
1317 stderr,
1318 JSON_INDENT (2));
1319 return NULL;
1320 }
1321 r->ri.version = version;
1322 if ( (GNUNET_OK !=
1323 parse_cs_array (r,
1324 cs_arr)) ||
1325 (GNUNET_OK !=
1326 parse_dps_array (r,
1327 dps_arr)) )
1328 {
1329 GNUNET_break_op (0);
1330 ANASTASIS_recovery_abort (r);
1331 GNUNET_JSON_parse_free (spec);
1332 return NULL;
1333 }
1334 r->id_data = json_incref (id_data);
1335 r->provider_url = GNUNET_strdup (provider_url);
1336 if (NULL != secret_name)
1337 r->secret_name = GNUNET_strdup (secret_name);
1338 r->ri.secret_name = r->secret_name;
1339 if (0 != ecs_size)
1340 {
1341 r->enc_core_secret = GNUNET_memdup (ecs,
1342 ecs_size);
1343 r->enc_core_secret_size = ecs_size;
1344 }
1345 GNUNET_JSON_parse_free (spec);
1346 }
1347 if (0 == r->ri.dps_len)
1348 {
1349 struct ANASTASIS_CRYPTO_AccountPublicKeyP pub_key;
1350
1351 ANASTASIS_CRYPTO_account_public_key_derive (&r->id,
1352 &pub_key);
1353 if (0 != r->ri.version)
1354 {
1355 r->plo = ANASTASIS_policy_lookup_version (r->ctx,
1356 r->provider_url,
1357 &pub_key,
1358 &policy_lookup_cb,
1359 r,
1360 r->ri.version);
1361 }
1362 else
1363 {
1364 r->plo = ANASTASIS_policy_lookup (r->ctx,
1365 r->provider_url,
1366 &pub_key,
1367 &policy_lookup_cb,
1368 r);
1369 }
1370 if (NULL == r->plo)
1371 {
1372 GNUNET_break (0);
1373 ANASTASIS_recovery_abort (r);
1374 return NULL;
1375 }
1376 }
1377 else
1378 {
1379 r->do_async = GNUNET_SCHEDULER_add_now (&run_async_pc,
1380 r);
1381 }
1382 return r;
1383}
1384
1385
1386void
1387ANASTASIS_recovery_abort (struct ANASTASIS_Recovery *r)
1388{
1389 if (NULL != r->do_async)
1390 {
1391 GNUNET_SCHEDULER_cancel (r->do_async);
1392 r->do_async = NULL;
1393 }
1394 if (NULL != r->plo)
1395 {
1396 ANASTASIS_policy_lookup_cancel (r->plo);
1397 r->plo = NULL;
1398 }
1399 GNUNET_free (r->solved_challenges);
1400 for (unsigned int j = 0; j < r->ri.dps_len; j++)
1401 GNUNET_free (r->dps[j].pub_details.challenges);
1402 GNUNET_free (r->ri.dps);
1403 for (unsigned int i = 0; i < r->ri.cs_len; i++)
1404 {
1405 struct ANASTASIS_Challenge *cs = r->ri.cs[i];
1406
1407 if (NULL != cs->kslo)
1408 {
1409 ANASTASIS_keyshare_lookup_cancel (cs->kslo);
1410 cs->kslo = NULL;
1411 }
1412 GNUNET_free (cs->url);
1413 GNUNET_free (cs->type);
1414 GNUNET_free (cs->instructions);
1415 GNUNET_free (cs->answer);
1416 }
1417 GNUNET_free (r->ri.cs);
1418 GNUNET_free (r->cs);
1419 GNUNET_free (r->dps);
1420 json_decref (r->id_data);
1421 GNUNET_free (r->provider_url);
1422 GNUNET_free (r->secret_name);
1423 GNUNET_free (r->enc_core_secret);
1424 GNUNET_free (r);
1425}