uncrustify as demanded.
[oweals/gnunet.git] / src / credential / plugin_rest_credential.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2016 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  * @author Martin Schanzenbach
22  * @file credential/plugin_rest_credential.c
23  * @brief GNUnet CREDENTIAL REST plugin
24  *
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include <gnunet_identity_service.h>
30 #include <gnunet_gnsrecord_lib.h>
31 #include <gnunet_namestore_service.h>
32 #include <gnunet_credential_service.h>
33 #include <gnunet_rest_lib.h>
34 #include <gnunet_jsonapi_lib.h>
35 #include <gnunet_jsonapi_util.h>
36 #include <jansson.h>
37
38 #define GNUNET_REST_API_NS_CREDENTIAL "/credential"
39
40 #define GNUNET_REST_API_NS_CREDENTIAL_ISSUE "/credential/issue"
41
42 #define GNUNET_REST_API_NS_CREDENTIAL_VERIFY "/credential/verify"
43
44 #define GNUNET_REST_API_NS_CREDENTIAL_COLLECT "/credential/collect"
45
46 #define GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION "expiration"
47
48 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY "subject_key"
49
50 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_EGO "subject"
51
52 #define GNUNET_REST_JSONAPI_CREDENTIAL "credential"
53
54 #define GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO "credential"
55
56 #define GNUNET_REST_JSONAPI_DELEGATIONS "delegations"
57
58 #define GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR "attribute"
59
60 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR "credential"
61
62 /**
63  * @brief struct returned by the initialization function of the plugin
64  */
65 struct Plugin {
66   const struct GNUNET_CONFIGURATION_Handle *cfg;
67 };
68
69 const struct GNUNET_CONFIGURATION_Handle *cfg;
70
71 struct RequestHandle {
72   /**
73    * Handle to Credential service.
74    */
75   struct GNUNET_CREDENTIAL_Handle *credential;
76
77   /**
78    * Handle to lookup request
79    */
80   struct GNUNET_CREDENTIAL_Request *verify_request;
81
82   /**
83    * Handle to issue request
84    */
85   struct GNUNET_CREDENTIAL_Request *issue_request;
86
87   /**
88    * Handle to identity
89    */
90   struct GNUNET_IDENTITY_Handle *identity;
91
92   /**
93    * Handle to identity operation
94    */
95   struct GNUNET_IDENTITY_Operation *id_op;
96
97   /**
98    * Handle to ego lookup
99    */
100   struct GNUNET_IDENTITY_EgoLookup *ego_lookup;
101
102   /**
103    * Handle to rest request
104    */
105   struct GNUNET_REST_RequestHandle *rest_handle;
106
107   /**
108    * ID of a task associated with the resolution process.
109    */
110   struct GNUNET_SCHEDULER_Task * timeout_task;
111
112   /**
113    * The root of the received JSON or NULL
114    */
115   json_t *json_root;
116
117   /**
118    * The plugin result processor
119    */
120   GNUNET_REST_ResultProcessor proc;
121
122   /**
123    * The closure of the result processor
124    */
125   void *proc_cls;
126
127   /**
128    * The issuer attribute to verify
129    */
130   char *issuer_attr;
131
132   /**
133    * The subject attribute
134    */
135   char *subject_attr;
136
137   /**
138    * The public key of the issuer
139    */
140   struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
141
142   /**
143    * The public key of the subject
144    */
145   struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
146
147   /**
148    * HTTP response code
149    */
150   int response_code;
151
152   /**
153    * Timeout
154    */
155   struct GNUNET_TIME_Relative timeout;
156 };
157
158
159 /**
160  * Cleanup lookup handle.
161  *
162  * @param handle Handle to clean up
163  */
164 static void
165 cleanup_handle(struct RequestHandle *handle)
166 {
167   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
168              "Cleaning up\n");
169   if (NULL != handle->json_root)
170     json_decref(handle->json_root);
171
172   if (NULL != handle->issuer_attr)
173     GNUNET_free(handle->issuer_attr);
174   if (NULL != handle->subject_attr)
175     GNUNET_free(handle->subject_attr);
176   if (NULL != handle->verify_request)
177     GNUNET_CREDENTIAL_request_cancel(handle->verify_request);
178   if (NULL != handle->credential)
179     GNUNET_CREDENTIAL_disconnect(handle->credential);
180   if (NULL != handle->id_op)
181     GNUNET_IDENTITY_cancel(handle->id_op);
182   if (NULL != handle->ego_lookup)
183     GNUNET_IDENTITY_ego_lookup_cancel(handle->ego_lookup);
184   if (NULL != handle->identity)
185     GNUNET_IDENTITY_disconnect(handle->identity);
186   if (NULL != handle->timeout_task)
187     {
188       GNUNET_SCHEDULER_cancel(handle->timeout_task);
189     }
190   GNUNET_free(handle);
191 }
192
193
194 static void
195 do_error(void *cls)
196 {
197   struct RequestHandle *handle = cls;
198   struct MHD_Response *resp;
199
200   resp = GNUNET_REST_create_response(NULL);
201   handle->proc(handle->proc_cls, resp, handle->response_code);
202   cleanup_handle(handle);
203 }
204
205 /**
206  * Attribute delegation to JSON
207  *
208  * @param delegation_chain_entry the DSE
209  * @return JSON, NULL if failed
210  */
211 static json_t*
212 attribute_delegation_to_json(struct GNUNET_CREDENTIAL_Delegation *delegation_chain_entry)
213 {
214   char *subject;
215   char *issuer;
216   json_t *attr_obj;
217
218   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string(&delegation_chain_entry->issuer_key);
219   if (NULL == issuer)
220     {
221       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
222                  "Issuer in delegation malformed\n");
223       return NULL;
224     }
225   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string(&delegation_chain_entry->subject_key);
226   if (NULL == subject)
227     {
228       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
229                  "Subject in credential malformed\n");
230       GNUNET_free(issuer);
231       return NULL;
232     }
233   attr_obj = json_object();
234
235   json_object_set_new(attr_obj, "issuer", json_string(issuer));
236   json_object_set_new(attr_obj, "issuer_attribute",
237                       json_string(delegation_chain_entry->issuer_attribute));
238
239   json_object_set_new(attr_obj, "subject", json_string(subject));
240   if (0 < delegation_chain_entry->subject_attribute_len)
241     {
242       json_object_set_new(attr_obj, "subject_attribute",
243                           json_string(delegation_chain_entry->subject_attribute));
244     }
245   GNUNET_free(issuer);
246   GNUNET_free(subject);
247   return attr_obj;
248 }
249
250 /**
251  * JSONAPI resource to Credential
252  *
253  * @param res the JSONAPI resource
254  * @return the resulting credential, NULL if failed
255  */
256 static struct GNUNET_CREDENTIAL_Credential*
257 json_to_credential(json_t *res)
258 {
259   struct GNUNET_CREDENTIAL_Credential *cred;
260   json_t *tmp;
261   const char *attribute;
262   const char *signature;
263   char *sig;
264
265   tmp = json_object_get(res, "attribute");
266   if (0 == json_is_string(tmp))
267     {
268       return NULL;
269     }
270   attribute = json_string_value(tmp);
271   cred = GNUNET_malloc(sizeof(struct GNUNET_CREDENTIAL_Credential)
272                        + strlen(attribute));
273   cred->issuer_attribute = attribute;
274   cred->issuer_attribute_len = strlen(attribute);
275   tmp = json_object_get(res, "issuer");
276   if (0 == json_is_string(tmp))
277     {
278       GNUNET_free(cred);
279       return NULL;
280     }
281
282   GNUNET_CRYPTO_ecdsa_public_key_from_string(json_string_value(tmp),
283                                              strlen(json_string_value(tmp)),
284                                              &cred->issuer_key);
285   tmp = json_object_get(res, "subject");
286   if (0 == json_is_string(tmp))
287     {
288       GNUNET_free(cred);
289       return NULL;
290     }
291   GNUNET_CRYPTO_ecdsa_public_key_from_string(json_string_value(tmp),
292                                              strlen(json_string_value(tmp)),
293                                              &cred->subject_key);
294
295   tmp = json_object_get(res, "signature");
296   if (0 == json_is_string(tmp))
297     {
298       GNUNET_free(cred);
299       return NULL;
300     }
301   signature = json_string_value(tmp);
302   GNUNET_STRINGS_base64_decode(signature,
303                                strlen(signature),
304                                (char**)&sig);
305   GNUNET_memcpy(&cred->signature,
306                 sig,
307                 sizeof(struct GNUNET_CRYPTO_EcdsaSignature));
308   GNUNET_free(sig);
309
310   tmp = json_object_get(res, "expiration");
311   if (0 == json_is_integer(tmp))
312     {
313       GNUNET_free(cred);
314       return NULL;
315     }
316   cred->expiration.abs_value_us = json_integer_value(tmp);
317   return cred;
318 }
319
320
321 /**
322  * Credential to JSON
323  *
324  * @param cred the credential
325  * @return the resulting json, NULL if failed
326  */
327 static json_t*
328 credential_to_json(struct GNUNET_CREDENTIAL_Credential *cred)
329 {
330   char *issuer;
331   char *subject;
332   char *signature;
333   char attribute[cred->issuer_attribute_len + 1];
334   json_t *cred_obj;
335
336   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string(&cred->issuer_key);
337   if (NULL == issuer)
338     {
339       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
340                  "Issuer in credential malformed\n");
341       return NULL;
342     }
343   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string(&cred->subject_key);
344   if (NULL == subject)
345     {
346       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
347                  "Subject in credential malformed\n");
348       GNUNET_free(issuer);
349       return NULL;
350     }
351   GNUNET_STRINGS_base64_encode((char*)&cred->signature,
352                                sizeof(struct GNUNET_CRYPTO_EcdsaSignature),
353                                &signature);
354   GNUNET_memcpy(attribute,
355                 cred->issuer_attribute,
356                 cred->issuer_attribute_len);
357   attribute[cred->issuer_attribute_len] = '\0';
358   cred_obj = json_object();
359   json_object_set_new(cred_obj, "issuer", json_string(issuer));
360   json_object_set_new(cred_obj, "subject", json_string(subject));
361   json_object_set_new(cred_obj, "attribute", json_string(attribute));
362   json_object_set_new(cred_obj, "signature", json_string(signature));
363   json_object_set_new(cred_obj, "expiration", json_integer(cred->expiration.abs_value_us));
364   GNUNET_free(issuer);
365   GNUNET_free(subject);
366   GNUNET_free(signature);
367   return cred_obj;
368 }
369
370 static void
371 handle_collect_response(void *cls,
372                         unsigned int d_count,
373                         struct GNUNET_CREDENTIAL_Delegation *delegation_chain,
374                         unsigned int c_count,
375                         struct GNUNET_CREDENTIAL_Credential *cred)
376 {
377   struct RequestHandle *handle = cls;
378   struct MHD_Response *resp;
379   struct GNUNET_JSONAPI_Document *json_document;
380   struct GNUNET_JSONAPI_Resource *json_resource;
381   json_t *cred_obj;
382   json_t *cred_array;
383   char *result;
384   char *issuer;
385   char *id;
386   uint32_t i;
387
388   handle->verify_request = NULL;
389   if (NULL == cred)
390     {
391       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
392                  "Verify failed.\n");
393       handle->response_code = MHD_HTTP_NOT_FOUND;
394       GNUNET_SCHEDULER_add_now(&do_error, handle);
395       return;
396     }
397   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string(&handle->issuer_key);
398   if (NULL == issuer)
399     {
400       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
401                  "Issuer in delegation malformed\n");
402       return;
403     }
404   GNUNET_asprintf(&id,
405                   "%s.%s",
406                   issuer,
407                   handle->issuer_attr);
408   GNUNET_free(issuer);
409   json_document = GNUNET_JSONAPI_document_new();
410   json_resource = GNUNET_JSONAPI_resource_new(GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
411                                               id);
412   GNUNET_free(id);
413   cred_array = json_array();
414   for (i = 0; i < c_count; i++)
415     {
416       cred_obj = credential_to_json(&cred[i]);
417       json_array_append_new(cred_array, cred_obj);
418     }
419   GNUNET_JSONAPI_resource_add_attr(json_resource,
420                                    GNUNET_REST_JSONAPI_CREDENTIAL,
421                                    cred_array);
422   GNUNET_JSONAPI_document_resource_add(json_document, json_resource);
423   GNUNET_JSONAPI_document_serialize(json_document, &result);
424   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
425              "Result %s\n",
426              result);
427   json_decref(cred_array);
428   GNUNET_JSONAPI_document_delete(json_document);
429   resp = GNUNET_REST_create_response(result);
430   GNUNET_free(result);
431   handle->proc(handle->proc_cls, resp, MHD_HTTP_OK);
432   cleanup_handle(handle);
433 }
434
435 static void
436 subject_ego_lookup(void *cls,
437                    const struct GNUNET_IDENTITY_Ego *ego)
438 {
439   struct RequestHandle *handle = cls;
440   const struct GNUNET_CRYPTO_EcdsaPrivateKey *sub_key;
441
442   handle->ego_lookup = NULL;
443
444   if (NULL == ego)
445     {
446       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
447                  "Subject not found\n");
448       GNUNET_SCHEDULER_add_now(&do_error, handle);
449       return;
450     }
451   sub_key = GNUNET_IDENTITY_ego_get_private_key(ego);
452   handle->verify_request = GNUNET_CREDENTIAL_collect(handle->credential,
453                                                      &handle->issuer_key,
454                                                      handle->issuer_attr,
455                                                      sub_key,
456                                                      &handle_collect_response,
457                                                      handle);
458 }
459
460
461
462 static void
463 handle_verify_response(void *cls,
464                        unsigned int d_count,
465                        struct GNUNET_CREDENTIAL_Delegation *delegation_chain,
466                        unsigned int c_count,
467                        struct GNUNET_CREDENTIAL_Credential *cred)
468 {
469   struct RequestHandle *handle = cls;
470   struct MHD_Response *resp;
471   struct GNUNET_JSONAPI_Document *json_document;
472   struct GNUNET_JSONAPI_Resource *json_resource;
473   json_t *cred_obj;
474   json_t *attr_obj;
475   json_t *cred_array;
476   json_t *attr_array;
477   char *result;
478   char *issuer;
479   char *id;
480   uint32_t i;
481
482   handle->verify_request = NULL;
483   if (NULL == cred)
484     {
485       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
486                  "Verify failed.\n");
487       handle->response_code = MHD_HTTP_NOT_FOUND;
488       GNUNET_SCHEDULER_add_now(&do_error, handle);
489       return;
490     }
491   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string(&handle->issuer_key);
492   if (NULL == issuer)
493     {
494       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
495                  "Issuer in delegation malformed\n");
496       return;
497     }
498   GNUNET_asprintf(&id,
499                   "%s.%s",
500                   issuer,
501                   handle->issuer_attr);
502   GNUNET_free(issuer);
503   json_document = GNUNET_JSONAPI_document_new();
504   json_resource = GNUNET_JSONAPI_resource_new(GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
505                                               id);
506   GNUNET_free(id);
507   attr_array = json_array();
508   for (i = 0; i < d_count; i++)
509     {
510       attr_obj = attribute_delegation_to_json(&delegation_chain[i]);
511       json_array_append_new(attr_array, attr_obj);
512     }
513   cred_array = json_array();
514   for (i = 0; i < c_count; i++)
515     {
516       cred_obj = credential_to_json(&cred[i]);
517       json_array_append_new(cred_array, cred_obj);
518     }
519   GNUNET_JSONAPI_resource_add_attr(json_resource,
520                                    GNUNET_REST_JSONAPI_CREDENTIAL,
521                                    cred_array);
522   GNUNET_JSONAPI_resource_add_attr(json_resource,
523                                    GNUNET_REST_JSONAPI_DELEGATIONS,
524                                    attr_array);
525   GNUNET_JSONAPI_document_resource_add(json_document, json_resource);
526   GNUNET_JSONAPI_document_serialize(json_document, &result);
527   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
528              "Result %s\n",
529              result);
530   json_decref(attr_array);
531   json_decref(cred_array);
532   GNUNET_JSONAPI_document_delete(json_document);
533   resp = GNUNET_REST_create_response(result);
534   handle->proc(handle->proc_cls, resp, MHD_HTTP_OK);
535   GNUNET_free(result);
536   cleanup_handle(handle);
537 }
538
539 static void
540 collect_cred_cont(struct GNUNET_REST_RequestHandle *conndata_handle,
541                   const char* url,
542                   void *cls)
543 {
544   struct RequestHandle *handle = cls;
545   struct GNUNET_HashCode key;
546   char *tmp;
547   char *entity_attr;
548
549   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
550              "Connecting...\n");
551   handle->credential = GNUNET_CREDENTIAL_connect(cfg);
552   handle->timeout_task = GNUNET_SCHEDULER_add_delayed(handle->timeout,
553                                                       &do_error, handle);
554   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
555              "Connected\n");
556   if (NULL == handle->credential)
557     {
558       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
559                  "Connecting to CREDENTIAL failed\n");
560       GNUNET_SCHEDULER_add_now(&do_error, handle);
561       return;
562     }
563   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
564                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
565                      &key);
566   if (GNUNET_NO ==
567       GNUNET_CONTAINER_multihashmap_contains(conndata_handle->url_param_map,
568                                              &key))
569     {
570       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
571                  "Missing issuer attribute\n");
572       GNUNET_SCHEDULER_add_now(&do_error, handle);
573       return;
574     }
575   tmp = GNUNET_CONTAINER_multihashmap_get(conndata_handle->url_param_map,
576                                           &key);
577   entity_attr = GNUNET_strdup(tmp);
578   tmp = strtok(entity_attr, ".");
579   if (NULL == tmp)
580     {
581       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
582                  "Malformed issuer or attribute\n");
583       GNUNET_free(entity_attr);
584       GNUNET_SCHEDULER_add_now(&do_error, handle);
585       return;
586     }
587   if (GNUNET_OK !=
588       GNUNET_CRYPTO_ecdsa_public_key_from_string(tmp,
589                                                  strlen(tmp),
590                                                  &handle->issuer_key))
591     {
592       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
593                  "Malformed issuer key\n");
594       GNUNET_free(entity_attr);
595       GNUNET_SCHEDULER_add_now(&do_error, handle);
596       return;
597     }
598   tmp = strtok(NULL, ".");  //Issuer attribute
599   if (NULL == tmp)
600     {
601       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
602                  "Malformed attribute\n");
603       GNUNET_free(entity_attr);
604       GNUNET_SCHEDULER_add_now(&do_error, handle);
605       return;
606     }
607   handle->issuer_attr = GNUNET_strdup(tmp);
608   GNUNET_free(entity_attr);
609
610   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_EGO,
611                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_EGO),
612                      &key);
613   if (GNUNET_NO ==
614       GNUNET_CONTAINER_multihashmap_contains(conndata_handle->url_param_map,
615                                              &key))
616     {
617       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
618                  "Missing subject\n");
619       GNUNET_SCHEDULER_add_now(&do_error, handle);
620       return;
621     }
622   tmp = GNUNET_CONTAINER_multihashmap_get(conndata_handle->url_param_map,
623                                           &key);
624   if (NULL == tmp)
625     {
626       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
627                  "Malformed subject\n");
628       GNUNET_SCHEDULER_add_now(&do_error, handle);
629       return;
630     }
631   handle->ego_lookup = GNUNET_IDENTITY_ego_lookup(cfg,
632                                                   tmp,
633                                                   &subject_ego_lookup,
634                                                   handle);
635 }
636
637
638
639 static void
640 verify_cred_cont(struct GNUNET_REST_RequestHandle *conndata_handle,
641                  const char* url,
642                  void *cls)
643 {
644   struct RequestHandle *handle = cls;
645   struct GNUNET_HashCode key;
646   struct GNUNET_JSONAPI_Document *json_obj;
647   struct GNUNET_JSONAPI_Resource *res;
648   struct GNUNET_CREDENTIAL_Credential *cred;
649   char *tmp;
650   char *entity_attr;
651   int i;
652   uint32_t credential_count;
653   uint32_t resource_count;
654   json_t *cred_json;
655   json_t *data_js;
656   json_error_t err;
657
658   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
659              "Connecting...\n");
660   handle->credential = GNUNET_CREDENTIAL_connect(cfg);
661   handle->timeout_task = GNUNET_SCHEDULER_add_delayed(handle->timeout,
662                                                       &do_error, handle);
663   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
664              "Connected\n");
665   if (NULL == handle->credential)
666     {
667       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
668                  "Connecting to CREDENTIAL failed\n");
669       GNUNET_SCHEDULER_add_now(&do_error, handle);
670       return;
671     }
672   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
673                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
674                      &key);
675   if (GNUNET_NO ==
676       GNUNET_CONTAINER_multihashmap_contains(conndata_handle->url_param_map,
677                                              &key))
678     {
679       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
680                  "Missing issuer attribute\n");
681       GNUNET_SCHEDULER_add_now(&do_error, handle);
682       return;
683     }
684   tmp = GNUNET_CONTAINER_multihashmap_get(conndata_handle->url_param_map,
685                                           &key);
686   entity_attr = GNUNET_strdup(tmp);
687   tmp = strtok(entity_attr, ".");
688   if (NULL == tmp)
689     {
690       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
691                  "Malformed issuer or attribute\n");
692       GNUNET_free(entity_attr);
693       GNUNET_SCHEDULER_add_now(&do_error, handle);
694       return;
695     }
696   if (GNUNET_OK !=
697       GNUNET_CRYPTO_ecdsa_public_key_from_string(tmp,
698                                                  strlen(tmp),
699                                                  &handle->issuer_key))
700     {
701       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
702                  "Malformed issuer key\n");
703       GNUNET_free(entity_attr);
704       GNUNET_SCHEDULER_add_now(&do_error, handle);
705       return;
706     }
707   tmp = strtok(NULL, ".");  //Issuer attribute
708   if (NULL == tmp)
709     {
710       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
711                  "Malformed attribute\n");
712       GNUNET_free(entity_attr);
713       GNUNET_SCHEDULER_add_now(&do_error, handle);
714       return;
715     }
716   handle->issuer_attr = GNUNET_strdup(tmp);
717   GNUNET_free(entity_attr);
718
719   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY,
720                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY),
721                      &key);
722   if (GNUNET_NO ==
723       GNUNET_CONTAINER_multihashmap_contains(conndata_handle->url_param_map,
724                                              &key))
725     {
726       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
727                  "Missing subject key\n");
728       GNUNET_SCHEDULER_add_now(&do_error, handle);
729       return;
730     }
731   tmp = GNUNET_CONTAINER_multihashmap_get(conndata_handle->url_param_map,
732                                           &key);
733   if (NULL == tmp)
734     {
735       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
736                  "Malformed subject\n");
737       GNUNET_SCHEDULER_add_now(&do_error, handle);
738       return;
739     }
740   if (GNUNET_OK !=
741       GNUNET_CRYPTO_ecdsa_public_key_from_string(tmp,
742                                                  strlen(tmp),
743                                                  &handle->subject_key))
744     {
745       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
746                  "Malformed subject key\n");
747       GNUNET_SCHEDULER_add_now(&do_error, handle);
748       return;
749     }
750
751   if (0 >= handle->rest_handle->data_size)
752     {
753       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
754                  "Missing credentials\n");
755       GNUNET_SCHEDULER_add_now(&do_error, handle);
756       return;
757     }
758
759   struct GNUNET_JSON_Specification docspec[] = {
760     GNUNET_JSON_spec_jsonapi_document(&json_obj),
761     GNUNET_JSON_spec_end()
762   };
763   char term_data[handle->rest_handle->data_size + 1];
764   term_data[handle->rest_handle->data_size] = '\0';
765   credential_count = 0;
766   GNUNET_memcpy(term_data,
767                 handle->rest_handle->data,
768                 handle->rest_handle->data_size);
769   data_js = json_loads(term_data,
770                        JSON_DECODE_ANY,
771                        &err);
772   GNUNET_assert(GNUNET_OK == GNUNET_JSON_parse(data_js, docspec,
773                                                NULL, NULL));
774   json_decref(data_js);
775   if (NULL == json_obj)
776     {
777       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
778                  "Unable to parse JSONAPI Object from %s\n",
779                  term_data);
780       GNUNET_SCHEDULER_add_now(&do_error, handle);
781       return;
782     }
783
784   resource_count = GNUNET_JSONAPI_document_resource_count(json_obj);
785   GNUNET_assert(1 == resource_count);
786   res = (GNUNET_JSONAPI_document_get_resource(json_obj, 0));
787   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type(res,
788                                                       GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO))
789     {
790       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
791                  "Resource not a credential!\n");
792       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
793                  "Unable to parse JSONAPI Object from %s\n",
794                  term_data);
795       GNUNET_JSONAPI_document_delete(json_obj);
796       GNUNET_SCHEDULER_add_now(&do_error, handle);
797       return;
798     }
799   cred_json = GNUNET_JSONAPI_resource_read_attr(res,
800                                                 GNUNET_REST_JSONAPI_CREDENTIAL);
801
802   GNUNET_assert(json_is_array(cred_json));
803
804   credential_count = json_array_size(cred_json);
805
806   struct GNUNET_CREDENTIAL_Credential credentials[credential_count];
807   for (i = 0; i < credential_count; i++)
808     {
809       cred = json_to_credential(json_array_get(cred_json, i));
810       if (NULL == cred)
811         {
812           GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
813                      "Unable to parse credential!\n");
814           continue;
815         }
816       GNUNET_memcpy(&credentials[i],
817                     cred,
818                     sizeof(struct GNUNET_CREDENTIAL_Credential));
819       credentials[i].issuer_attribute = GNUNET_strdup(cred->issuer_attribute);
820       GNUNET_free(cred);
821     }
822   GNUNET_JSONAPI_document_delete(json_obj);
823   handle->verify_request = GNUNET_CREDENTIAL_verify(handle->credential,
824                                                     &handle->issuer_key,
825                                                     handle->issuer_attr,
826                                                     &handle->subject_key,
827                                                     credential_count,
828                                                     credentials,
829                                                     &handle_verify_response,
830                                                     handle);
831   for (i = 0; i < credential_count; i++)
832     GNUNET_free((char*)credentials[i].issuer_attribute);
833 }
834
835 void
836 send_cred_response(struct RequestHandle *handle,
837                    struct GNUNET_CREDENTIAL_Credential *cred)
838 {
839   struct MHD_Response *resp;
840   struct GNUNET_JSONAPI_Document *json_document;
841   struct GNUNET_JSONAPI_Resource *json_resource;
842   json_t *cred_obj;
843   char *result;
844   char *issuer;
845   char *subject;
846   char *signature;
847   char *id;
848
849   GNUNET_assert(NULL != cred);
850   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string(&cred->issuer_key);
851   if (NULL == issuer)
852     {
853       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
854                  "Subject malformed\n");
855       GNUNET_free(issuer);
856       return;
857     }
858   GNUNET_asprintf(&id,
859                   "%s.%s",
860                   issuer,
861                   (char*)&cred[1]);
862   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string(&cred->subject_key);
863   if (NULL == subject)
864     {
865       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
866                  "Subject malformed\n");
867       GNUNET_free(id);
868       GNUNET_free(issuer);
869       return;
870     }
871   GNUNET_STRINGS_base64_encode((char*)&cred->signature,
872                                sizeof(struct GNUNET_CRYPTO_EcdsaSignature),
873                                &signature);
874   json_document = GNUNET_JSONAPI_document_new();
875   json_resource = GNUNET_JSONAPI_resource_new(GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
876                                               id);
877   GNUNET_free(id);
878   cred_obj = json_object();
879   json_object_set_new(cred_obj, "issuer", json_string(issuer));
880   json_object_set_new(cred_obj, "subject", json_string(subject));
881   json_object_set_new(cred_obj, "expiration", json_integer(cred->expiration.abs_value_us));
882   json_object_set_new(cred_obj, "signature", json_string(signature));
883   GNUNET_JSONAPI_resource_add_attr(json_resource,
884                                    GNUNET_REST_JSONAPI_CREDENTIAL,
885                                    cred_obj);
886   GNUNET_JSONAPI_document_resource_add(json_document, json_resource);
887   GNUNET_JSONAPI_document_serialize(json_document, &result);
888   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
889              "Result %s\n",
890              result);
891   json_decref(cred_obj);
892   GNUNET_JSONAPI_document_delete(json_document);
893   resp = GNUNET_REST_create_response(result);
894   handle->proc(handle->proc_cls, resp, MHD_HTTP_OK);
895   GNUNET_free(result);
896   GNUNET_free(signature);
897   GNUNET_free(issuer);
898   GNUNET_free(subject);
899   cleanup_handle(handle);
900 }
901
902 void
903 get_cred_issuer_cb(void *cls,
904                    struct GNUNET_IDENTITY_Ego *ego,
905                    void **ctx,
906                    const char *name)
907 {
908   struct RequestHandle *handle = cls;
909   struct GNUNET_TIME_Absolute etime_abs;
910   struct GNUNET_TIME_Relative etime_rel;
911   const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer_key;
912   struct GNUNET_HashCode key;
913   struct GNUNET_CREDENTIAL_Credential *cred;
914   char* expiration_str;
915   char* tmp;
916
917   handle->id_op = NULL;
918
919   if (NULL == name)
920     {
921       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
922                  "Issuer not configured!\n");
923       GNUNET_SCHEDULER_add_now(&do_error, handle);
924       return;
925     }
926
927   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
928              "Connecting to credential service...\n");
929   handle->credential = GNUNET_CREDENTIAL_connect(cfg);
930   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
931              "Connected\n");
932   if (NULL == handle->credential)
933     {
934       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
935                  "Connecting to CREDENTIAL failed\n");
936       GNUNET_SCHEDULER_add_now(&do_error, handle);
937       return;
938     }
939   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION,
940                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION),
941                      &key);
942   if (GNUNET_NO ==
943       GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
944                                              &key))
945     {
946       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
947                  "Missing expiration\n");
948       GNUNET_SCHEDULER_add_now(&do_error, handle);
949       return;
950     }
951   expiration_str = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
952                                                      &key);
953   if (NULL == expiration_str)
954     {
955       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
956                  "Expiration malformed\n");
957       GNUNET_SCHEDULER_add_now(&do_error, handle);
958       return;
959     }
960
961   if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative(expiration_str,
962                                                          &etime_rel))
963     {
964       etime_abs = GNUNET_TIME_relative_to_absolute(etime_rel);
965     }
966   else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute(expiration_str,
967                                                               &etime_abs))
968     {
969       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
970                  "Malformed expiration: %s\n", expiration_str);
971       GNUNET_SCHEDULER_add_now(&do_error, handle);
972       return;
973     }
974   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
975                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
976                      &key);
977   if (GNUNET_NO ==
978       GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
979                                              &key))
980     {
981       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
982                  "Missing issuer attribute\n");
983       GNUNET_SCHEDULER_add_now(&do_error, handle);
984       return;
985     }
986   handle->issuer_attr = GNUNET_strdup(GNUNET_CONTAINER_multihashmap_get
987                                         (handle->rest_handle->url_param_map,
988                                         &key));
989   GNUNET_CRYPTO_hash(GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY,
990                      strlen(GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY),
991                      &key);
992   if (GNUNET_NO ==
993       GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
994                                              &key))
995     {
996       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
997                  "Missing subject\n");
998       GNUNET_SCHEDULER_add_now(&do_error, handle);
999       return;
1000     }
1001   tmp = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1002                                           &key);
1003   if (NULL == tmp)
1004     {
1005       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1006                  "Malformed subject\n");
1007       GNUNET_SCHEDULER_add_now(&do_error, handle);
1008       return;
1009     }
1010   if (GNUNET_OK !=
1011       GNUNET_CRYPTO_ecdsa_public_key_from_string(tmp,
1012                                                  strlen(tmp),
1013                                                  &handle->subject_key))
1014     {
1015       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1016                  "Malformed subject key\n");
1017       GNUNET_SCHEDULER_add_now(&do_error, handle);
1018       return;
1019     }
1020   issuer_key = GNUNET_IDENTITY_ego_get_private_key(ego);
1021   cred = GNUNET_CREDENTIAL_credential_issue(issuer_key,
1022                                             &handle->subject_key,
1023                                             handle->issuer_attr,
1024                                             &etime_abs);
1025   if (NULL == cred)
1026     {
1027       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1028                  "Failed to create credential\n");
1029       GNUNET_SCHEDULER_add_now(&do_error, handle);
1030       return;
1031     }
1032   send_cred_response(handle, cred);
1033 }
1034
1035
1036 static void
1037 issue_cred_cont(struct GNUNET_REST_RequestHandle *conndata_handle,
1038                 const char* url,
1039                 void *cls)
1040 {
1041   struct RequestHandle *handle = cls;
1042
1043   handle->identity = GNUNET_IDENTITY_connect(cfg,
1044                                              NULL,
1045                                              NULL);
1046   handle->id_op = GNUNET_IDENTITY_get(handle->identity,
1047                                       "credential-issuer",
1048                                       &get_cred_issuer_cb,
1049                                       handle);
1050   handle->timeout_task = GNUNET_SCHEDULER_add_delayed(handle->timeout,
1051                                                       &do_error,
1052                                                       handle);
1053 }
1054
1055 static void
1056 options_cont(struct GNUNET_REST_RequestHandle *con_handle,
1057              const char* url,
1058              void *cls)
1059 {
1060   struct MHD_Response *resp;
1061   struct RequestHandle *handle = cls;
1062
1063   //For GNS, independent of path return all options
1064   resp = GNUNET_REST_create_response(NULL);
1065   MHD_add_response_header(resp,
1066                           "Access-Control-Allow-Methods",
1067                           MHD_HTTP_METHOD_GET);
1068   handle->proc(handle->proc_cls,
1069                resp,
1070                MHD_HTTP_OK);
1071   cleanup_handle(handle);
1072 }
1073
1074
1075 static void
1076 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
1077                                 GNUNET_REST_ResultProcessor proc,
1078                                 void *proc_cls)
1079 {
1080   struct RequestHandle *handle = GNUNET_new(struct RequestHandle);
1081   struct GNUNET_REST_RequestHandlerError err;
1082
1083   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1084   handle->proc_cls = proc_cls;
1085   handle->proc = proc;
1086   handle->rest_handle = conndata_handle;
1087
1088   static const struct GNUNET_REST_RequestHandler handlers[] = {
1089     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_CREDENTIAL_VERIFY, &verify_cred_cont },
1090     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_COLLECT, &collect_cred_cont },
1091     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_ISSUE, &issue_cred_cont },
1092     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont },
1093     GNUNET_REST_HANDLER_END
1094   };
1095
1096   if (GNUNET_NO == GNUNET_JSONAPI_handle_request(conndata_handle,
1097                                                  handlers,
1098                                                  &err,
1099                                                  handle))
1100     {
1101       handle->response_code = err.error_code;
1102       GNUNET_SCHEDULER_add_now(&do_error, handle);
1103     }
1104 }
1105
1106
1107 /**
1108  * Entry point for the plugin.
1109  *
1110  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
1111  * @return NULL on error, otherwise the plugin context
1112  */
1113 void *
1114 libgnunet_plugin_rest_credential_init(void *cls)
1115 {
1116   static struct Plugin plugin;
1117
1118   cfg = cls;
1119   struct GNUNET_REST_Plugin *api;
1120
1121   if (NULL != plugin.cfg)
1122     return NULL;                /* can only initialize once! */
1123   memset(&plugin, 0, sizeof(struct Plugin));
1124   plugin.cfg = cfg;
1125   api = GNUNET_new(struct GNUNET_REST_Plugin);
1126   api->cls = &plugin;
1127   api->name = GNUNET_REST_API_NS_CREDENTIAL;
1128   api->process_request = &rest_credential_process_request;
1129   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1130              _("GNS REST API initialized\n"));
1131   return api;
1132 }
1133
1134
1135 /**
1136  * Exit point from the plugin.
1137  *
1138  * @param cls the plugin context (as returned by "init")
1139  * @return always NULL
1140  */
1141 void *
1142 libgnunet_plugin_rest_credential_done(void *cls)
1143 {
1144   struct GNUNET_REST_Plugin *api = cls;
1145   struct Plugin *plugin = api->cls;
1146
1147   plugin->cfg = NULL;
1148   GNUNET_free(api);
1149   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1150              "GNS REST plugin is finished\n");
1151   return NULL;
1152 }
1153
1154 /* end of plugin_rest_gns.c */