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