RECLAIM: refactoring; cleanup
[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 static struct GNUNET_NAMESTORE_Handle *nsh;
109
110 /**
111  * Cleanup ticket consume handle
112  * @param handle the handle to clean up
113  */
114 static void
115 cleanup_issue_handle (struct TicketIssueHandle *handle)
116 {
117   struct TicketReference *tr;
118   struct TicketReference *tr_tmp;
119   if (NULL != handle->attrs)
120     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (handle->attrs);
121   if (NULL != handle->ns_qe)
122     GNUNET_NAMESTORE_cancel (handle->ns_qe);
123   for (tr = handle->ticket_refs_head; NULL != tr;)
124   {
125     if (NULL != tr->attrs)
126       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
127     tr_tmp = tr;
128     tr = tr->next;
129     GNUNET_free (tr_tmp);
130   }
131   GNUNET_free (handle);
132 }
133
134
135
136 static void
137 store_ticket_refs_cont (void *cls,
138                         int32_t success,
139                         const char *emsg)
140 {
141   struct TicketIssueHandle *handle = cls;
142   handle->ns_qe = NULL;
143   if (GNUNET_OK != success)
144   {
145     handle->cb (handle->cb_cls,
146                 NULL,
147                 GNUNET_SYSERR,
148                 "Error storing updated ticket refs in GNS");
149     cleanup_issue_handle (handle);
150     return;
151   }
152   handle->cb (handle->cb_cls,
153               &handle->ticket,
154               GNUNET_OK,
155               NULL);
156   cleanup_issue_handle (handle);
157 }
158
159
160
161 static void
162 update_ticket_refs (void* cls)
163 {
164   struct TicketIssueHandle *handle = cls;
165   struct GNUNET_GNSRECORD_Data refs_rd[handle->ticket_ref_num];
166   struct TicketReference *tr;
167   char* buf;
168   size_t buf_size;
169
170   tr = handle->ticket_refs_head;
171   for (int i = 0; i < handle->ticket_ref_num; i++)
172   {
173     buf_size = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (tr->attrs);
174     buf_size += sizeof (struct GNUNET_RECLAIM_Ticket);
175     buf = GNUNET_malloc (buf_size);
176     memcpy (buf, &tr->ticket, sizeof (struct GNUNET_RECLAIM_Ticket));
177     GNUNET_RECLAIM_ATTRIBUTE_list_serialize (tr->attrs,
178                                              buf + sizeof (struct GNUNET_RECLAIM_Ticket));
179     refs_rd[i].data = buf;
180     refs_rd[i].data_size = buf_size;
181     refs_rd[i].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
182     refs_rd[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF;
183     refs_rd[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION |
184       GNUNET_GNSRECORD_RF_PRIVATE;
185     tr = tr->next;
186   }
187
188   handle->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
189                                                   &handle->identity,
190                                                   GNUNET_GNS_EMPTY_LABEL_AT,
191                                                   handle->ticket_ref_num,
192                                                   refs_rd,
193                                                   &store_ticket_refs_cont,
194                                                   handle);
195   for (int i = 0; i < handle->ticket_ref_num; i++)
196     GNUNET_free ((char*)refs_rd[i].data);
197 }
198
199
200
201 static void
202 ticket_lookup_cb (void *cls,
203                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
204                   const char *label,
205                   unsigned int rd_count,
206                   const struct GNUNET_GNSRECORD_Data *rd)
207 {
208   struct TicketIssueHandle *handle = cls;
209   struct TicketReference *tr;
210   const char* attr_data;
211   size_t attr_data_len;
212   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213               "Received tickets from local namestore.\n");
214   handle->ns_qe = NULL;
215   for (int i = 0; i < rd_count; i++)
216   {
217     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
218       continue;
219     tr = GNUNET_new (struct TicketReference);
220     memcpy (&tr->ticket, rd[i].data,
221             sizeof (struct GNUNET_RECLAIM_Ticket));
222     if (0 != memcmp (&tr->ticket.identity,
223                      &handle->ticket.identity,
224                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
225     {
226       //Not our ticket
227       GNUNET_free (tr);
228       continue;
229     }
230     attr_data = rd[i].data + sizeof (struct GNUNET_RECLAIM_Ticket);
231     attr_data_len = rd[i].data_size - sizeof (struct GNUNET_RECLAIM_Ticket);
232     tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attr_data,
233                                                            attr_data_len);
234     GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
235                                  handle->ticket_refs_tail,
236                                  tr);
237     handle->ticket_ref_num++;
238   }
239   tr = GNUNET_new (struct TicketReference);
240   tr->ticket = handle->ticket;
241   tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (handle->attrs);
242   GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
243                                handle->ticket_refs_tail,
244                                tr);
245   handle->ticket_ref_num++;
246   GNUNET_SCHEDULER_add_now (&update_ticket_refs, handle);
247 }
248
249 static void
250 ticket_lookup_error_cb (void *cls)
251 {
252   struct TicketIssueHandle *handle = cls;
253   handle->ns_qe = NULL;
254   handle->cb (handle->cb_cls,
255               &handle->ticket,
256               GNUNET_SYSERR,
257               "Error checking for ticketsin GNS\n");
258   cleanup_issue_handle (handle);
259 }
260
261 static void
262 store_ticket_issue_cont (void *cls,
263                          int32_t success,
264                          const char *emsg)
265 {
266   struct TicketIssueHandle *handle = cls;
267
268   handle->ns_qe = NULL;
269   if (GNUNET_SYSERR == success)
270   {
271     handle->cb (handle->cb_cls,
272                 &handle->ticket,
273                 GNUNET_SYSERR,
274                 "Error storing AuthZ ticket in GNS");
275     return;
276   }
277   /* First, local references to tickets */
278   handle->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
279                                                    &handle->identity,
280                                                    GNUNET_GNS_EMPTY_LABEL_AT,
281                                                    &ticket_lookup_error_cb,
282                                                    handle,
283                                                    &ticket_lookup_cb,
284                                                    handle);
285 }
286
287 static int
288 create_sym_key_from_ecdh (const struct GNUNET_HashCode *new_key_hash,
289                           struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
290                           struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
291 {
292   struct GNUNET_CRYPTO_HashAsciiEncoded new_key_hash_str;
293
294   GNUNET_CRYPTO_hash_to_enc (new_key_hash,
295                              &new_key_hash_str);
296   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating symmetric rsa key from %s\n", (char*)&new_key_hash_str);
297   static const char ctx_key[] = "gnuid-aes-ctx-key";
298   GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
299                      new_key_hash, sizeof (struct GNUNET_HashCode),
300                      ctx_key, strlen (ctx_key),
301                      NULL, 0);
302   static const char ctx_iv[] = "gnuid-aes-ctx-iv";
303   GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
304                      new_key_hash, sizeof (struct GNUNET_HashCode),
305                      ctx_iv, strlen (ctx_iv),
306                      NULL, 0);
307   return GNUNET_OK;
308 }
309
310
311 static int
312 serialize_authz_record (const struct GNUNET_RECLAIM_Ticket *ticket,
313                         const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
314                         struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey,
315                         char **result)
316 {
317   struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey;
318   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
319   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
320   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
321   struct GNUNET_HashCode new_key_hash;
322   ssize_t enc_size;
323   char *enc_keyinfo;
324   char *buf;
325   char *write_ptr;
326   char attrs_str_len;
327   char* label;
328
329   GNUNET_assert (NULL != attrs->list_head);
330   attrs_str_len = 0;
331   for (le = attrs->list_head; NULL != le; le = le->next) {
332     attrs_str_len += 15 + 1; //TODO propery calculate
333   }
334   buf = GNUNET_malloc (attrs_str_len);
335   write_ptr = buf;
336   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337               "Writing attributes\n");
338   for (le = attrs->list_head; NULL != le; le = le->next) {
339     label = GNUNET_STRINGS_data_to_string_alloc (&le->claim->id,
340                                                  sizeof (uint64_t));
341     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
342                 "Adding attribute to record: %s\n", label);
343
344     GNUNET_memcpy (write_ptr,
345                    label,
346                    strlen (label));
347     write_ptr[strlen (label)] = ',';
348     write_ptr += strlen (label) + 1;
349     GNUNET_free (label);
350   }
351   write_ptr--;
352   write_ptr[0] = '\0'; //replace last , with a 0-terminator
353   // ECDH keypair E = eG
354   *ecdh_privkey = GNUNET_CRYPTO_ecdhe_key_create();
355   GNUNET_CRYPTO_ecdhe_key_get_public (*ecdh_privkey,
356                                       &ecdh_pubkey);
357   enc_keyinfo = GNUNET_malloc (attrs_str_len);
358   // Derived key K = H(eB)
359   GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (*ecdh_privkey,
360                                                         &ticket->audience,
361                                                         &new_key_hash));
362   create_sym_key_from_ecdh (&new_key_hash, &skey, &iv);
363   enc_size = GNUNET_CRYPTO_symmetric_encrypt (buf,
364                                               attrs_str_len,
365                                               &skey, &iv,
366                                               enc_keyinfo);
367   *result = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+
368                            enc_size);
369   GNUNET_memcpy (*result,
370                  &ecdh_pubkey,
371                  sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
372   GNUNET_memcpy (*result + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
373                  enc_keyinfo,
374                  enc_size);
375   GNUNET_free (enc_keyinfo);
376   GNUNET_free (buf);
377   return sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+enc_size;
378 }
379
380
381
382 static void
383 issue_ticket (struct TicketIssueHandle *ih)
384 {
385   struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
386   struct GNUNET_GNSRECORD_Data code_record[1];
387   char *authz_record_data;
388   size_t authz_record_len;
389   char *label;
390
391   //TODO rename function
392   authz_record_len = serialize_authz_record (&ih->ticket,
393                                              ih->attrs,
394                                              &ecdhe_privkey,
395                                              &authz_record_data);
396   code_record[0].data = authz_record_data;
397   code_record[0].data_size = authz_record_len;
398   code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
399   code_record[0].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_AUTHZ;
400   code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
401
402   label = GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd,
403                                                sizeof (uint64_t));
404   //Publish record
405   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
406                                               &ih->identity,
407                                               label,
408                                               1,
409                                               code_record,
410                                               &store_ticket_issue_cont,
411                                               ih);
412   GNUNET_free (ecdhe_privkey);
413   GNUNET_free (label);
414   GNUNET_free (authz_record_data);
415 }
416
417
418
419
420 void
421 RECLAIM_TICKETS_issue_ticket (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
422                               const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
423                               const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
424                               RECLAIM_TICKETS_TicketResult cb,
425                               void* cb_cls)
426 {
427   struct TicketIssueHandle *tih;
428   tih = GNUNET_new (struct TicketIssueHandle);
429   tih->cb = cb;
430   tih->cb_cls = cb_cls;
431   tih->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (attrs);
432   tih->identity = *identity;
433   GNUNET_CRYPTO_ecdsa_key_get_public (identity,
434                                       &tih->ticket.identity);
435   tih->ticket.rnd =
436     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
437                               UINT64_MAX);
438   tih->ticket.audience = *audience;
439   issue_ticket (tih);
440 }
441
442
443 int
444 RECLAIM_TICKETS_init (const struct GNUNET_CONFIGURATION_Handle *c)
445 {
446   //Connect to identity and namestore services
447   nsh = GNUNET_NAMESTORE_connect (c);
448   if (NULL == nsh)
449   {
450     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
451                 "Error connecting to namestore\n");
452     return GNUNET_SYSERR;
453   }
454   return GNUNET_OK;
455 }