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,
495 handle->subject_attr,
496 &handle_verify_response,
502 send_cred_response (struct RequestHandle *handle,
503 struct GNUNET_CREDENTIAL_Credential *cred)
505 struct MHD_Response *resp;
506 struct GNUNET_JSONAPI_Document *json_document;
507 struct GNUNET_JSONAPI_Resource *json_resource;
515 GNUNET_assert (NULL != cred);
516 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
519 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
520 "Subject malformed\n");
523 GNUNET_asprintf (&id,
527 subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
530 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
531 "Subject malformed\n");
534 GNUNET_STRINGS_base64_encode ((char*)&cred->signature,
535 sizeof (struct GNUNET_CRYPTO_EcdsaSignature),
537 json_document = GNUNET_JSONAPI_document_new ();
538 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
541 cred_obj = json_object();
542 json_object_set_new (cred_obj, "issuer", json_string (issuer));
543 json_object_set_new (cred_obj, "subject", json_string (subject));
544 json_object_set_new (cred_obj, "expiration", json_integer( cred->expiration.abs_value_us));
545 json_object_set_new (cred_obj, "signature", json_string (signature));
546 GNUNET_JSONAPI_resource_add_attr (json_resource,
547 GNUNET_REST_JSONAPI_CREDENTIAL,
549 GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
550 GNUNET_JSONAPI_document_serialize (json_document, &result);
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554 json_decref (cred_obj);
555 GNUNET_JSONAPI_document_delete (json_document);
556 resp = GNUNET_REST_create_response (result);
557 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
558 GNUNET_free (result);
559 GNUNET_free (signature);
560 GNUNET_free (issuer);
561 GNUNET_free (subject);
562 cleanup_handle (handle);
566 get_cred_issuer_cb (void *cls,
567 struct GNUNET_IDENTITY_Ego *ego,
571 struct RequestHandle *handle = cls;
572 struct GNUNET_TIME_Absolute etime_abs;
573 struct GNUNET_TIME_Relative etime_rel;
574 const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer_key;
575 struct GNUNET_HashCode key;
576 struct GNUNET_CREDENTIAL_Credential *cred;
577 char* expiration_str;
580 handle->id_op = NULL;
584 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
585 "Issuer not configured!\n");
586 GNUNET_SCHEDULER_add_now (&do_error, handle);
590 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591 "Connecting to credential service...\n");
592 handle->credential = GNUNET_CREDENTIAL_connect (cfg);
593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595 if (NULL == handle->credential)
597 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598 "Connecting to CREDENTIAL failed\n");
599 GNUNET_SCHEDULER_add_now (&do_error, handle);
602 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION,
603 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION),
606 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
609 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
610 "Missing expiration\n");
611 GNUNET_SCHEDULER_add_now (&do_error, handle);
614 expiration_str = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
616 if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative (expiration_str,
619 etime_abs = GNUNET_TIME_relative_to_absolute (etime_rel);
620 } else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (expiration_str,
623 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
624 "Malformed expiration: %s\n", expiration_str);
625 GNUNET_SCHEDULER_add_now (&do_error, handle);
628 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
629 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
632 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
635 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
636 "Missing issuer attribute\n");
637 GNUNET_SCHEDULER_add_now (&do_error, handle);
640 handle->issuer_attr = GNUNET_strdup(GNUNET_CONTAINER_multihashmap_get
641 (handle->rest_handle->url_param_map,
643 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY,
644 strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY),
647 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651 "Missing subject\n");
652 GNUNET_SCHEDULER_add_now (&do_error, handle);
655 tmp = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
659 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
660 "Malformed subject\n");
661 GNUNET_SCHEDULER_add_now (&do_error, handle);
665 GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
667 &handle->subject_key)) {
668 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
669 "Malformed subject key\n");
670 GNUNET_SCHEDULER_add_now (&do_error, handle);
673 issuer_key = GNUNET_IDENTITY_ego_get_private_key (ego);
674 cred = GNUNET_CREDENTIAL_credential_issue (issuer_key,
675 &handle->subject_key,
680 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
681 "Failed to create credential\n");
682 GNUNET_SCHEDULER_add_now (&do_error, handle);
685 send_cred_response (handle, cred);
690 issue_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
694 struct RequestHandle *handle = cls;
696 handle->identity = GNUNET_IDENTITY_connect (cfg,
699 handle->id_op = GNUNET_IDENTITY_get(handle->identity,
703 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
709 * Handle rest request
711 * @param handle the lookup handle
714 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
718 struct MHD_Response *resp;
719 struct RequestHandle *handle = cls;
721 //For GNS, independent of path return all options
722 resp = GNUNET_REST_create_response (NULL);
723 MHD_add_response_header (resp,
724 "Access-Control-Allow-Methods",
725 MHD_HTTP_METHOD_GET);
726 handle->proc (handle->proc_cls,
729 cleanup_handle (handle);
734 * Function processing the REST call
736 * @param method HTTP method
737 * @param url URL of the HTTP request
738 * @param data body of the HTTP request (optional)
739 * @param data_size length of the body
740 * @param proc callback function for the result
741 * @param proc_cls closure for callback function
742 * @return GNUNET_OK if request accepted
745 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
746 GNUNET_REST_ResultProcessor proc,
749 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
750 struct GNUNET_REST_RequestHandlerError err;
752 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
753 handle->proc_cls = proc_cls;
755 handle->rest_handle = conndata_handle;
757 static const struct GNUNET_REST_RequestHandler handlers[] = {
758 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_VERIFY, &verify_cred_cont},
759 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_ISSUE, &issue_cred_cont},
760 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont},
761 GNUNET_REST_HANDLER_END
764 if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
769 handle->response_code = err.error_code;
770 GNUNET_SCHEDULER_add_now (&do_error, handle);
776 * Entry point for the plugin.
778 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
779 * @return NULL on error, otherwise the plugin context
782 libgnunet_plugin_rest_credential_init (void *cls)
784 static struct Plugin plugin;
786 struct GNUNET_REST_Plugin *api;
788 if (NULL != plugin.cfg)
789 return NULL; /* can only initialize once! */
790 memset (&plugin, 0, sizeof (struct Plugin));
792 api = GNUNET_new (struct GNUNET_REST_Plugin);
794 api->name = GNUNET_REST_API_NS_CREDENTIAL;
795 api->process_request = &rest_credential_process_request;
796 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
797 _("GNS REST API initialized\n"));
803 * Exit point from the plugin.
805 * @param cls the plugin context (as returned by "init")
806 * @return always NULL
809 libgnunet_plugin_rest_credential_done (void *cls)
811 struct GNUNET_REST_Plugin *api = cls;
812 struct Plugin *plugin = api->cls;
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "GNS REST plugin is finished\n");
821 /* end of plugin_rest_gns.c */