2 This file is part of GNUnet.
3 Copyright (C) 2012-2016 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @author Martin Schanzenbach
22 * @file gns/plugin_rest_credential.c
23 * @brief GNUnet CREDENTIAL REST plugin
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>
38 #define GNUNET_REST_API_NS_CREDENTIAL "/credential"
40 #define GNUNET_REST_API_NS_CREDENTIAL_ISSUE "/credential/issue"
42 #define GNUNET_REST_API_NS_CREDENTIAL_VERIFY "/credential/verify"
44 #define GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION "expiration"
46 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY "subject_key"
48 #define GNUNET_REST_JSONAPI_CREDENTIAL "credential"
50 #define GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO "credential"
52 #define GNUNET_REST_JSONAPI_DELEGATIONS "delegations"
54 #define GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR "attribute"
56 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR "credential"
59 * @brief struct returned by the initialization function of the plugin
63 const struct GNUNET_CONFIGURATION_Handle *cfg;
66 const struct GNUNET_CONFIGURATION_Handle *cfg;
71 * Handle to Credential service.
73 struct GNUNET_CREDENTIAL_Handle *credential;
76 * Handle to lookup request
78 struct GNUNET_CREDENTIAL_Request *verify_request;
81 * Handle to issue request
83 struct GNUNET_CREDENTIAL_Request *issue_request;
88 struct GNUNET_IDENTITY_Handle *identity;
91 * Handle to identity operation
93 struct GNUNET_IDENTITY_Operation *id_op;
96 * Handle to rest request
98 struct GNUNET_REST_RequestHandle *rest_handle;
101 * ID of a task associated with the resolution process.
103 struct GNUNET_SCHEDULER_Task * timeout_task;
106 * The root of the received JSON or NULL
111 * The plugin result processor
113 GNUNET_REST_ResultProcessor proc;
116 * The closure of the result processor
121 * The issuer attribute to verify
126 * The subject attribute
131 * The public key of the issuer
133 struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
136 * The public key of the subject
138 struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
148 struct GNUNET_TIME_Relative timeout;
154 * Cleanup lookup handle.
156 * @param handle Handle to clean up
159 cleanup_handle (struct RequestHandle *handle)
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163 if (NULL != handle->json_root)
164 json_decref (handle->json_root);
166 if (NULL != handle->issuer_attr)
167 GNUNET_free (handle->issuer_attr);
168 if (NULL != handle->subject_attr)
169 GNUNET_free (handle->subject_attr);
170 if (NULL != handle->verify_request)
171 GNUNET_CREDENTIAL_verify_cancel (handle->verify_request);
172 if (NULL != handle->credential)
173 GNUNET_CREDENTIAL_disconnect (handle->credential);
174 if (NULL != handle->id_op)
175 GNUNET_IDENTITY_cancel (handle->id_op);
176 if (NULL != handle->identity)
177 GNUNET_IDENTITY_disconnect (handle->identity);
178 if (NULL != handle->timeout_task)
180 GNUNET_SCHEDULER_cancel (handle->timeout_task);
182 GNUNET_free (handle);
187 * Task run on shutdown. Cleans up everything.
190 * @param tc scheduler context
195 struct RequestHandle *handle = cls;
196 struct MHD_Response *resp;
198 resp = GNUNET_REST_create_response (NULL);
199 handle->proc (handle->proc_cls, resp, handle->response_code);
200 cleanup_handle (handle);
204 * Attribute delegation to JSON
205 * @param attr the attribute
206 * @return JSON, NULL if failed
209 attribute_delegation_to_json (struct GNUNET_CREDENTIAL_Delegation *delegation_chain_entry)
215 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&delegation_chain_entry->issuer_key);
218 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
219 "Issuer in delegation malformed\n");
222 subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&delegation_chain_entry->subject_key);
225 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
226 "Subject in credential malformed\n");
227 GNUNET_free (issuer);
230 attr_obj = json_object ();
232 json_object_set_new (attr_obj, "issuer", json_string (issuer));
233 json_object_set_new (attr_obj, "issuer_attribute",
234 json_string (delegation_chain_entry->issuer_attribute));
236 json_object_set_new (attr_obj, "subject", json_string (subject));
237 if (0 < delegation_chain_entry->subject_attribute_len)
239 json_object_set_new (attr_obj, "subject_attribute",
240 json_string (delegation_chain_entry->subject_attribute));
242 GNUNET_free (issuer);
243 GNUNET_free (subject);
249 * @param cred the credential
250 * @return the resulting json, NULL if failed
253 credential_to_json (struct GNUNET_CREDENTIAL_Credential *cred)
257 char attribute[cred->issuer_attribute_len + 1];
260 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264 "Issuer in credential malformed\n");
267 subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
271 "Subject in credential malformed\n");
272 GNUNET_free (issuer);
276 cred->issuer_attribute,
277 cred->issuer_attribute_len);
278 attribute[cred->issuer_attribute_len] = '\0';
279 cred_obj = json_object ();
280 json_object_set_new (cred_obj, "issuer", json_string (issuer));
281 json_object_set_new (cred_obj, "subject", json_string (subject));
282 json_object_set_new (cred_obj, "attribute", json_string (attribute));
283 GNUNET_free (issuer);
284 GNUNET_free (subject);
289 * Function called with the result of a Credential lookup.
291 * @param cls the 'const char *' name that was resolved
292 * @param cd_count number of records returned
293 * @param cd array of @a cd_count records with the results
296 handle_verify_response (void *cls,
297 unsigned int d_count,
298 struct GNUNET_CREDENTIAL_Delegation *delegation_chain,
299 unsigned int c_count,
300 struct GNUNET_CREDENTIAL_Credential *cred)
303 struct RequestHandle *handle = cls;
304 struct MHD_Response *resp;
305 struct GNUNET_JSONAPI_Document *json_document;
306 struct GNUNET_JSONAPI_Resource *json_resource;
316 handle->verify_request = NULL;
318 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
320 handle->response_code = MHD_HTTP_NOT_FOUND;
321 GNUNET_SCHEDULER_add_now (&do_error, handle);
324 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&handle->issuer_key);
327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
328 "Issuer in delegation malformed\n");
331 GNUNET_asprintf (&id,
334 handle->issuer_attr);
335 GNUNET_free (issuer);
336 json_document = GNUNET_JSONAPI_document_new ();
337 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
340 attr_array = json_array ();
341 for (i = 0; i < d_count; i++)
343 attr_obj = attribute_delegation_to_json (&delegation_chain[i]);
344 json_array_append_new (attr_array, attr_obj);
346 cred_array = json_array ();
347 for (i=0;i<c_count;i++)
349 cred_obj = credential_to_json (&cred[i]);
350 json_array_append_new (cred_array, cred_obj);
352 GNUNET_JSONAPI_resource_add_attr (json_resource,
353 GNUNET_REST_JSONAPI_CREDENTIAL,
355 GNUNET_JSONAPI_resource_add_attr (json_resource,
356 GNUNET_REST_JSONAPI_DELEGATIONS,
358 GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
359 GNUNET_JSONAPI_document_serialize (json_document, &result);
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363 json_decref (attr_array);
364 json_decref (cred_array);
365 GNUNET_JSONAPI_document_delete (json_document);
366 resp = GNUNET_REST_create_response (result);
367 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
368 GNUNET_free (result);
369 cleanup_handle (handle);
374 verify_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
378 struct RequestHandle *handle = cls;
379 struct GNUNET_HashCode key;
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385 handle->credential = GNUNET_CREDENTIAL_connect (cfg);
386 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390 if (NULL == handle->credential)
392 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 "Connecting to CREDENTIAL failed\n");
394 GNUNET_SCHEDULER_add_now (&do_error, handle);
397 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
398 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
401 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
405 "Missing issuer attribute\n");
406 GNUNET_SCHEDULER_add_now (&do_error, handle);
409 tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
411 entity_attr = GNUNET_strdup (tmp);
412 tmp = strtok(entity_attr, ".");
415 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
416 "Malformed issuer or attribute\n");
417 GNUNET_free (entity_attr);
418 GNUNET_SCHEDULER_add_now (&do_error, handle);
422 GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
424 &handle->issuer_key))
426 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
427 "Malformed issuer key\n");
428 GNUNET_free (entity_attr);
429 GNUNET_SCHEDULER_add_now (&do_error, handle);
432 tmp = strtok (NULL, "."); //Issuer attribute
435 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
436 "Malformed attribute\n");
437 GNUNET_free (entity_attr);
438 GNUNET_SCHEDULER_add_now (&do_error, handle);
441 handle->issuer_attr = GNUNET_strdup (tmp);
442 GNUNET_free (entity_attr);
444 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR,
445 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR),
448 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
451 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
452 "Missing subject or attribute\n");
453 GNUNET_free (entity_attr);
454 GNUNET_SCHEDULER_add_now (&do_error, handle);
457 tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
459 entity_attr = GNUNET_strdup (tmp);
460 tmp = strtok(entity_attr, ".");
463 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
464 "Malformed subject\n");
465 GNUNET_free (entity_attr);
466 GNUNET_SCHEDULER_add_now (&do_error, handle);
470 GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
472 &handle->subject_key)) {
473 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
474 "Malformed subject key\n");
475 GNUNET_free (entity_attr);
476 GNUNET_SCHEDULER_add_now (&do_error, handle);
479 tmp = strtok (NULL, ".");
482 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
483 "Malformed subject attribute\n");
484 GNUNET_free (entity_attr);
485 GNUNET_SCHEDULER_add_now (&do_error, handle);
488 handle->subject_attr = GNUNET_strdup (tmp);
489 GNUNET_free (entity_attr);
491 handle->verify_request = GNUNET_CREDENTIAL_verify (handle->credential,
494 &handle->subject_key,
496 NULL,//TODOhandle->subject_attr,
497 &handle_verify_response,
503 send_cred_response (struct RequestHandle *handle,
504 struct GNUNET_CREDENTIAL_Credential *cred)
506 struct MHD_Response *resp;
507 struct GNUNET_JSONAPI_Document *json_document;
508 struct GNUNET_JSONAPI_Resource *json_resource;
516 GNUNET_assert (NULL != cred);
517 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
520 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
521 "Subject malformed\n");
524 GNUNET_asprintf (&id,
528 subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
532 "Subject malformed\n");
535 GNUNET_STRINGS_base64_encode ((char*)&cred->signature,
536 sizeof (struct GNUNET_CRYPTO_EcdsaSignature),
538 json_document = GNUNET_JSONAPI_document_new ();
539 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
542 cred_obj = json_object();
543 json_object_set_new (cred_obj, "issuer", json_string (issuer));
544 json_object_set_new (cred_obj, "subject", json_string (subject));
545 json_object_set_new (cred_obj, "expiration", json_integer( cred->expiration.abs_value_us));
546 json_object_set_new (cred_obj, "signature", json_string (signature));
547 GNUNET_JSONAPI_resource_add_attr (json_resource,
548 GNUNET_REST_JSONAPI_CREDENTIAL,
550 GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
551 GNUNET_JSONAPI_document_serialize (json_document, &result);
552 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 json_decref (cred_obj);
556 GNUNET_JSONAPI_document_delete (json_document);
557 resp = GNUNET_REST_create_response (result);
558 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
559 GNUNET_free (result);
560 GNUNET_free (signature);
561 GNUNET_free (issuer);
562 GNUNET_free (subject);
563 cleanup_handle (handle);
567 get_cred_issuer_cb (void *cls,
568 struct GNUNET_IDENTITY_Ego *ego,
572 struct RequestHandle *handle = cls;
573 struct GNUNET_TIME_Absolute etime_abs;
574 struct GNUNET_TIME_Relative etime_rel;
575 const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer_key;
576 struct GNUNET_HashCode key;
577 struct GNUNET_CREDENTIAL_Credential *cred;
578 char* expiration_str;
581 handle->id_op = NULL;
585 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586 "Issuer not configured!\n");
587 GNUNET_SCHEDULER_add_now (&do_error, handle);
591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592 "Connecting to credential service...\n");
593 handle->credential = GNUNET_CREDENTIAL_connect (cfg);
594 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
596 if (NULL == handle->credential)
598 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
599 "Connecting to CREDENTIAL failed\n");
600 GNUNET_SCHEDULER_add_now (&do_error, handle);
603 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION,
604 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION),
607 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
610 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
611 "Missing expiration\n");
612 GNUNET_SCHEDULER_add_now (&do_error, handle);
615 expiration_str = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
617 if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative (expiration_str,
620 etime_abs = GNUNET_TIME_relative_to_absolute (etime_rel);
621 } else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (expiration_str,
624 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
625 "Malformed expiration: %s\n", expiration_str);
626 GNUNET_SCHEDULER_add_now (&do_error, handle);
629 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
630 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
633 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
636 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
637 "Missing issuer attribute\n");
638 GNUNET_SCHEDULER_add_now (&do_error, handle);
641 handle->issuer_attr = GNUNET_strdup(GNUNET_CONTAINER_multihashmap_get
642 (handle->rest_handle->url_param_map,
644 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY,
645 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY),
648 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
651 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
652 "Missing subject\n");
653 GNUNET_SCHEDULER_add_now (&do_error, handle);
656 tmp = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
660 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
661 "Malformed subject\n");
662 GNUNET_SCHEDULER_add_now (&do_error, handle);
666 GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
668 &handle->subject_key)) {
669 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
670 "Malformed subject key\n");
671 GNUNET_SCHEDULER_add_now (&do_error, handle);
674 issuer_key = GNUNET_IDENTITY_ego_get_private_key (ego);
675 cred = GNUNET_CREDENTIAL_credential_issue (issuer_key,
676 &handle->subject_key,
681 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
682 "Failed to create credential\n");
683 GNUNET_SCHEDULER_add_now (&do_error, handle);
686 send_cred_response (handle, cred);
691 issue_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
695 struct RequestHandle *handle = cls;
697 handle->identity = GNUNET_IDENTITY_connect (cfg,
700 handle->id_op = GNUNET_IDENTITY_get(handle->identity,
704 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
710 * Handle rest request
712 * @param handle the lookup handle
715 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
719 struct MHD_Response *resp;
720 struct RequestHandle *handle = cls;
722 //For GNS, independent of path return all options
723 resp = GNUNET_REST_create_response (NULL);
724 MHD_add_response_header (resp,
725 "Access-Control-Allow-Methods",
726 MHD_HTTP_METHOD_GET);
727 handle->proc (handle->proc_cls,
730 cleanup_handle (handle);
735 * Function processing the REST call
737 * @param method HTTP method
738 * @param url URL of the HTTP request
739 * @param data body of the HTTP request (optional)
740 * @param data_size length of the body
741 * @param proc callback function for the result
742 * @param proc_cls closure for callback function
743 * @return GNUNET_OK if request accepted
746 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
747 GNUNET_REST_ResultProcessor proc,
750 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
751 struct GNUNET_REST_RequestHandlerError err;
753 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
754 handle->proc_cls = proc_cls;
756 handle->rest_handle = conndata_handle;
758 static const struct GNUNET_REST_RequestHandler handlers[] = {
759 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_VERIFY, &verify_cred_cont},
760 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_ISSUE, &issue_cred_cont},
761 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont},
762 GNUNET_REST_HANDLER_END
765 if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
770 handle->response_code = err.error_code;
771 GNUNET_SCHEDULER_add_now (&do_error, handle);
777 * Entry point for the plugin.
779 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
780 * @return NULL on error, otherwise the plugin context
783 libgnunet_plugin_rest_credential_init (void *cls)
785 static struct Plugin plugin;
787 struct GNUNET_REST_Plugin *api;
789 if (NULL != plugin.cfg)
790 return NULL; /* can only initialize once! */
791 memset (&plugin, 0, sizeof (struct Plugin));
793 api = GNUNET_new (struct GNUNET_REST_Plugin);
795 api->name = GNUNET_REST_API_NS_CREDENTIAL;
796 api->process_request = &rest_credential_process_request;
797 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
798 _("GNS REST API initialized\n"));
804 * Exit point from the plugin.
806 * @param cls the plugin context (as returned by "init")
807 * @return always NULL
810 libgnunet_plugin_rest_credential_done (void *cls)
812 struct GNUNET_REST_Plugin *api = cls;
813 struct Plugin *plugin = api->cls;
817 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
818 "GNS REST plugin is finished\n");
822 /* end of plugin_rest_gns.c */