RECLAIM: Towards -sql
[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 /**
30  * A reference to a ticket stored in GNS
31  */
32 struct TicketReference
33 {
34   /**
35    * DLL
36    */
37   struct TicketReference *next;
38
39   /**
40    * DLL
41    */
42   struct TicketReference *prev;
43
44   /**
45    * Attributes
46    */
47   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
48
49   /**
50    * Tickets
51    */
52   struct GNUNET_RECLAIM_Ticket ticket;
53 };
54
55
56 /**
57  * Ticket issue request handle
58  */
59 struct TicketIssueHandle
60 {
61   /**
62    * Attributes to issue
63    */
64   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
65
66   /**
67    * Issuer Key
68    */
69   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
70
71   /**
72    * Ticket to issue
73    */
74   struct GNUNET_RECLAIM_Ticket ticket;
75
76   /**
77    * QueueEntry
78    */
79   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
80
81   /**
82    * Ticket reference list
83    */
84   struct TicketReference *ticket_refs_head;
85
86   /**
87    * Ticket reference list
88    */
89   struct TicketReference *ticket_refs_tail;
90
91   /**
92    * Number of references
93    */
94   uint32_t ticket_ref_num;
95
96   /**
97    * Callback
98    */
99   RECLAIM_TICKETS_TicketResult cb;
100
101   /**
102    * Callback cls
103    */
104   void *cb_cls;
105
106 };
107
108 /**
109  * Ticket iterator
110  */
111 struct RECLAIM_TICKETS_Iterator
112 {
113   /**
114    * Issuer Key
115    */
116   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
117
118   /**
119    * Issuer pubkey
120    */
121   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
122
123   /**
124    * Namestore queue entry
125    */
126   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
127
128   /**
129    * Iter callback
130    */
131   RECLAIM_TICKETS_TicketIter cb;
132
133   /**
134    * Iter cls
135    */
136   void *cb_cls;
137
138   /**
139    * Ticket reference list
140    */
141   struct TicketReference *tickets_head;
142
143   /**
144    * Ticket reference list
145    */
146   struct TicketReference *tickets_tail;
147 };
148
149 static struct GNUNET_NAMESTORE_Handle *nsh;
150
151 /**
152  * Cleanup ticket consume handle
153  * @param handle the handle to clean up
154  */
155 static void
156 cleanup_issue_handle (struct TicketIssueHandle *handle)
157 {
158   struct TicketReference *tr;
159   struct TicketReference *tr_tmp;
160   if (NULL != handle->attrs)
161     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (handle->attrs);
162   if (NULL != handle->ns_qe)
163     GNUNET_NAMESTORE_cancel (handle->ns_qe);
164   for (tr = handle->ticket_refs_head; NULL != tr;)
165   {
166     if (NULL != tr->attrs)
167       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
168     tr_tmp = tr;
169     tr = tr->next;
170     GNUNET_free (tr_tmp);
171   }
172   GNUNET_free (handle);
173 }
174
175
176
177 static void
178 store_ticket_refs_cont (void *cls,
179                         int32_t success,
180                         const char *emsg)
181 {
182   struct TicketIssueHandle *handle = cls;
183   handle->ns_qe = NULL;
184   if (GNUNET_OK != success)
185   {
186     handle->cb (handle->cb_cls,
187                 NULL,
188                 GNUNET_SYSERR,
189                 "Error storing updated ticket refs in GNS");
190     cleanup_issue_handle (handle);
191     return;
192   }
193   handle->cb (handle->cb_cls,
194               &handle->ticket,
195               GNUNET_OK,
196               NULL);
197   cleanup_issue_handle (handle);
198 }
199
200
201
202 static void
203 update_ticket_refs (void* cls)
204 {
205   struct TicketIssueHandle *handle = cls;
206   struct GNUNET_GNSRECORD_Data refs_rd[handle->ticket_ref_num];
207   struct TicketReference *tr;
208   char* buf;
209   size_t buf_size;
210
211   tr = handle->ticket_refs_head;
212   for (int i = 0; i < handle->ticket_ref_num; i++)
213   {
214     buf_size = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (tr->attrs);
215     buf_size += sizeof (struct GNUNET_RECLAIM_Ticket);
216     buf = GNUNET_malloc (buf_size);
217     memcpy (buf, &tr->ticket, sizeof (struct GNUNET_RECLAIM_Ticket));
218     GNUNET_RECLAIM_ATTRIBUTE_list_serialize (tr->attrs,
219                                              buf + sizeof (struct GNUNET_RECLAIM_Ticket));
220     refs_rd[i].data = buf;
221     refs_rd[i].data_size = buf_size;
222     refs_rd[i].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
223     refs_rd[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF;
224     refs_rd[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION |
225       GNUNET_GNSRECORD_RF_PRIVATE;
226     tr = tr->next;
227   }
228
229   handle->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
230                                                   &handle->identity,
231                                                   GNUNET_GNS_EMPTY_LABEL_AT,
232                                                   handle->ticket_ref_num,
233                                                   refs_rd,
234                                                   &store_ticket_refs_cont,
235                                                   handle);
236   for (int i = 0; i < handle->ticket_ref_num; i++)
237     GNUNET_free ((char*)refs_rd[i].data);
238 }
239
240
241
242 static void
243 ticket_lookup_cb (void *cls,
244                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
245                   const char *label,
246                   unsigned int rd_count,
247                   const struct GNUNET_GNSRECORD_Data *rd)
248 {
249   struct TicketIssueHandle *handle = cls;
250   struct TicketReference *tr;
251   const char* attr_data;
252   size_t attr_data_len;
253   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
254               "Received tickets from local namestore.\n");
255   handle->ns_qe = NULL;
256   for (int i = 0; i < rd_count; i++)
257   {
258     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
259       continue;
260     tr = GNUNET_new (struct TicketReference);
261     memcpy (&tr->ticket, rd[i].data,
262             sizeof (struct GNUNET_RECLAIM_Ticket));
263     if (0 != memcmp (&tr->ticket.identity,
264                      &handle->ticket.identity,
265                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
266     {
267       //Not our ticket
268       GNUNET_free (tr);
269       continue;
270     }
271     attr_data = rd[i].data + sizeof (struct GNUNET_RECLAIM_Ticket);
272     attr_data_len = rd[i].data_size - sizeof (struct GNUNET_RECLAIM_Ticket);
273     tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attr_data,
274                                                            attr_data_len);
275     GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
276                                  handle->ticket_refs_tail,
277                                  tr);
278     handle->ticket_ref_num++;
279   }
280   tr = GNUNET_new (struct TicketReference);
281   tr->ticket = handle->ticket;
282   tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (handle->attrs);
283   GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
284                                handle->ticket_refs_tail,
285                                tr);
286   handle->ticket_ref_num++;
287   GNUNET_SCHEDULER_add_now (&update_ticket_refs, handle);
288 }
289
290 static void
291 ticket_lookup_error_cb (void *cls)
292 {
293   struct TicketIssueHandle *handle = cls;
294   handle->ns_qe = NULL;
295   handle->cb (handle->cb_cls,
296               &handle->ticket,
297               GNUNET_SYSERR,
298               "Error checking for ticketsin GNS\n");
299   cleanup_issue_handle (handle);
300 }
301
302 static void
303 store_ticket_issue_cont (void *cls,
304                          int32_t success,
305                          const char *emsg)
306 {
307   struct TicketIssueHandle *handle = cls;
308
309   handle->ns_qe = NULL;
310   if (GNUNET_SYSERR == success)
311   {
312     handle->cb (handle->cb_cls,
313                 &handle->ticket,
314                 GNUNET_SYSERR,
315                 "Error storing AuthZ ticket in GNS");
316     return;
317   }
318   /* First, local references to tickets */
319   handle->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
320                                                    &handle->identity,
321                                                    GNUNET_GNS_EMPTY_LABEL_AT,
322                                                    &ticket_lookup_error_cb,
323                                                    handle,
324                                                    &ticket_lookup_cb,
325                                                    handle);
326 }
327
328 static int
329 create_sym_key_from_ecdh (const struct GNUNET_HashCode *new_key_hash,
330                           struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
331                           struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
332 {
333   struct GNUNET_CRYPTO_HashAsciiEncoded new_key_hash_str;
334
335   GNUNET_CRYPTO_hash_to_enc (new_key_hash,
336                              &new_key_hash_str);
337   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating symmetric rsa key from %s\n", (char*)&new_key_hash_str);
338   static const char ctx_key[] = "gnuid-aes-ctx-key";
339   GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
340                      new_key_hash, sizeof (struct GNUNET_HashCode),
341                      ctx_key, strlen (ctx_key),
342                      NULL, 0);
343   static const char ctx_iv[] = "gnuid-aes-ctx-iv";
344   GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
345                      new_key_hash, sizeof (struct GNUNET_HashCode),
346                      ctx_iv, strlen (ctx_iv),
347                      NULL, 0);
348   return GNUNET_OK;
349 }
350
351
352 static int
353 serialize_authz_record (const struct GNUNET_RECLAIM_Ticket *ticket,
354                         const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
355                         struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey,
356                         char **result)
357 {
358   struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey;
359   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
360   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
361   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
362   struct GNUNET_HashCode new_key_hash;
363   ssize_t enc_size;
364   char *enc_keyinfo;
365   char *buf;
366   char *write_ptr;
367   char attrs_str_len;
368   char* label;
369
370   GNUNET_assert (NULL != attrs->list_head);
371   attrs_str_len = 0;
372   for (le = attrs->list_head; NULL != le; le = le->next) {
373     attrs_str_len += 15 + 1; //TODO propery calculate
374   }
375   buf = GNUNET_malloc (attrs_str_len);
376   write_ptr = buf;
377   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
378               "Writing attributes\n");
379   for (le = attrs->list_head; NULL != le; le = le->next) {
380     label = GNUNET_STRINGS_data_to_string_alloc (&le->claim->id,
381                                                  sizeof (uint64_t));
382     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383                 "Adding attribute to record: %s\n", label);
384
385     GNUNET_memcpy (write_ptr,
386                    label,
387                    strlen (label));
388     write_ptr[strlen (label)] = ',';
389     write_ptr += strlen (label) + 1;
390     GNUNET_free (label);
391   }
392   write_ptr--;
393   write_ptr[0] = '\0'; //replace last , with a 0-terminator
394   // ECDH keypair E = eG
395   *ecdh_privkey = GNUNET_CRYPTO_ecdhe_key_create();
396   GNUNET_CRYPTO_ecdhe_key_get_public (*ecdh_privkey,
397                                       &ecdh_pubkey);
398   enc_keyinfo = GNUNET_malloc (attrs_str_len);
399   // Derived key K = H(eB)
400   GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (*ecdh_privkey,
401                                                         &ticket->audience,
402                                                         &new_key_hash));
403   create_sym_key_from_ecdh (&new_key_hash, &skey, &iv);
404   enc_size = GNUNET_CRYPTO_symmetric_encrypt (buf,
405                                               attrs_str_len,
406                                               &skey, &iv,
407                                               enc_keyinfo);
408   *result = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+
409                            enc_size);
410   GNUNET_memcpy (*result,
411                  &ecdh_pubkey,
412                  sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
413   GNUNET_memcpy (*result + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
414                  enc_keyinfo,
415                  enc_size);
416   GNUNET_free (enc_keyinfo);
417   GNUNET_free (buf);
418   return sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+enc_size;
419 }
420
421
422
423 static void
424 issue_ticket (struct TicketIssueHandle *ih)
425 {
426   struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
427   struct GNUNET_GNSRECORD_Data code_record[1];
428   char *authz_record_data;
429   size_t authz_record_len;
430   char *label;
431
432   //TODO rename function
433   authz_record_len = serialize_authz_record (&ih->ticket,
434                                              ih->attrs,
435                                              &ecdhe_privkey,
436                                              &authz_record_data);
437   code_record[0].data = authz_record_data;
438   code_record[0].data_size = authz_record_len;
439   code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
440   code_record[0].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_AUTHZ;
441   code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
442
443   label = GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd,
444                                                sizeof (uint64_t));
445   //Publish record
446   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
447                                               &ih->identity,
448                                               label,
449                                               1,
450                                               code_record,
451                                               &store_ticket_issue_cont,
452                                               ih);
453   GNUNET_free (ecdhe_privkey);
454   GNUNET_free (label);
455   GNUNET_free (authz_record_data);
456 }
457
458
459
460
461 void
462 RECLAIM_TICKETS_issue_ticket (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
463                               const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
464                               const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
465                               RECLAIM_TICKETS_TicketResult cb,
466                               void* cb_cls)
467 {
468   struct TicketIssueHandle *tih;
469   tih = GNUNET_new (struct TicketIssueHandle);
470   tih->cb = cb;
471   tih->cb_cls = cb_cls;
472   tih->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (attrs);
473   tih->identity = *identity;
474   GNUNET_CRYPTO_ecdsa_key_get_public (identity,
475                                       &tih->ticket.identity);
476   tih->ticket.rnd =
477     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
478                               UINT64_MAX);
479   tih->ticket.audience = *audience;
480   issue_ticket (tih);
481 }
482
483
484 static void
485 cleanup_iter (struct RECLAIM_TICKETS_Iterator *iter)
486 {
487   struct TicketReference *tr;
488   struct TicketReference *tr_tmp;
489   if (NULL != iter->ns_qe)
490     GNUNET_NAMESTORE_cancel (iter->ns_qe);
491   for (tr = iter->tickets_head; NULL != tr;)
492   {
493     if (NULL != tr->attrs)
494       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
495     tr_tmp = tr;
496     tr = tr->next;
497     GNUNET_free (tr_tmp);
498   }
499   GNUNET_free (iter);
500 }
501
502 static void
503 do_cleanup_iter (void* cls)
504 {
505   struct RECLAIM_TICKETS_Iterator *iter = cls;
506   cleanup_iter (iter);
507 }
508
509 /**
510  * Perform ticket iteration step
511  *
512  * @param ti ticket iterator to process
513  */
514 static void
515 run_ticket_iteration_round (struct RECLAIM_TICKETS_Iterator *iter)
516 {
517   struct TicketReference *tr;
518   if (NULL == iter->tickets_head)
519   {
520     //No more tickets
521     iter->cb (iter->cb_cls,
522               NULL);
523     GNUNET_SCHEDULER_add_now (&do_cleanup_iter, iter);
524     return;
525   }
526   tr = iter->tickets_head;
527   GNUNET_CONTAINER_DLL_remove (iter->tickets_head,
528                                iter->tickets_tail,
529                                tr);
530   iter->cb (iter->cb_cls,
531             &tr->ticket);
532   if (NULL != tr->attrs)
533     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
534   GNUNET_free (tr);
535 }
536
537 static void
538 collect_tickets_cb (void *cls,
539                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
540                   const char *label,
541                   unsigned int rd_count,
542                   const struct GNUNET_GNSRECORD_Data *rd)
543 {
544   struct RECLAIM_TICKETS_Iterator *iter = cls;
545   struct TicketReference *tr;
546   size_t attr_data_len;
547   const char* attr_data;
548   iter->ns_qe = NULL;
549
550   for (int i = 0; i < rd_count; i++)
551   {
552     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
553       continue;
554     tr = GNUNET_new (struct TicketReference);
555     memcpy (&tr->ticket, rd[i].data,
556             sizeof (struct GNUNET_RECLAIM_Ticket));
557     if (0 != memcmp (&tr->ticket.identity,
558                      &iter->identity_pub,
559                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
560     {
561       //Not our ticket
562       GNUNET_free (tr);
563       continue;
564     }
565     attr_data = rd[i].data + sizeof (struct GNUNET_RECLAIM_Ticket);
566     attr_data_len = rd[i].data_size - sizeof (struct GNUNET_RECLAIM_Ticket);
567     tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attr_data,
568                                                            attr_data_len);
569     GNUNET_CONTAINER_DLL_insert (iter->tickets_head,
570                                  iter->tickets_tail,
571                                  tr);
572   }
573   run_ticket_iteration_round (iter);
574 }
575
576 static void
577 collect_tickets_error_cb (void *cls)
578 {
579   struct RECLAIM_TICKETS_Iterator *iter = cls;
580   iter->ns_qe = NULL;
581   iter->cb (iter->cb_cls,
582             NULL);
583   cleanup_iter (iter);
584 }
585
586 void
587 RECLAIM_TICKETS_iteration_next (struct RECLAIM_TICKETS_Iterator *iter)
588 {
589   run_ticket_iteration_round (iter);
590 }
591
592 void
593 RECLAIM_TICKETS_iteration_stop (struct RECLAIM_TICKETS_Iterator *iter)
594 {
595   cleanup_iter (iter);
596 }
597
598 struct RECLAIM_TICKETS_Iterator*
599 RECLAIM_TICKETS_iteration_start (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
600                                  RECLAIM_TICKETS_TicketIter cb,
601                                  void* cb_cls)
602 {
603   struct RECLAIM_TICKETS_Iterator *iter;
604
605   iter = GNUNET_new (struct RECLAIM_TICKETS_Iterator);
606   iter->identity = *identity;
607   GNUNET_CRYPTO_ecdsa_key_get_public (identity,
608                                       &iter->identity_pub);
609   iter->cb = cb;
610   iter->cb_cls = cb_cls;
611   iter->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
612                                                  identity,
613                                                  GNUNET_GNS_EMPTY_LABEL_AT,
614                                                  &collect_tickets_error_cb,
615                                                  iter,
616                                                  &collect_tickets_cb,
617                                                  iter);
618   return iter;
619 }
620
621
622
623
624 int
625 RECLAIM_TICKETS_init (const struct GNUNET_CONFIGURATION_Handle *c)
626 {
627   //Connect to identity and namestore services
628   nsh = GNUNET_NAMESTORE_connect (c);
629   if (NULL == nsh)
630   {
631     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
632                 "Error connecting to namestore\n");
633     return GNUNET_SYSERR;
634   }
635   return GNUNET_OK;
636 }
637
638 void
639 RECLAIM_TICKETS_deinit (void)
640 {
641   if (NULL != nsh)
642     GNUNET_NAMESTORE_disconnect (nsh);
643   nsh = NULL;
644 }