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