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