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