/* This file is part of GNUnet. Copyright (C) 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @author Johannes Späth * @file escrow/escrow_api.c * * @brief api to interact with the escrow component */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_escrow_lib.h" #include "gnunet_escrow_plugin.h" #include "escrow.h" /** * Init canary for the plaintext plugin */ static int plaintext_initialized; /** * Init canary for the GNS plugin */ static int gns_initialized; /** * Init canary for the Anastasis plugin */ static int anastasis_initialized; /** * Plaintext method string */ static const char *plaintext_string = "plaintext"; /** * GNS method string */ static const char *gns_string = "gns"; /** * Anastasis method string */ static const char *anastasis_string = "anastasis"; /** * None method string */ static const char *none_string = "INVALID-METHOD"; /** * Pointer to the plaintext plugin API */ static struct GNUNET_ESCROW_KeyPluginFunctions *plaintext_api; /** * Pointer to the GNS plugin API */ static struct GNUNET_ESCROW_KeyPluginFunctions *gns_api; /** * Pointer to the Anastasis plugin API */ static struct GNUNET_ESCROW_KeyPluginFunctions *anastasis_api; /** * Initialize an escrow plugin * * @param method the escrow method determining the plugin * * @return pointer to the escrow plugin API */ static const struct GNUNET_ESCROW_KeyPluginFunctions * init_plugin (const struct GNUNET_ESCROW_Handle *h, enum GNUNET_ESCROW_Key_Escrow_Method method) { switch (method) { case GNUNET_ESCROW_KEY_PLAINTEXT: if (GNUNET_YES == plaintext_initialized) return plaintext_api; plaintext_initialized = GNUNET_YES; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading PLAINTEXT escrow plugin\n"); plaintext_api = GNUNET_PLUGIN_load ("libgnunet_plugin_escrow_plaintext", (void *)h->cfg); return plaintext_api; case GNUNET_ESCROW_KEY_GNS: if (GNUNET_YES == gns_initialized) return gns_api; gns_initialized = GNUNET_YES; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading GNS escrow plugin\n"); gns_api = GNUNET_PLUGIN_load ("libgnunet_plugin_escrow_gns", (void *)h->cfg); return gns_api; case GNUNET_ESCROW_KEY_ANASTASIS: if (GNUNET_YES == anastasis_initialized) return anastasis_api; anastasis_initialized = GNUNET_YES; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading ANASTASIS escrow plugin\n"); anastasis_api = GNUNET_PLUGIN_load ("libgnunet_plugin_escrow_anastasis", (void *)h->cfg); return anastasis_api; case GNUNET_ESCROW_KEY_NONE: // error case fprintf (stderr, "incorrect escrow method!\n"); return NULL; } // should never be reached return NULL; } /** * Get a fresh operation id to distinguish between escrow operations * * @param h the escrow handle * * @return next operation id to use */ static uint32_t get_op_id (struct GNUNET_ESCROW_Handle *h) { return h->last_op_id_used++; } /** * Initialize the escrow component. * * @param cfg the configuration to use * * @return handle to use */ struct GNUNET_ESCROW_Handle * GNUNET_ESCROW_init (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_ESCROW_Handle *h; h = GNUNET_new (struct GNUNET_ESCROW_Handle); h->cfg = GNUNET_CONFIGURATION_dup (cfg); h->op_head = NULL; h->op_tail = NULL; return h; } /** * Unload all loaded plugins on destruction. * * @param h the escrow handle */ void GNUNET_ESCROW_fini (struct GNUNET_ESCROW_Handle *h) { struct GNUNET_ESCROW_Operation *op; /* unload all loaded plugins */ if (GNUNET_YES == plaintext_initialized) { plaintext_initialized = GNUNET_NO; GNUNET_break (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_escrow_plaintext", plaintext_api)); plaintext_api = NULL; } if (GNUNET_YES == gns_initialized) { gns_initialized = GNUNET_NO; GNUNET_break (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_escrow_gns", gns_api)); gns_api = NULL; } if (GNUNET_YES == anastasis_initialized) { anastasis_initialized = GNUNET_NO; GNUNET_break (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_escrow_anastasis", anastasis_api)); anastasis_api = NULL; } /* clean up the operation DLL */ while (NULL != (op = h->op_head)) { GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op); GNUNET_ESCROW_cancel (op); } /* free the configuration */ GNUNET_free (h->cfg); /* free the escrow handle */ GNUNET_free (h); } static void handle_start_escrow_result (void *cls) { struct ESCROW_Plugin_AnchorContinuationWrapper *w = cls; struct GNUNET_ESCROW_Operation *op; for (op = w->h->op_head; NULL != op; op = op->next) if (op->id == w->op_id) break; if (NULL == op) { GNUNET_break (0); return; } GNUNET_CONTAINER_DLL_remove (w->h->op_head, w->h->op_tail, op); if (NULL != op->cb_put) op->cb_put (op->cb_cls, w->anchor, w->emsg); GNUNET_free (op); } /** * Put some data in escrow using the specified escrow method * * @param h the handle for the escrow component * @param ego the identity ego to put in escrow * @param userSecret the user secret (e.g. for derivation of escrow identities) * for GNS escrow, this has to be UNIQUE in the whole network! * @param method the escrow method to use * @param cb function to call with the escrow anchor on completion * @param cb_cls closure for @a cb * * @return handle to abort the operation */ struct GNUNET_ESCROW_Operation * GNUNET_ESCROW_put (struct GNUNET_ESCROW_Handle *h, struct GNUNET_IDENTITY_Ego *ego, const char *userSecret, enum GNUNET_ESCROW_Key_Escrow_Method method, GNUNET_ESCROW_AnchorContinuation cb, void *cb_cls) { struct GNUNET_ESCROW_Operation *op; const struct GNUNET_ESCROW_KeyPluginFunctions *api; op = GNUNET_new (struct GNUNET_ESCROW_Operation); op->h = h; op->id = get_op_id (h); op->method = method; op->cb_put = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); api = init_plugin (h, method); op->plugin_op_wrap = api->start_key_escrow (h, ego, userSecret, &handle_start_escrow_result, op->id); return op; } static void handle_restore_key_result (void *cls) { struct ESCROW_Plugin_EgoContinuationWrapper *w = cls; struct GNUNET_ESCROW_Operation *op; for (op = w->h->op_head; NULL != op; op = op->next) if (op->id == w->op_id) break; if (NULL == op) { GNUNET_break (0); return; } GNUNET_CONTAINER_DLL_remove (w->h->op_head, w->h->op_tail, op); if (NULL != op->cb_get) op->cb_get (op->cb_cls, w->ego, w->emsg); GNUNET_free (op); } /** * Get the escrowed data back * * @param h the handle for the escrow component * @param anchor the escrow anchor returned by the GNUNET_ESCROW_put method * @param cb function to call with the restored ego on completion * @param cb_cls closure for @a cb * * @return handle to abort the operation */ struct GNUNET_ESCROW_Operation * GNUNET_ESCROW_get (struct GNUNET_ESCROW_Handle *h, const struct GNUNET_ESCROW_Anchor *anchor, GNUNET_ESCROW_EgoContinuation cb, void *cb_cls) { struct GNUNET_ESCROW_Operation *op; const struct GNUNET_ESCROW_KeyPluginFunctions *api; op = GNUNET_new (struct GNUNET_ESCROW_Operation); op->h = h; op->id = get_op_id (h); op->method = anchor->method; op->cb_get = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); api = init_plugin (h, anchor->method); op->plugin_op_wrap = api->restore_key (h, anchor, &handle_restore_key_result, op->id); return op; } static void handle_verify_escrow_result (void *cls) { struct ESCROW_Plugin_VerifyContinuationWrapper *w = cls; struct GNUNET_ESCROW_Operation *op; for (op = w->h->op_head; NULL != op; op = op->next) if (op->id == w->op_id) break; if (NULL == op) { GNUNET_break (0); return; } GNUNET_CONTAINER_DLL_remove (w->h->op_head, w->h->op_tail, op); if (NULL != op->cb_verify) op->cb_verify (op->cb_cls, w->verificationResult, w->emsg); GNUNET_free (op); } /** * Verify the escrowed data * * @param h the handle for the escrow component * @param ego the identity ego that was put into escrow * @param anchor the escrow anchor returned by the GNUNET_ESCROW_put method * @param method the escrow method to use * @param cb function to call with the verification result on completion * @param cb_cls closure for @a cb * * @return handle to abort the operation */ struct GNUNET_ESCROW_Operation * GNUNET_ESCROW_verify (struct GNUNET_ESCROW_Handle *h, struct GNUNET_IDENTITY_Ego *ego, const struct GNUNET_ESCROW_Anchor *anchor, enum GNUNET_ESCROW_Key_Escrow_Method method, GNUNET_ESCROW_VerifyContinuation cb, void *cb_cls) { struct GNUNET_ESCROW_Operation *op; const struct GNUNET_ESCROW_KeyPluginFunctions *api; op = GNUNET_new (struct GNUNET_ESCROW_Operation); op->h = h; op->id = get_op_id (h); op->method = method; op->cb_verify = cb; op->cb_cls = cb_cls; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); api = init_plugin (h, method); op->plugin_op_wrap = api->verify_key_escrow (h, ego, anchor, &handle_verify_escrow_result, op->id); return op; } /** * Get the status of an escrow, i.e. * -> when the last escrow was * -> when the next escrow is recommended * * @param h the handle for the escrow component * @param ego the identity ego of which the escrow status has to be determined * @param method the escrow method to use * * @return the status of the escrow packed into a GNUNET_ESCROW_Status struct */ struct GNUNET_ESCROW_Status * GNUNET_ESCROW_get_status (struct GNUNET_ESCROW_Handle *h, struct GNUNET_IDENTITY_Ego *ego, enum GNUNET_ESCROW_Key_Escrow_Method method) { const struct GNUNET_ESCROW_KeyPluginFunctions *api; api = init_plugin (h, method); return api->get_status (h, ego); } /** * Deserialize an escrow anchor string (e.g. from command line) into a * GNUNET_ESCROW_Anchor struct * The anchor string is expected to have the following form: * :: * with , and being URL-encoded * * * @param anchorString the encoded escrow anchor string * * @return the deserialized data packed into a GNUNET_ESCROW_Anchor struct, * NULL if we failed to parse the string */ struct GNUNET_ESCROW_Anchor * GNUNET_ESCROW_anchor_string_to_data (const char *anchorString) { struct GNUNET_ESCROW_Anchor *anchor; uint32_t data_size; char *anchorStringCopy, *ptr; char *methodString, *egoNameString, *anchorDataString; char delimiter[] = ":"; anchorStringCopy = GNUNET_strdup (anchorString); anchor = NULL; /* parse and decode method */ ptr = strtok (anchorStringCopy, delimiter); if (NULL == ptr) goto END; GNUNET_STRINGS_urldecode (ptr, strlen (ptr), &methodString); /* parse and decode ego name */ ptr = strtok (NULL, delimiter); if (NULL == ptr) goto END; GNUNET_STRINGS_urldecode (ptr, strlen (ptr), &egoNameString); /* parse and decode anchor data */ ptr = strtok (NULL, delimiter); if (NULL == ptr) goto END; GNUNET_STRINGS_urldecode (ptr, strlen (ptr), &anchorDataString); /* check if string is over */ ptr = strtok (NULL, delimiter); if (NULL != ptr) goto END; data_size = strlen (anchorDataString); // data is NOT null-terminated anchor = GNUNET_malloc (sizeof (struct GNUNET_ESCROW_Anchor) + data_size + strlen (egoNameString) + 1); anchor->size = data_size; anchor->method = GNUNET_ESCROW_method_string_to_number (methodString); // ptr is now used to fill the anchor ptr = (char *)&anchor[1]; strcpy (ptr, anchorDataString); ptr += data_size; anchor->egoName = ptr; strcpy (ptr, egoNameString); END: /* free all non-NULL strings */ if (NULL != anchorStringCopy) GNUNET_free (anchorStringCopy); if (NULL != methodString) GNUNET_free (methodString); if (NULL != egoNameString) GNUNET_free (egoNameString); if (NULL != anchorDataString) GNUNET_free (anchorDataString); return anchor; } /** * Serialize an escrow anchor (struct GNUNET_ESCROW_Anchor) into a string * * @param anchor the escrow anchor struct * * @return the encoded escrow anchor string */ char * GNUNET_ESCROW_anchor_data_to_string (const struct GNUNET_ESCROW_Anchor *anchor) { char *anchorString, *ptr; const char *methodString, *egoNameString, *anchorData; char *methodStringEnc, *egoNameStringEnc, *anchorDataEnc; methodString = GNUNET_ESCROW_method_number_to_string (anchor->method); GNUNET_STRINGS_urlencode (methodString, strlen (methodString), &methodStringEnc); egoNameString = anchor->egoName; GNUNET_STRINGS_urlencode (egoNameString, strlen (egoNameString), &egoNameStringEnc); anchorData = (const char *)&anchor[1]; GNUNET_STRINGS_urlencode (anchorData, anchor->size, &anchorDataEnc); anchorString = GNUNET_malloc (strlen (methodStringEnc) + 1 + strlen (egoNameStringEnc) + 1 + strlen (anchorDataEnc) + 1); ptr = anchorString; GNUNET_memcpy (ptr, methodStringEnc, strlen (methodStringEnc)); ptr += strlen (methodStringEnc); GNUNET_free (methodStringEnc); *(ptr++) = ':'; GNUNET_memcpy (ptr, egoNameStringEnc, strlen (egoNameStringEnc)); ptr += strlen (egoNameStringEnc); GNUNET_free (egoNameStringEnc); *(ptr++) = ':'; GNUNET_memcpy (ptr, anchorDataEnc, strlen (anchorDataEnc)); ptr += strlen (anchorDataEnc); GNUNET_free (anchorDataEnc); *(ptr++) = '\0'; return anchorString; } /** * Convert a method name string to the respective enum number * * @param methodString the method name string * * @return the enum number */ enum GNUNET_ESCROW_Key_Escrow_Method GNUNET_ESCROW_method_string_to_number (const char *methodString) { if (!strcmp (plaintext_string, methodString)) return GNUNET_ESCROW_KEY_PLAINTEXT; else if (!strcmp (gns_string, methodString)) return GNUNET_ESCROW_KEY_GNS; else if (!strcmp (anastasis_string, methodString)) return GNUNET_ESCROW_KEY_ANASTASIS; else return GNUNET_ESCROW_KEY_NONE; } /** * Convert a method enum number to the respective method string * * @param method the method enum number * * @return the method string */ const char * GNUNET_ESCROW_method_number_to_string (enum GNUNET_ESCROW_Key_Escrow_Method method) { switch (method) { case GNUNET_ESCROW_KEY_PLAINTEXT: return plaintext_string; case GNUNET_ESCROW_KEY_GNS: return gns_string; case GNUNET_ESCROW_KEY_ANASTASIS: return anastasis_string; default: return none_string; } } /** * Cancel an escrow operation. Note that the operation MAY still * be executed; this merely cancels the continuation. * * @param op operation to cancel */ void GNUNET_ESCROW_cancel (struct GNUNET_ESCROW_Operation *op) { const struct GNUNET_ESCROW_KeyPluginFunctions *api; api = init_plugin (op->h, op->method); api->cancel_plugin_operation (op->plugin_op_wrap); // TODO: check which callback is not NULL? op->cb_put = NULL; op->cb_verify = NULL; op->cb_get = NULL; GNUNET_free (op); } /* end of escrow_api.c */