RECLAIM: Refactoring
[oweals/gnunet.git] / src / reclaim / gnunet-service-reclaim_tickets.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
4
5    GNUnet is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your option) any later version.
9
10    GNUnet is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18    SPDX-License-Identifier: AGPL3.0-or-later
19    */
20
21 /**
22  * @author Martin Schanzenbach
23  * @file src/reclaim/gnunet-service-reclaim_tickets.c
24  * @brief reclaim tickets
25  *
26  */
27 #include "gnunet-service-reclaim_tickets.h"
28
29 struct ParallelLookup;
30
31 struct RECLAIM_TICKETS_ConsumeHandle
32 {
33   /**
34    * Ticket
35    */
36   struct GNUNET_RECLAIM_Ticket ticket;
37
38   /**
39    * LookupRequest
40    */
41   struct GNUNET_GNS_LookupRequest *lookup_request;
42
43   /**
44    * Audience Key
45    */
46   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
47
48   /**
49    * Audience Key
50    */
51   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
52
53   /**
54    * Lookup DLL
55    */
56   struct ParallelLookup *parallel_lookups_head;
57
58   /**
59    * Lookup DLL
60    */
61   struct ParallelLookup *parallel_lookups_tail;
62
63   /**
64    * Kill task
65    */
66   struct GNUNET_SCHEDULER_Task *kill_task;
67
68   /**
69    * Attributes
70    */
71   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
72
73   /**
74    * Lookup time
75    */
76   struct GNUNET_TIME_Absolute lookup_start_time;
77
78   /**
79    * Callback
80    */
81   RECLAIM_TICKETS_ConsumeCallback cb;
82
83   /**
84    * Callbacl closure
85    */
86   void *cb_cls;
87
88 };
89
90 /**
91  * Handle for a parallel GNS lookup job
92  */
93 struct ParallelLookup
94 {
95   /* DLL */
96   struct ParallelLookup *next;
97
98   /* DLL */
99   struct ParallelLookup *prev;
100
101   /* The GNS request */
102   struct GNUNET_GNS_LookupRequest *lookup_request;
103
104   /* The handle the return to */
105   struct RECLAIM_TICKETS_ConsumeHandle *handle;
106
107   /**
108    * Lookup time
109    */
110   struct GNUNET_TIME_Absolute lookup_start_time;
111
112   /* The label to look up */
113   char *label;
114 };
115
116
117 /**
118  * A reference to a ticket stored in GNS
119  */
120 struct TicketReference
121 {
122   /**
123    * DLL
124    */
125   struct TicketReference *next;
126
127   /**
128    * DLL
129    */
130   struct TicketReference *prev;
131
132   /**
133    * Attributes
134    */
135   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
136
137   /**
138    * Tickets
139    */
140   struct GNUNET_RECLAIM_Ticket ticket;
141 };
142
143
144 /**
145  * Ticket issue request handle
146  */
147 struct TicketIssueHandle
148 {
149   /**
150    * Attributes to issue
151    */
152   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
153
154   /**
155    * Issuer Key
156    */
157   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
158
159   /**
160    * Ticket to issue
161    */
162   struct GNUNET_RECLAIM_Ticket ticket;
163
164   /**
165    * QueueEntry
166    */
167   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
168
169   /**
170    * Ticket reference list
171    */
172   struct TicketReference *ticket_refs_head;
173
174   /**
175    * Ticket reference list
176    */
177   struct TicketReference *ticket_refs_tail;
178
179   /**
180    * Number of references
181    */
182   uint32_t ticket_ref_num;
183
184   /**
185    * Callback
186    */
187   RECLAIM_TICKETS_TicketResult cb;
188
189   /**
190    * Callback cls
191    */
192   void *cb_cls;
193
194 };
195
196 /**
197  * Ticket iterator
198  */
199 struct RECLAIM_TICKETS_Iterator
200 {
201   /**
202    * Issuer Key
203    */
204   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
205
206   /**
207    * Issuer pubkey
208    */
209   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
210
211   /**
212    * Namestore queue entry
213    */
214   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
215
216   /**
217    * Iter callback
218    */
219   RECLAIM_TICKETS_TicketIter cb;
220
221   /**
222    * Iter cls
223    */
224   void *cb_cls;
225
226   /**
227    * Ticket reference list
228    */
229   struct TicketReference *tickets_head;
230
231   /**
232    * Ticket reference list
233    */
234   struct TicketReference *tickets_tail;
235 };
236
237 /* Namestore handle */
238 static struct GNUNET_NAMESTORE_Handle *nsh;
239
240 /* GNS handle */
241 static struct GNUNET_GNS_Handle *gns;
242
243 /* Handle to the statistics service */
244 static struct GNUNET_STATISTICS_Handle *stats;
245
246 static int
247 create_sym_key_from_ecdh (const struct GNUNET_HashCode *new_key_hash,
248                           struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
249                           struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
250 {
251   struct GNUNET_CRYPTO_HashAsciiEncoded new_key_hash_str;
252
253   GNUNET_CRYPTO_hash_to_enc (new_key_hash,
254                              &new_key_hash_str);
255   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating symmetric rsa key from %s\n", (char*)&new_key_hash_str);
256   static const char ctx_key[] = "gnuid-aes-ctx-key";
257   GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
258                      new_key_hash, sizeof (struct GNUNET_HashCode),
259                      ctx_key, strlen (ctx_key),
260                      NULL, 0);
261   static const char ctx_iv[] = "gnuid-aes-ctx-iv";
262   GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
263                      new_key_hash, sizeof (struct GNUNET_HashCode),
264                      ctx_iv, strlen (ctx_iv),
265                      NULL, 0);
266   return GNUNET_OK;
267 }
268
269
270 /**
271  * Cleanup ticket consume handle
272  * @param cth the handle to clean up
273  */
274 static void
275 cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth)
276 {
277   struct ParallelLookup *lu;
278   struct ParallelLookup *tmp;
279   if (NULL != cth->lookup_request)
280     GNUNET_GNS_lookup_cancel (cth->lookup_request);
281   for (lu = cth->parallel_lookups_head;
282        NULL != lu;) {
283     GNUNET_GNS_lookup_cancel (lu->lookup_request);
284     GNUNET_free (lu->label);
285     tmp = lu->next;
286     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
287                                  cth->parallel_lookups_tail,
288                                  lu);
289     GNUNET_free (lu);
290     lu = tmp;
291   }
292
293   if (NULL != cth->attrs)
294     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cth->attrs);
295   GNUNET_free (cth);
296 }
297
298
299 static void
300 process_parallel_lookup_result (void *cls,
301                           uint32_t rd_count,
302                           const struct GNUNET_GNSRECORD_Data *rd)
303 {
304   struct ParallelLookup *parallel_lookup = cls;
305   struct RECLAIM_TICKETS_ConsumeHandle *cth = parallel_lookup->handle;
306   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *attr_le;
307   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308               "Parallel lookup finished (count=%u)\n", rd_count);
309
310   GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
311                                cth->parallel_lookups_tail,
312                                parallel_lookup);
313   GNUNET_free (parallel_lookup->label);
314
315   GNUNET_STATISTICS_update (stats,
316                             "attribute_lookup_time_total",
317                             GNUNET_TIME_absolute_get_duration (parallel_lookup->lookup_start_time).rel_value_us,
318                             GNUNET_YES);
319   GNUNET_STATISTICS_update (stats,
320                             "attribute_lookups_count",
321                             1,
322                             GNUNET_YES);
323
324
325   GNUNET_free (parallel_lookup);
326   if (1 != rd_count)
327     GNUNET_break(0);//TODO
328   if (rd->record_type == GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR)
329   {
330     attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
331     attr_le->claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data,
332                                                            rd->data_size);
333     GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head,
334                                  cth->attrs->list_tail,
335                                  attr_le);
336   }
337   if (NULL != cth->parallel_lookups_head)
338     return; //Wait for more
339   /* Else we are done */
340
341   GNUNET_SCHEDULER_cancel (cth->kill_task);
342   cth->cb (cth->cb_cls,
343            &cth->ticket.identity,
344            cth->attrs,
345            GNUNET_OK,
346            NULL);
347   cleanup_cth (cth);
348 }
349
350
351 static void
352 abort_parallel_lookups (void *cls)
353 {
354   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
355   struct ParallelLookup *lu;
356   struct ParallelLookup *tmp;
357
358   cth->kill_task = NULL;
359   for (lu = cth->parallel_lookups_head;
360        NULL != lu;) {
361     GNUNET_GNS_lookup_cancel (lu->lookup_request);
362     GNUNET_free (lu->label);
363     tmp = lu->next;
364     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
365                                  cth->parallel_lookups_tail,
366                                  lu);
367     GNUNET_free (lu);
368     lu = tmp;
369   }
370   cth->cb (cth->cb_cls,
371            NULL,
372            NULL,
373            GNUNET_SYSERR,
374            "Aborted");
375
376 }
377
378
379
380 static void
381 lookup_authz_cb (void *cls,
382                  uint32_t rd_count,
383                  const struct GNUNET_GNSRECORD_Data *rd)
384 {
385   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
386   struct GNUNET_HashCode new_key_hash;
387   struct GNUNET_CRYPTO_SymmetricSessionKey enc_key;
388   struct GNUNET_CRYPTO_SymmetricInitializationVector enc_iv;
389   struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_key;
390   struct ParallelLookup *parallel_lookup;
391   size_t size;
392   char *buf;
393   char *attr_lbl;
394   char *lbls;
395
396   cth->lookup_request = NULL;
397   if (1 != rd_count)
398   {
399     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
400                 "Number of keys %d != 1.",
401                 rd_count);
402     cth->cb (cth->cb_cls,
403              NULL,
404              NULL,
405              GNUNET_SYSERR,
406              "Number of keys %d != 1.");
407     cleanup_cth (cth);
408     return;
409   }
410
411   //Decrypt
412   ecdh_key = (struct GNUNET_CRYPTO_EcdhePublicKey *)rd->data;
413
414   buf = GNUNET_malloc (rd->data_size
415                        - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
416
417   //Calculate symmetric key from ecdh parameters
418   GNUNET_assert (GNUNET_OK ==
419                  GNUNET_CRYPTO_ecdsa_ecdh (&cth->identity,
420                                            ecdh_key,
421                                            &new_key_hash));
422   create_sym_key_from_ecdh (&new_key_hash,
423                             &enc_key,
424                             &enc_iv);
425   size = GNUNET_CRYPTO_symmetric_decrypt (rd->data
426                                + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
427                                           rd->data_size
428                                - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
429                                           &enc_key,
430                                           &enc_iv,
431                                           buf);
432
433   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
434               "Decrypted bytes: %zd Expected bytes: %zd\n",
435               size,
436               rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
437   GNUNET_STATISTICS_update (stats,
438     "reclaim_authz_lookup_time_total",
439     GNUNET_TIME_absolute_get_duration (cth->lookup_start_time).rel_value_us,
440     GNUNET_YES);
441   GNUNET_STATISTICS_update (stats,
442                             "reclaim_authz_lookups_count",
443                             1,
444                             GNUNET_YES);
445   lbls = GNUNET_strdup (buf);
446   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
447               "Attributes found %s\n", lbls);
448
449   for (attr_lbl = strtok (lbls, ",");
450        NULL != attr_lbl;
451        attr_lbl = strtok (NULL, ","))
452   {
453     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454                 "Looking up %s\n", attr_lbl);
455     parallel_lookup = GNUNET_new (struct ParallelLookup);
456     parallel_lookup->handle = cth;
457     parallel_lookup->label = GNUNET_strdup (attr_lbl);
458     parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get();
459     parallel_lookup->lookup_request
460       = GNUNET_GNS_lookup (gns,
461                            attr_lbl,
462                            &cth->ticket.identity,
463                            GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR,
464                            GNUNET_GNS_LO_DEFAULT,
465                            &process_parallel_lookup_result,
466                            parallel_lookup);
467     GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head,
468                                  cth->parallel_lookups_tail,
469                                  parallel_lookup);
470   }
471   GNUNET_free (lbls);
472   GNUNET_free (buf);
473   cth->kill_task = GNUNET_SCHEDULER_add_delayed (
474                     GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES,3),
475                     &abort_parallel_lookups,
476                     cth);
477 }
478
479
480
481 struct RECLAIM_TICKETS_ConsumeHandle*
482 RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id,
483                                const struct GNUNET_RECLAIM_Ticket *ticket,
484                                RECLAIM_TICKETS_ConsumeCallback cb,
485                                void* cb_cls)
486 {
487   struct RECLAIM_TICKETS_ConsumeHandle *cth;
488   char *label;
489   cth = GNUNET_new (struct RECLAIM_TICKETS_ConsumeHandle);
490
491   cth->identity = *id;
492   GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity,
493                                       &cth->identity_pub);
494   cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
495   cth->ticket = *ticket;
496   cth->cb = cb;
497   cth->cb_cls = cb_cls;
498   label = GNUNET_STRINGS_data_to_string_alloc (&cth->ticket.rnd,
499                                                sizeof (uint64_t));
500   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
501               "Looking for AuthZ info under %s\n", label);
502   cth->lookup_start_time = GNUNET_TIME_absolute_get ();
503   cth->lookup_request = GNUNET_GNS_lookup (gns,
504                                            label,
505                                            &cth->ticket.identity,
506                                            GNUNET_GNSRECORD_TYPE_RECLAIM_AUTHZ,
507                                            GNUNET_GNS_LO_DEFAULT,
508                                            &lookup_authz_cb,
509                                            cth);
510   GNUNET_free (label);
511   return cth;
512 }
513
514 void
515 RECLAIM_TICKETS_consume_cancel (struct RECLAIM_TICKETS_ConsumeHandle *cth)
516 {
517   cleanup_cth (cth);
518   return;
519 }
520
521
522 /*******************************
523  * Ticket issue
524  *******************************/
525
526 /**
527  * Cleanup ticket consume handle
528  * @param handle the handle to clean up
529  */
530 static void
531 cleanup_issue_handle (struct TicketIssueHandle *handle)
532 {
533   struct TicketReference *tr;
534   struct TicketReference *tr_tmp;
535   if (NULL != handle->attrs)
536     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (handle->attrs);
537   if (NULL != handle->ns_qe)
538     GNUNET_NAMESTORE_cancel (handle->ns_qe);
539   for (tr = handle->ticket_refs_head; NULL != tr;)
540   {
541     if (NULL != tr->attrs)
542       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
543     tr_tmp = tr;
544     tr = tr->next;
545     GNUNET_free (tr_tmp);
546   }
547   GNUNET_free (handle);
548 }
549
550
551
552 static void
553 store_ticket_refs_cont (void *cls,
554                         int32_t success,
555                         const char *emsg)
556 {
557   struct TicketIssueHandle *handle = cls;
558   handle->ns_qe = NULL;
559   if (GNUNET_OK != success)
560   {
561     handle->cb (handle->cb_cls,
562                 NULL,
563                 GNUNET_SYSERR,
564                 "Error storing updated ticket refs in GNS");
565     cleanup_issue_handle (handle);
566     return;
567   }
568   handle->cb (handle->cb_cls,
569               &handle->ticket,
570               GNUNET_OK,
571               NULL);
572   cleanup_issue_handle (handle);
573 }
574
575
576
577 static void
578 update_ticket_refs (void* cls)
579 {
580   struct TicketIssueHandle *handle = cls;
581   struct GNUNET_GNSRECORD_Data refs_rd[handle->ticket_ref_num];
582   struct TicketReference *tr;
583   char* buf;
584   size_t buf_size;
585
586   tr = handle->ticket_refs_head;
587   for (int i = 0; i < handle->ticket_ref_num; i++)
588   {
589     buf_size = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (tr->attrs);
590     buf_size += sizeof (struct GNUNET_RECLAIM_Ticket);
591     buf = GNUNET_malloc (buf_size);
592     memcpy (buf, &tr->ticket, sizeof (struct GNUNET_RECLAIM_Ticket));
593     GNUNET_RECLAIM_ATTRIBUTE_list_serialize (tr->attrs,
594                                              buf + sizeof (struct GNUNET_RECLAIM_Ticket));
595     refs_rd[i].data = buf;
596     refs_rd[i].data_size = buf_size;
597     refs_rd[i].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
598     refs_rd[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF;
599     refs_rd[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION |
600       GNUNET_GNSRECORD_RF_PRIVATE;
601     tr = tr->next;
602   }
603
604   handle->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
605                                                   &handle->identity,
606                                                   GNUNET_GNS_EMPTY_LABEL_AT,
607                                                   handle->ticket_ref_num,
608                                                   refs_rd,
609                                                   &store_ticket_refs_cont,
610                                                   handle);
611   for (int i = 0; i < handle->ticket_ref_num; i++)
612     GNUNET_free ((char*)refs_rd[i].data);
613 }
614
615
616
617 static void
618 ticket_lookup_cb (void *cls,
619                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
620                   const char *label,
621                   unsigned int rd_count,
622                   const struct GNUNET_GNSRECORD_Data *rd)
623 {
624   struct TicketIssueHandle *handle = cls;
625   struct TicketReference *tr;
626   const char* attr_data;
627   size_t attr_data_len;
628   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629               "Received tickets from local namestore.\n");
630   handle->ns_qe = NULL;
631   for (int i = 0; i < rd_count; i++)
632   {
633     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
634       continue;
635     tr = GNUNET_new (struct TicketReference);
636     memcpy (&tr->ticket, rd[i].data,
637             sizeof (struct GNUNET_RECLAIM_Ticket));
638     if (0 != memcmp (&tr->ticket.identity,
639                      &handle->ticket.identity,
640                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
641     {
642       //Not our ticket
643       GNUNET_free (tr);
644       continue;
645     }
646     attr_data = rd[i].data + sizeof (struct GNUNET_RECLAIM_Ticket);
647     attr_data_len = rd[i].data_size - sizeof (struct GNUNET_RECLAIM_Ticket);
648     tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attr_data,
649                                                            attr_data_len);
650     GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
651                                  handle->ticket_refs_tail,
652                                  tr);
653     handle->ticket_ref_num++;
654   }
655   tr = GNUNET_new (struct TicketReference);
656   tr->ticket = handle->ticket;
657   tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (handle->attrs);
658   GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
659                                handle->ticket_refs_tail,
660                                tr);
661   handle->ticket_ref_num++;
662   GNUNET_SCHEDULER_add_now (&update_ticket_refs, handle);
663 }
664
665 static void
666 ticket_lookup_error_cb (void *cls)
667 {
668   struct TicketIssueHandle *handle = cls;
669   handle->ns_qe = NULL;
670   handle->cb (handle->cb_cls,
671               &handle->ticket,
672               GNUNET_SYSERR,
673               "Error checking for ticketsin GNS\n");
674   cleanup_issue_handle (handle);
675 }
676
677 static void
678 store_ticket_issue_cont (void *cls,
679                          int32_t success,
680                          const char *emsg)
681 {
682   struct TicketIssueHandle *handle = cls;
683
684   handle->ns_qe = NULL;
685   if (GNUNET_SYSERR == success)
686   {
687     handle->cb (handle->cb_cls,
688                 &handle->ticket,
689                 GNUNET_SYSERR,
690                 "Error storing AuthZ ticket in GNS");
691     return;
692   }
693   /* First, local references to tickets */
694   handle->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
695                                                    &handle->identity,
696                                                    GNUNET_GNS_EMPTY_LABEL_AT,
697                                                    &ticket_lookup_error_cb,
698                                                    handle,
699                                                    &ticket_lookup_cb,
700                                                    handle);
701 }
702
703
704 static int
705 serialize_authz_record (const struct GNUNET_RECLAIM_Ticket *ticket,
706                         const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
707                         struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey,
708                         char **result)
709 {
710   struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey;
711   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
712   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
713   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
714   struct GNUNET_HashCode new_key_hash;
715   ssize_t enc_size;
716   char *enc_keyinfo;
717   char *buf;
718   char *write_ptr;
719   char attrs_str_len;
720   char* label;
721
722   GNUNET_assert (NULL != attrs->list_head);
723   attrs_str_len = 0;
724   for (le = attrs->list_head; NULL != le; le = le->next) {
725     attrs_str_len += 15 + 1; //TODO propery calculate
726   }
727   buf = GNUNET_malloc (attrs_str_len);
728   write_ptr = buf;
729   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730               "Writing attributes\n");
731   for (le = attrs->list_head; NULL != le; le = le->next) {
732     label = GNUNET_STRINGS_data_to_string_alloc (&le->claim->id,
733                                                  sizeof (uint64_t));
734     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
735                 "Adding attribute to record: %s\n", label);
736
737     GNUNET_memcpy (write_ptr,
738                    label,
739                    strlen (label));
740     write_ptr[strlen (label)] = ',';
741     write_ptr += strlen (label) + 1;
742     GNUNET_free (label);
743   }
744   write_ptr--;
745   write_ptr[0] = '\0'; //replace last , with a 0-terminator
746   // ECDH keypair E = eG
747   *ecdh_privkey = GNUNET_CRYPTO_ecdhe_key_create();
748   GNUNET_CRYPTO_ecdhe_key_get_public (*ecdh_privkey,
749                                       &ecdh_pubkey);
750   enc_keyinfo = GNUNET_malloc (attrs_str_len);
751   // Derived key K = H(eB)
752   GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (*ecdh_privkey,
753                                                         &ticket->audience,
754                                                         &new_key_hash));
755   create_sym_key_from_ecdh (&new_key_hash, &skey, &iv);
756   enc_size = GNUNET_CRYPTO_symmetric_encrypt (buf,
757                                               attrs_str_len,
758                                               &skey, &iv,
759                                               enc_keyinfo);
760   *result = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+
761                            enc_size);
762   GNUNET_memcpy (*result,
763                  &ecdh_pubkey,
764                  sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
765   GNUNET_memcpy (*result + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
766                  enc_keyinfo,
767                  enc_size);
768   GNUNET_free (enc_keyinfo);
769   GNUNET_free (buf);
770   return sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+enc_size;
771 }
772
773
774
775 static void
776 issue_ticket (struct TicketIssueHandle *ih)
777 {
778   struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
779   struct GNUNET_GNSRECORD_Data code_record[1];
780   char *authz_record_data;
781   size_t authz_record_len;
782   char *label;
783
784   //TODO rename function
785   authz_record_len = serialize_authz_record (&ih->ticket,
786                                              ih->attrs,
787                                              &ecdhe_privkey,
788                                              &authz_record_data);
789   code_record[0].data = authz_record_data;
790   code_record[0].data_size = authz_record_len;
791   code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
792   code_record[0].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_AUTHZ;
793   code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
794
795   label = GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd,
796                                                sizeof (uint64_t));
797   //Publish record
798   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
799                                               &ih->identity,
800                                               label,
801                                               1,
802                                               code_record,
803                                               &store_ticket_issue_cont,
804                                               ih);
805   GNUNET_free (ecdhe_privkey);
806   GNUNET_free (label);
807   GNUNET_free (authz_record_data);
808 }
809
810
811
812
813 void
814 RECLAIM_TICKETS_issue (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
815                        const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
816                        const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
817                        RECLAIM_TICKETS_TicketResult cb,
818                        void* cb_cls)
819 {
820   struct TicketIssueHandle *tih;
821   tih = GNUNET_new (struct TicketIssueHandle);
822   tih->cb = cb;
823   tih->cb_cls = cb_cls;
824   tih->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (attrs);
825   tih->identity = *identity;
826   GNUNET_CRYPTO_ecdsa_key_get_public (identity,
827                                       &tih->ticket.identity);
828   tih->ticket.rnd =
829     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
830                               UINT64_MAX);
831   tih->ticket.audience = *audience;
832   issue_ticket (tih);
833 }
834
835 /************************************
836  * Ticket iteration
837  ************************************/
838
839 static void
840 cleanup_iter (struct RECLAIM_TICKETS_Iterator *iter)
841 {
842   struct TicketReference *tr;
843   struct TicketReference *tr_tmp;
844   if (NULL != iter->ns_qe)
845     GNUNET_NAMESTORE_cancel (iter->ns_qe);
846   for (tr = iter->tickets_head; NULL != tr;)
847   {
848     if (NULL != tr->attrs)
849       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
850     tr_tmp = tr;
851     tr = tr->next;
852     GNUNET_free (tr_tmp);
853   }
854   GNUNET_free (iter);
855 }
856
857 static void
858 do_cleanup_iter (void* cls)
859 {
860   struct RECLAIM_TICKETS_Iterator *iter = cls;
861   cleanup_iter (iter);
862 }
863
864 /**
865  * Perform ticket iteration step
866  *
867  * @param ti ticket iterator to process
868  */
869 static void
870 run_ticket_iteration_round (struct RECLAIM_TICKETS_Iterator *iter)
871 {
872   struct TicketReference *tr;
873   if (NULL == iter->tickets_head)
874   {
875     //No more tickets
876     iter->cb (iter->cb_cls,
877               NULL);
878     GNUNET_SCHEDULER_add_now (&do_cleanup_iter, iter);
879     return;
880   }
881   tr = iter->tickets_head;
882   GNUNET_CONTAINER_DLL_remove (iter->tickets_head,
883                                iter->tickets_tail,
884                                tr);
885   iter->cb (iter->cb_cls,
886             &tr->ticket);
887   if (NULL != tr->attrs)
888     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
889   GNUNET_free (tr);
890 }
891
892 static void
893 collect_tickets_cb (void *cls,
894                     const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
895                     const char *label,
896                     unsigned int rd_count,
897                     const struct GNUNET_GNSRECORD_Data *rd)
898 {
899   struct RECLAIM_TICKETS_Iterator *iter = cls;
900   struct TicketReference *tr;
901   size_t attr_data_len;
902   const char* attr_data;
903   iter->ns_qe = NULL;
904
905   for (int i = 0; i < rd_count; i++)
906   {
907     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
908       continue;
909     tr = GNUNET_new (struct TicketReference);
910     memcpy (&tr->ticket, rd[i].data,
911             sizeof (struct GNUNET_RECLAIM_Ticket));
912     if (0 != memcmp (&tr->ticket.identity,
913                      &iter->identity_pub,
914                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
915     {
916       //Not our ticket
917       GNUNET_free (tr);
918       continue;
919     }
920     attr_data = rd[i].data + sizeof (struct GNUNET_RECLAIM_Ticket);
921     attr_data_len = rd[i].data_size - sizeof (struct GNUNET_RECLAIM_Ticket);
922     tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attr_data,
923                                                            attr_data_len);
924     GNUNET_CONTAINER_DLL_insert (iter->tickets_head,
925                                  iter->tickets_tail,
926                                  tr);
927   }
928   run_ticket_iteration_round (iter);
929 }
930
931 static void
932 collect_tickets_error_cb (void *cls)
933 {
934   struct RECLAIM_TICKETS_Iterator *iter = cls;
935   iter->ns_qe = NULL;
936   iter->cb (iter->cb_cls,
937             NULL);
938   cleanup_iter (iter);
939 }
940
941 void
942 RECLAIM_TICKETS_iteration_next (struct RECLAIM_TICKETS_Iterator *iter)
943 {
944   run_ticket_iteration_round (iter);
945 }
946
947 void
948 RECLAIM_TICKETS_iteration_stop (struct RECLAIM_TICKETS_Iterator *iter)
949 {
950   cleanup_iter (iter);
951 }
952
953 struct RECLAIM_TICKETS_Iterator*
954 RECLAIM_TICKETS_iteration_start (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
955                                  RECLAIM_TICKETS_TicketIter cb,
956                                  void* cb_cls)
957 {
958   struct RECLAIM_TICKETS_Iterator *iter;
959
960   iter = GNUNET_new (struct RECLAIM_TICKETS_Iterator);
961   iter->identity = *identity;
962   GNUNET_CRYPTO_ecdsa_key_get_public (identity,
963                                       &iter->identity_pub);
964   iter->cb = cb;
965   iter->cb_cls = cb_cls;
966   iter->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
967                                                  identity,
968                                                  GNUNET_GNS_EMPTY_LABEL_AT,
969                                                  &collect_tickets_error_cb,
970                                                  iter,
971                                                  &collect_tickets_cb,
972                                                  iter);
973   return iter;
974 }
975
976
977
978
979 int
980 RECLAIM_TICKETS_init (const struct GNUNET_CONFIGURATION_Handle *c)
981 {
982   //Connect to identity and namestore services
983   nsh = GNUNET_NAMESTORE_connect (c);
984   if (NULL == nsh)
985   {
986     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
987                          "error connecting to namestore");
988     return GNUNET_SYSERR;
989   }
990   gns = GNUNET_GNS_connect (c);
991   if (NULL == gns)
992   {
993     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
994                          "error connecting to gns");
995     return GNUNET_SYSERR;
996   }
997   stats = GNUNET_STATISTICS_create ("reclaim", c);
998   return GNUNET_OK;
999 }
1000
1001 void
1002 RECLAIM_TICKETS_deinit (void)
1003 {
1004   if (NULL != nsh)
1005     GNUNET_NAMESTORE_disconnect (nsh);
1006   nsh = NULL;
1007   if (NULL != gns)
1008     GNUNET_GNS_disconnect (gns);
1009   gns = NULL;
1010   if (NULL != stats)
1011   {
1012     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1013     stats = NULL;
1014   }
1015 }