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