/*
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
*/
/**
* @file escrow/plugin_escrow_plaintext.c
* @brief escrow-plugin-plaintext escrow plugin for plaintext escrow of the key
*
* @author Johannes Späth
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_escrow_plugin.h"
#include "escrow_plugin_helper.h"
#include "gnunet_identity_service.h"
#include "escrow.h"
#include
struct ESCROW_PlaintextPluginOperation
{
/**
* Handle for the escrow component
*/
struct GNUNET_ESCROW_Handle *h;
/**
* Scheduler task the SCHEDULE operation returns (needed for cancellation)
*/
struct GNUNET_SCHEDULER_Task *sched_task;
/**
* Identity operation
*/
struct GNUNET_IDENTITY_Operation *id_op;
/**
* Private key of the created ego
*/
struct GNUNET_CRYPTO_EcdsaPrivateKey pk;
/**
* The ego
*/
struct GNUNET_IDENTITY_Ego *ego;
/**
* The anchor
*/
const struct GNUNET_ESCROW_Anchor *anchor;
/**
* Name of the ego
*/
char *egoName;
/**
* Continuation for a plugin operation (e.g. used for restore, as this
* callback has to be called from the IDENTITY service after finishing)
*/
ESCROW_Plugin_Continuation cont;
/**
* Ego continuation wrapper
*/
struct ESCROW_Plugin_EgoContinuationWrapper *ego_wrap;
/**
* Anchor continuation wrapper
*/
struct ESCROW_Plugin_AnchorContinuationWrapper *anchor_wrap;
/**
* Verify continuation wrapper
*/
struct ESCROW_Plugin_VerifyContinuationWrapper *verify_wrap;
};
/**
* Identity handle
*/
static struct GNUNET_IDENTITY_Handle *identity_handle;
/**
* Handle for the plugin instance
*/
static struct ESCROW_PluginHandle ph;
/**
* Clean up a plugin operation, i.e. remove it from the list and
* free the respective memory
*/
static void
cleanup_plugin_operation (struct ESCROW_PluginOperationWrapper *plugin_op_wrap)
{
struct ESCROW_PlaintextPluginOperation *p_op;
p_op = (struct ESCROW_PlaintextPluginOperation*)plugin_op_wrap->plugin_op;
GNUNET_CONTAINER_DLL_remove (ph.plugin_op_head,
ph.plugin_op_tail,
plugin_op_wrap);
if (NULL != p_op->anchor_wrap)
GNUNET_free (p_op->anchor_wrap);
if (NULL != p_op->ego_wrap)
GNUNET_free (p_op->ego_wrap);
if (NULL != p_op->verify_wrap)
GNUNET_free (p_op->verify_wrap);
if (NULL != p_op->egoName)
GNUNET_free (p_op->egoName);
GNUNET_free (p_op);
GNUNET_free (plugin_op_wrap);
}
static void
start_cont (void *cls)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
struct ESCROW_PlaintextPluginOperation *p_op;
p_op = (struct ESCROW_PlaintextPluginOperation*)plugin_op_wrap->plugin_op;
p_op->sched_task = NULL;
p_op->cont (p_op->anchor_wrap);
cleanup_plugin_operation (plugin_op_wrap);
}
static void
continue_start (void *cls)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
struct ESCROW_PlaintextPluginOperation *p_op;
struct GNUNET_TIME_Relative delay;
struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub;
struct EgoEntry *ego_entry;
char *pub_keystring;
const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk;
char *pkString;
p_op = (struct ESCROW_PlaintextPluginOperation *)plugin_op_wrap->plugin_op;
if (ESCROW_PLUGIN_STATE_POST_INIT != ph.state)
{
delay.rel_value_us = 100 * GNUNET_TIME_relative_get_millisecond_().rel_value_us;
GNUNET_SCHEDULER_add_delayed (delay, &continue_start, plugin_op_wrap);
return;
}
GNUNET_IDENTITY_ego_get_public_key (p_op->ego, &ego_pub);
pub_keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_pub);
for (ego_entry = ph.ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
if (0 == strcmp (pub_keystring, ego_entry->keystring))
break;
GNUNET_free (pub_keystring);
if (NULL == ego_entry)
{
p_op->anchor_wrap->anchor = NULL;
p_op->anchor_wrap->emsg = _ ("Identity was not found in plugin!\n");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
return;
}
p_op->egoName = GNUNET_strdup (ego_entry->identifier);
pk = GNUNET_IDENTITY_ego_get_private_key (p_op->ego);
pkString = GNUNET_CRYPTO_ecdsa_private_key_to_string (pk);
p_op->anchor_wrap->anchor = ESCROW_build_anchor (GNUNET_ESCROW_KEY_PLAINTEXT,
p_op->egoName,
pkString,
strlen (pkString));
/* update escrow status, i.e. set the last escrow method */
ESCROW_update_escrow_status_put (p_op->h, p_op->ego, "plaintext");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
}
/**
* Start the plaintext escrow of the key, i.e. simply hand out the key
*
* @param h the handle for the escrow component
* @param ego the identity ego containing the private key
* @param userSecret the user secret (e.g. for derivation of escrow identities)
* @param cb the function called upon completion
* @param op_id unique ID of the respective ESCROW_Operation
*
* @return plugin operation wrapper
*/
struct ESCROW_PluginOperationWrapper *
start_plaintext_key_escrow (struct GNUNET_ESCROW_Handle *h,
struct GNUNET_IDENTITY_Ego *ego,
const char *userSecret,
ESCROW_Plugin_Continuation cb,
uint32_t op_id)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
struct ESCROW_PlaintextPluginOperation *p_op;
struct ESCROW_Plugin_AnchorContinuationWrapper *w;
struct GNUNET_TIME_Relative delay;
// create a new plaintext plugin operation (in a wrapper) and insert it into the DLL
plugin_op_wrap = GNUNET_new (struct ESCROW_PluginOperationWrapper);
plugin_op_wrap->plugin_op = GNUNET_new (struct ESCROW_PlaintextPluginOperation);
GNUNET_CONTAINER_DLL_insert_tail (ph.plugin_op_head,
ph.plugin_op_tail,
plugin_op_wrap);
p_op = (struct ESCROW_PlaintextPluginOperation *)plugin_op_wrap->plugin_op;
p_op->h = h;
p_op->cont = cb;
p_op->ego = ego;
w = GNUNET_new (struct ESCROW_Plugin_AnchorContinuationWrapper);
w->h = h;
w->op_id = op_id;
p_op->anchor_wrap = w;
if (NULL == ego)
{
w->anchor = NULL;
w->emsg = _ ("ESCROW_put was called with ego == NULL!\n");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
return plugin_op_wrap;
}
if (ESCROW_PLUGIN_STATE_POST_INIT == ph.state)
{
continue_start (plugin_op_wrap);
}
else
{
delay.rel_value_us = 200 * GNUNET_TIME_relative_get_millisecond_().rel_value_us;
GNUNET_SCHEDULER_add_delayed (delay, &continue_start, plugin_op_wrap);
}
return plugin_op_wrap;
}
static void
verify_cont (void *cls)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
struct ESCROW_PlaintextPluginOperation *p_op;
p_op = (struct ESCROW_PlaintextPluginOperation*)plugin_op_wrap->plugin_op;
p_op->sched_task = NULL;
p_op->cont (p_op->verify_wrap);
cleanup_plugin_operation (plugin_op_wrap);
}
static void
continue_verify (void *cls)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
struct ESCROW_PlaintextPluginOperation *p_op;
struct GNUNET_TIME_Relative delay;
const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk;
char *pkString;
int verificationResult;
struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub;
struct EgoEntry *ego_entry;
char *pub_keystring;
p_op = (struct ESCROW_PlaintextPluginOperation *)plugin_op_wrap->plugin_op;
if (ESCROW_PLUGIN_STATE_POST_INIT != ph.state)
{
delay.rel_value_us = 100 * GNUNET_TIME_relative_get_millisecond_().rel_value_us;
GNUNET_SCHEDULER_add_delayed (delay, &continue_verify, plugin_op_wrap);
return;
}
GNUNET_IDENTITY_ego_get_public_key (p_op->ego, &ego_pub);
pub_keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_pub);
for (ego_entry = ph.ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
if (0 == strcmp (pub_keystring, ego_entry->keystring))
break;
GNUNET_free (pub_keystring);
if (NULL == ego_entry)
{
p_op->anchor_wrap->anchor = NULL;
p_op->anchor_wrap->emsg = _ ("Identity was not found in plugin!\n");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
return;
}
p_op->egoName = GNUNET_strdup (ego_entry->identifier);
if (0 != strcmp (p_op->egoName, p_op->anchor->egoName))
{
p_op->verify_wrap->verificationResult = GNUNET_ESCROW_INVALID;
p_op->verify_wrap->emsg = _ ("This anchor was not created when putting that ego in escrow!\n");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_cont, plugin_op_wrap);
return;
}
pk = GNUNET_IDENTITY_ego_get_private_key (p_op->ego);
pkString = GNUNET_CRYPTO_ecdsa_private_key_to_string (pk);
verificationResult = strncmp (pkString,
(char *)&p_op->anchor[1],
p_op->anchor->size) == 0 ?
GNUNET_ESCROW_VALID : GNUNET_ESCROW_INVALID;
/* update the escrow status if valid */
if (GNUNET_ESCROW_VALID == verificationResult)
ESCROW_update_escrow_status_verify (p_op->h, p_op->ego, "plaintext");
p_op->verify_wrap->verificationResult = verificationResult;
p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_cont, plugin_op_wrap);
}
/**
* Verify the plaintext escrow of the key
*
* @param h the handle for the escrow component
* @param ego the identity ego containing the private key
* @param anchor the escrow anchor needed to restore the key
* @param cb the function called upon completion
* @param op_id unique ID of the respective ESCROW_Operation
*
* @return plugin operation wrapper
*/
struct ESCROW_PluginOperationWrapper *
verify_plaintext_key_escrow (struct GNUNET_ESCROW_Handle *h,
struct GNUNET_IDENTITY_Ego *ego,
const struct GNUNET_ESCROW_Anchor *anchor,
ESCROW_Plugin_Continuation cb,
uint32_t op_id)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
struct ESCROW_PlaintextPluginOperation *p_op;
struct ESCROW_Plugin_VerifyContinuationWrapper *w;
struct GNUNET_TIME_Relative delay;
// create a new plaintext plugin operation (in a wrapper) and insert it into the DLL
plugin_op_wrap = GNUNET_new (struct ESCROW_PluginOperationWrapper);
plugin_op_wrap->plugin_op = GNUNET_new (struct ESCROW_PlaintextPluginOperation);
GNUNET_CONTAINER_DLL_insert_tail (ph.plugin_op_head,
ph.plugin_op_tail,
plugin_op_wrap);
p_op = (struct ESCROW_PlaintextPluginOperation *)plugin_op_wrap->plugin_op;
p_op->h = h;
p_op->cont = cb;
p_op->ego = ego;
p_op->anchor = anchor;
w = GNUNET_new (struct ESCROW_Plugin_VerifyContinuationWrapper);
w->h = h;
w->op_id = op_id;
p_op->verify_wrap = w;
if (NULL == ego)
{
w->verificationResult = GNUNET_ESCROW_INVALID;
w->emsg = _ ("ESCROW_verify was called with ego == NULL!\n");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_cont, plugin_op_wrap);
return plugin_op_wrap;
}
if (GNUNET_ESCROW_KEY_PLAINTEXT != anchor->method)
{
w->verificationResult = GNUNET_ESCROW_INVALID;
w->emsg = _ ("This anchor was not created using plaintext escrow!\n");
p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_cont, plugin_op_wrap);
return plugin_op_wrap;
}
if (ESCROW_PLUGIN_STATE_POST_INIT == ph.state)
{
continue_verify (plugin_op_wrap);
}
else
{
delay.rel_value_us = 200 * GNUNET_TIME_relative_get_millisecond_().rel_value_us;
GNUNET_SCHEDULER_add_delayed (delay, &continue_verify, plugin_op_wrap);
}
return plugin_op_wrap;
}
static void
ego_created (struct GNUNET_IDENTITY_Ego *ego)
{
struct ESCROW_PluginOperationWrapper *curr;
struct ESCROW_PlaintextPluginOperation *curr_p_op;
char *ego_pk_string, *curr_pk_string;
ego_pk_string = GNUNET_CRYPTO_ecdsa_private_key_to_string (
GNUNET_IDENTITY_ego_get_private_key(ego));
for (curr = ph.plugin_op_head; NULL != curr; curr = curr->next)
{
curr_p_op = (struct ESCROW_PlaintextPluginOperation *)curr->plugin_op;
curr_pk_string = GNUNET_CRYPTO_ecdsa_private_key_to_string (&curr_p_op->pk);
// compare the strings of the private keys
if (0 == strcmp (ego_pk_string, curr_pk_string))
{
// the ego was created due to a restore operation that is not yet finished
curr_p_op->ego_wrap->ego = ego;
curr_p_op->cont (curr_p_op->ego_wrap);
cleanup_plugin_operation (curr);
GNUNET_free (curr_pk_string);
GNUNET_free (ego_pk_string);
return;
}
GNUNET_free (curr_pk_string);
}
GNUNET_free (ego_pk_string);
}
/**
* Creation operation finished.
* This method only handles errors that may have occurred. On success,
* the callback is executed by the ESCROW_list_ego function, as the
* new ego is in our ego list only after ESCROW_list_ego has added it.
*
* @param cls pointer to operation handle
* @param pk private key of the ego, or NULL on error
* @param emsg error message, NULL on success
*/
static void
create_finished (void *cls,
const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk,
const char *emsg)
{
struct ESCROW_PlaintextPluginOperation *p_op = cls;
if (NULL == pk)
{
if (NULL != emsg)
{
fprintf (stderr,
"Identity create operation returned with error: %s\n",
emsg);
p_op->ego_wrap->emsg = _ ("Identity create failed!\n");
}
else
p_op->ego_wrap->emsg = _ ("Failed to create ego!\n");
p_op->ego_wrap->ego = NULL;
p_op->cont (p_op->ego_wrap);
return;
}
/* no error occurred, p_op->restore_cont will be called in ego_created, which
is called from ESCROW_list_ego after adding the new ego to our list */
p_op->pk = *pk;
}
static void
handle_restore_error (void *cls)
{
struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
struct ESCROW_PlaintextPluginOperation *p_op;
p_op = (struct ESCROW_PlaintextPluginOperation*)plugin_op_wrap->plugin_op;
p_op->sched_task = NULL;
p_op->cont (p_op->ego_wrap);
cleanup_plugin_operation (plugin_op_wrap);
}
/**
* Restore the key from plaintext escrow
*
* @param h the handle for the escrow component
* @param anchor the escrow anchor needed to restore the key
* @param cb the function called upon completion
* @param op_id unique ID of the respective ESCROW_Operation
*
* @return plugin operation wrapper
*/
struct ESCROW_PluginOperationWrapper *
restore_plaintext_key_escrow (struct GNUNET_ESCROW_Handle *h,
const struct GNUNET_ESCROW_Anchor *anchor,
ESCROW_Plugin_Continuation cb,
uint32_t op_id)
{
struct GNUNET_CRYPTO_EcdsaPrivateKey pk;
struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
struct ESCROW_PlaintextPluginOperation *p_op;
struct ESCROW_Plugin_EgoContinuationWrapper *w;
// create a new plaintext plugin operation (in a wrapper) and insert it into the DLL
plugin_op_wrap = GNUNET_new (struct ESCROW_PluginOperationWrapper);
plugin_op_wrap->plugin_op = GNUNET_new (struct ESCROW_PlaintextPluginOperation);
GNUNET_CONTAINER_DLL_insert_tail (ph.plugin_op_head,
ph.plugin_op_tail,
plugin_op_wrap);
p_op = (struct ESCROW_PlaintextPluginOperation *)plugin_op_wrap->plugin_op;
p_op->h = h;
// set cont here (has to be scheduled from the IDENTITY service when it finished)
p_op->cont = cb;
w = GNUNET_new (struct ESCROW_Plugin_EgoContinuationWrapper);
w->h = h;
w->op_id = op_id;
p_op->ego_wrap = w;
if (NULL == anchor)
{
w->ego = NULL;
w->emsg = _ ("ESCROW_get was called with anchor == NULL!\n");
// schedule handle_restore_error, which calls the callback and cleans up
p_op->sched_task = GNUNET_SCHEDULER_add_now (&handle_restore_error, plugin_op_wrap);
return plugin_op_wrap;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_private_key_from_string ((char *)&anchor[1],
anchor->size,
&pk))
{
w->ego = NULL;
w->emsg = _ ("Failed to create ECDSA private key from escrow anchor!\n");
// schedule handle_restore_error, which calls the callback and cleans up
p_op->sched_task = GNUNET_SCHEDULER_add_now (&handle_restore_error, plugin_op_wrap);
return plugin_op_wrap;
}
p_op->id_op = GNUNET_IDENTITY_create (identity_handle,
anchor->egoName,
&pk,
&create_finished,
p_op);
return plugin_op_wrap;
}
/**
* Get the status of a plaintext escrow
*
* @param h the handle for the escrow component
* @param ego the identity ego of which the status has to be obtained
*
* @return the status of the escrow packed into a GNUNET_ESCROW_Status struct
*/
struct GNUNET_ESCROW_Status *
plaintext_get_status (struct GNUNET_ESCROW_Handle *h,
struct GNUNET_IDENTITY_Ego *ego)
{
return ESCROW_get_escrow_status (h, ego);
}
/**
* Cancel a plaintext plugin operation.
*
* @param plugin_op_wrap the plugin operation wrapper containing the operation
*/
void
cancel_plaintext_operation (struct ESCROW_PluginOperationWrapper *plugin_op_wrap)
{
struct ESCROW_PluginOperationWrapper *curr;
struct ESCROW_PlaintextPluginOperation *plugin_op;
for (curr = ph.plugin_op_head; NULL != curr; curr = curr->next)
{
if (curr == plugin_op_wrap)
{
GNUNET_CONTAINER_DLL_remove (ph.plugin_op_head,
ph.plugin_op_tail,
curr);
plugin_op = (struct ESCROW_PlaintextPluginOperation *)curr->plugin_op;
GNUNET_IDENTITY_cancel (plugin_op->id_op);
if (NULL != plugin_op->sched_task)
GNUNET_SCHEDULER_cancel (plugin_op->sched_task);
GNUNET_free (plugin_op);
GNUNET_free (curr);
return;
}
}
}
/**
* IdentityInitContinuation for the plaintext plugin
*/
static void
plaintext_cont_init ()
{
return;
}
/**
* Entry point for the plugin.
*
* @param cls Config info
*
* @return the exported block API
*/
void *
libgnunet_plugin_escrow_plaintext_init (void *cls)
{
struct GNUNET_ESCROW_KeyPluginFunctions *api;
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
api = GNUNET_new (struct GNUNET_ESCROW_KeyPluginFunctions);
api->start_key_escrow = &start_plaintext_key_escrow;
api->verify_key_escrow = &verify_plaintext_key_escrow;
api->restore_key = &restore_plaintext_key_escrow;
api->get_status = &plaintext_get_status;
api->cancel_plugin_operation = &cancel_plaintext_operation;
ph.state = ESCROW_PLUGIN_STATE_INIT;
ph.id_init_cont = &plaintext_cont_init;
// set ego_create_cont here so it is called every time an ego is created
ph.ego_create_cont = &ego_created;
identity_handle = GNUNET_IDENTITY_connect (cfg,
&ESCROW_list_ego,
&ph);
return api;
}
/**
* Exit point from the plugin.
*
* @param cls the return value from #libgnunet_plugin_block_test_init()
*
* @return NULL
*/
void *
libgnunet_plugin_escrow_plaintext_done (void *cls)
{
struct GNUNET_ESCROW_KeyPluginFunctions *api = cls;
ph.state = ESCROW_PLUGIN_STATE_CLEANUP;
GNUNET_free (api);
GNUNET_IDENTITY_disconnect (identity_handle);
ESCROW_cleanup_ego_list (&ph);
return NULL;
}
/* end of plugin_escrow_plaintext.c */