summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--configure.ac34
-rw-r--r--src/Makefile.am6
-rw-r--r--src/escrow/.gitignore1
-rw-r--r--src/escrow/Makefile.am128
-rw-r--r--src/escrow/escrow.h249
-rw-r--r--src/escrow/escrow_api.c618
-rw-r--r--src/escrow/escrow_plugin_helper.c438
-rw-r--r--src/escrow/escrow_plugin_helper.h142
-rw-r--r--src/escrow/gnunet-escrow.c528
-rw-r--r--src/escrow/plugin_escrow_anastasis.c231
-rw-r--r--src/escrow/plugin_escrow_gns.c1842
-rw-r--r--src/escrow/plugin_escrow_plaintext.c673
-rw-r--r--src/escrow/plugin_gnsrecord_escrow.c173
-rw-r--r--src/escrow/plugin_rest_escrow.c1311
-rwxr-xr-xsrc/escrow/test_gns_escrow.sh48
-rwxr-xr-xsrc/escrow/test_plaintext_escrow.sh47
-rw-r--r--src/include/gnunet_crypto_lib.h17
-rw-r--r--src/include/gnunet_escrow_lib.h437
-rw-r--r--src/include/gnunet_escrow_plugin.h171
-rw-r--r--src/include/gnunet_gnsrecord_lib.h5
-rw-r--r--src/include/gnunet_reclaim_plugin.h1
-rw-r--r--src/util/crypto_ecc.c35
-rw-r--r--src/util/strings.c2
24 files changed, 7137 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index b337cc058..90c692de8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,3 +72,7 @@ cmake/
build
CMakeLists.txt
CMakeFiles.txt
+
+.vscode/settings.json
+src/gnsrecord/gnunet-gnsrecord-tvg
+src/revocation/gnunet-revocation-tvg
diff --git a/configure.ac b/configure.ac
index 3cf7e6cb6..5540d5aa4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -792,6 +792,34 @@ AC_ARG_WITH(jansson,
AS_IF([test "x$jansson" != x1],
[AC_MSG_ERROR([GNUnet requires libjansson])])
+# check for sss (Shamir Secret Sharing) library
+sss=0
+AC_MSG_CHECKING(for sss)
+AC_ARG_WITH(sss,
+ [ --with-sss=PREFIX (base of sss installation)],
+ [AC_MSG_RESULT([$with_sss])
+ AS_CASE([$with_sss],
+ [no],[],
+ [yes],[
+ CHECK_LIBHEADER(SSS, sss, sss_create_shares, sss.h, sss=1,)
+ ],[
+ LDFLAGS="-L$with_sss/lib $LDFLAGS"
+ CPPFLAGS="-I$with_sss/include $CPPFLAGS"
+ AC_CHECK_HEADERS(sss.h,
+ AC_CHECK_LIB([sss], [sss_create_shares],
+ EXT_LIB_PATH="-L$with_sss/lib $EXT_LIB_PATH"
+ sss=1))
+ ])
+ ],
+ [AC_MSG_RESULT([--with-sss not specified])
+ CHECK_LIBHEADER(SSS, sss, sss_create_shares, sss.h,sss=1,)])
+AM_CONDITIONAL(HAVE_SSS, [test "$sss" = 1])
+AS_IF([test "$sss" = 1],
+ [AC_DEFINE([HAVE_SSS],[1],[Have sss library])
+ sss_msg="yes"],
+ [AC_DEFINE([HAVE_SSS],[0],[Lacking sss library])
+ sss_msg="no"])
+
# check for libpulse(audio) library
pulse=0
libpulse_msg="no"
@@ -1965,6 +1993,7 @@ src/zonemaster/zonemaster.conf
src/rest/Makefile
src/abe/Makefile
src/reclaim/Makefile
+src/escrow/Makefile
pkgconfig/Makefile
pkgconfig/gnunetarm.pc
pkgconfig/gnunetats.pc
@@ -2066,6 +2095,10 @@ AS_IF([test "x$conversation_backend" = "xnone"],
# -- interface
interface_msg=`echo $DEFAULT_INTERFACE | tr -d \"`
# -- jansson
+# -- sss
+AS_IF([test "$sss" != 1],
+ [AC_MSG_WARN([sss library not found. GNS escrow plugin will not be compiled.])],
+ [sss_msg="yes"])
# -- libextractor
AS_IF([test "$extractor" != 1],
[AC_MSG_WARN([libextractor not found, but various file-sharing functions require it])],
@@ -2178,6 +2211,7 @@ libpulse: ${libpulse_msg}
libextractor: ${libextractor_msg}
texi2mdoc: ${texi2mdoc_msg}
mandoc: ${mandoc_msg}
+sss: ${sss_msg}
GNUnet configuration:
=====================
diff --git a/src/Makefile.am b/src/Makefile.am
index 234a63389..64a4e594a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,10 +9,14 @@ if HAVE_TESTING
ATS_TESTS = ats-tests
endif
+if HAVE_SSS
+ ESCROW_DIR = escrow
+endif
if HAVE_EXPERIMENTAL
EXP_DIR = \
rps \
- abd
+ abd \
+ $(ESCROW_DIR)
if HAVE_ABE
EXP_DIR += \
abe
diff --git a/src/escrow/.gitignore b/src/escrow/.gitignore
new file mode 100644
index 000000000..116fabf88
--- /dev/null
+++ b/src/escrow/.gitignore
@@ -0,0 +1 @@
+gnunet-escrow \ No newline at end of file
diff --git a/src/escrow/Makefile.am b/src/escrow/Makefile.am
new file mode 100644
index 000000000..36bb027ab
--- /dev/null
+++ b/src/escrow/Makefile.am
@@ -0,0 +1,128 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+plugindir = $(libdir)/gnunet
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+EXTRA_DIST = \
+ escrow.conf \
+ test_escrow.conf \
+ $(check_SCRIPTS)
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+ escrow.conf
+
+lib_LTLIBRARIES = \
+ libgnunetescrow.la
+plugin_LTLIBRARIES = \
+ libgnunet_plugin_escrow_plaintext.la \
+ libgnunet_plugin_escrow_gns.la \
+ libgnunet_plugin_escrow_anastasis.la \
+ libgnunet_plugin_gnsrecord_escrow.la \
+ libgnunet_plugin_rest_escrow.la
+
+bin_PROGRAMS = \
+ gnunet-escrow
+
+
+libgnunet_plugin_rest_escrow_la_SOURCES = \
+ plugin_rest_escrow.c
+libgnunet_plugin_rest_escrow_la_LIBADD = \
+ libgnunetescrow.la \
+ $(top_builddir)/src/identity/libgnunetidentity.la \
+ $(top_builddir)/src/rest/libgnunetrest.la \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
+ $(LTLIBINTL) -ljansson $(MHD_LIBS)
+libgnunet_plugin_rest_escrow_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+libgnunet_plugin_rest_escrow_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
+
+
+libgnunetescrow_la_SOURCES = \
+ escrow_api.c \
+ escrow.h
+libgnunetescrow_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL) $(XLIB)
+libgnunetescrow_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) \
+ -version-info 0:0:0
+
+
+libgnunet_plugin_escrow_plaintext_la_SOURCES = \
+ plugin_escrow_plaintext.c \
+ escrow_plugin_helper.c \
+ escrow_plugin_helper.h
+libgnunet_plugin_escrow_plaintext_la_LIBADD = \
+ libgnunetescrow.la \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
+ $(top_builddir)/src/identity/libgnunetidentity.la \
+ $(LTLIBINTL) $(MHD_LIBS)
+libgnunet_plugin_escrow_plaintext_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+libgnunet_plugin_escrow_gns_la_SOURCES = \
+ plugin_escrow_gns.c \
+ escrow_plugin_helper.c \
+ escrow_plugin_helper.h
+libgnunet_plugin_escrow_gns_la_LIBADD = \
+ libgnunetescrow.la \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
+ $(top_builddir)/src/identity/libgnunetidentity.la \
+ $(top_builddir)/src/namestore/libgnunetnamestore.la \
+ $(top_builddir)/src/gns/libgnunetgns.la \
+ $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
+ -lsss \
+ $(LTLIBINTL) $(MHD_LIBS)
+libgnunet_plugin_escrow_gns_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+libgnunet_plugin_escrow_anastasis_la_SOURCES = \
+ plugin_escrow_anastasis.c \
+ escrow_plugin_helper.c \
+ escrow_plugin_helper.h
+libgnunet_plugin_escrow_anastasis_la_LIBADD = \
+ libgnunetescrow.la \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
+ $(top_builddir)/src/identity/libgnunetidentity.la \
+ $(LTLIBINTL) $(MHD_LIBS)
+libgnunet_plugin_escrow_anastasis_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+
+libgnunet_plugin_gnsrecord_escrow_la_SOURCES = \
+ plugin_gnsrecord_escrow.c
+libgnunet_plugin_gnsrecord_escrow_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(LTLIBINTL)
+libgnunet_plugin_gnsrecord_escrow_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+
+gnunet_escrow_SOURCES = \
+ gnunet-escrow.c
+gnunet_escrow_LDADD = \
+ libgnunetescrow.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/namestore/libgnunetnamestore.la \
+ $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
+ $(top_builddir)/src/identity/libgnunetidentity.la \
+ $(GN_LIBINTL)
+
+check_SCRIPTS = \
+ test_plaintext_escrow.sh \
+ test_gns_escrow.sh
+ # test_anastasis_escrow.sh
+
+if ENABLE_TEST_RUN
+ AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
+ TESTS = $(check_SCRIPTS)
+endif
diff --git a/src/escrow/escrow.h b/src/escrow/escrow.h
new file mode 100644
index 000000000..8fc9123c1
--- /dev/null
+++ b/src/escrow/escrow.h
@@ -0,0 +1,249 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Johannes Späth
+ * @file escrow/escrow.h
+ *
+ * @brief Common type definitions for the escrow component
+ */
+
+#ifndef ESCROW_H
+#define ESCROW_H
+
+#include "gnunet_escrow_lib.h"
+
+
+/**
+ * State while collecting all egos
+ */
+#define ESCROW_PLUGIN_STATE_INIT 0
+
+/**
+ * Done collecting egos
+ */
+#define ESCROW_PLUGIN_STATE_POST_INIT 1
+
+/**
+ * State while cleaning up
+ */
+#define ESCROW_PLUGIN_STATE_CLEANUP 2
+
+
+/**
+ * The ego list
+ */
+struct EgoEntry
+{
+ /**
+ * DLL
+ */
+ struct EgoEntry *next;
+
+ /**
+ * DLL
+ */
+ struct EgoEntry *prev;
+
+ /**
+ * Ego Identifier
+ */
+ char *identifier;
+
+ /**
+ * Public key string
+ */
+ char *keystring;
+
+ /**
+ * The Ego
+ */
+ struct GNUNET_IDENTITY_Ego *ego;
+};
+
+/**
+ * Handle for a plugin instance
+ */
+struct ESCROW_PluginHandle
+{
+ /**
+ * The identity init continuation.
+ */
+ GNUNET_ESCROW_IdentityInitContinuation id_init_cont;
+
+ /**
+ * The ego create continuation.
+ */
+ GNUNET_ESCROW_EgoCreateContinuation ego_create_cont;
+
+ /**
+ * The current restore callback.
+ */
+ GNUNET_ESCROW_EgoContinuation curr_restore_cb;
+
+ /**
+ * The handle to the escrow component.
+ */
+ struct GNUNET_ESCROW_Handle *escrow_handle;
+
+ /**
+ * The state of the plugin (in the initialization phase).
+ */
+ int state;
+
+ /**
+ * The head of the ego list.
+ */
+ struct EgoEntry *ego_head;
+
+ /**
+ * The tail of the ego list
+ */
+ struct EgoEntry *ego_tail;
+
+ /**
+ * The head of the plugin operation list
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_head;
+
+ /**
+ * The tail of the plugin operation list
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_tail;
+};
+
+/**
+ * Wrapper for an escrow plugin operation
+ */
+struct ESCROW_PluginOperationWrapper
+{
+ /**
+ * Plugin operations are kept in a DLL.
+ */
+ struct ESCROW_PluginOperationWrapper *prev;
+
+ /**
+ * Plugin operations are kept in a DLL.
+ */
+ struct ESCROW_PluginOperationWrapper *next;
+
+ /**
+ * The actual plugin operation
+ */
+ void *plugin_op;
+};
+
+
+/**
+ * Continuation for a plugin operation (e.g. used for restore, as this
+ * callback has to be called from the IDENTITY service after finishing)
+ */
+typedef void (*ESCROW_Plugin_Continuation) (void *cls);
+
+
+/**
+ * Wrapper for the Plugin_AnchorContinuation.
+ *
+ * As this type of function is called from the scheduler, which only takes
+ * one argument as closure, this struct is used to pass more arguments.
+ */
+struct ESCROW_Plugin_AnchorContinuationWrapper
+{
+ /**
+ * Handle for the escrow component
+ */
+ struct GNUNET_ESCROW_Handle *h;
+
+ /**
+ * The escrow anchor
+ */
+ struct GNUNET_ESCROW_Anchor *anchor;
+
+ /**
+ * The unique ID of the respective ESCROW_Operation
+ */
+ uint32_t op_id;
+
+ /**
+ * The error message, NULL on success
+ */
+ const char *emsg;
+};
+
+/**
+ * Wrapper for the Plugin_EgoContinuation.
+ *
+ * As this type of function is called from the scheduler, which only takes
+ * one argument as closure, this struct is used to pass more arguments.
+ */
+struct ESCROW_Plugin_EgoContinuationWrapper
+{
+ /**
+ * Handle for the escrow component
+ */
+ struct GNUNET_ESCROW_Handle *h;
+
+ /**
+ * The restored ego
+ */
+ struct GNUNET_IDENTITY_Ego *ego;
+
+ /**
+ * The unique ID of the respective ESCROW_Operation
+ */
+ uint32_t op_id;
+
+ /**
+ * The error message, NULL on success
+ */
+ const char *emsg;
+};
+
+/**
+ * Wrapper for the Plugin_VerifyContinuation.
+ *
+ * As this type of function is called from the scheduler, which only takes
+ * one argument as closure, this struct is used to pass more arguments.
+ */
+struct ESCROW_Plugin_VerifyContinuationWrapper
+{
+ /**
+ * Handle for the escrow component
+ */
+ struct GNUNET_ESCROW_Handle *h;
+
+ /**
+ * The result of the verification
+ */
+ int verificationResult;
+
+ /**
+ * The unique ID of the respective ESCROW_Operation
+ */
+ uint32_t op_id;
+
+ /**
+ * The error message, NULL on success
+ */
+ const char *emsg;
+};
+
+
+#endif
diff --git a/src/escrow/escrow_api.c b/src/escrow/escrow_api.c
new file mode 100644
index 000000000..bf712d3de
--- /dev/null
+++ b/src/escrow/escrow_api.c
@@ -0,0 +1,618 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ 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_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:
+ * <method>:<egoName>:<anchorData>
+ * with <method>, <egoName> and <anchorData> 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;
+ methodString = egoNameString = anchorDataString = 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 (NULL == methodString)
+ return GNUNET_ESCROW_KEY_NONE;
+ 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.
+ *
+ * @param op operation to cancel
+ */
+void
+GNUNET_ESCROW_cancel (struct GNUNET_ESCROW_Operation *op)
+{
+ const struct GNUNET_ESCROW_KeyPluginFunctions *api;
+ struct GNUNET_ESCROW_Handle *h = op->h;
+
+ api = init_plugin (op->h, op->method);
+ api->cancel_plugin_operation (op->plugin_op_wrap);
+ GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
+ GNUNET_free (op);
+}
+
+
+/* end of escrow_api.c */
diff --git a/src/escrow/escrow_plugin_helper.c b/src/escrow/escrow_plugin_helper.c
new file mode 100644
index 000000000..7192123d9
--- /dev/null
+++ b/src/escrow/escrow_plugin_helper.c
@@ -0,0 +1,438 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Johannes Späth
+ * @file escrow/escrow_plugin.c
+ *
+ * @brief helper functions for escrow plugins
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_escrow_plugin.h"
+#include "escrow.h"
+
+
+/**
+ * Maintains the ego list for an escrow plugin.
+ * This function is an implementation of GNUNET_IDENTITY_Callback.
+ *
+ * It is initially called for all egos and then again
+ * whenever a ego's identifier changes or if it is deleted. At the
+ * end of the initial pass over all egos, the function is once called
+ * with 'NULL' for 'ego'. That does NOT mean that the callback won't
+ * be invoked in the future or that there was an error.
+ *
+ * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
+ * this function is only called ONCE, and 'NULL' being passed in
+ * 'ego' does indicate an error (i.e. name is taken or no default
+ * value is known). If 'ego' is non-NULL and if '*ctx'
+ * is set in those callbacks, the value WILL be passed to a subsequent
+ * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
+ * that one was not NULL).
+ *
+ * When an identity is renamed, this function is called with the
+ * (known) ego but the NEW identifier.
+ *
+ * When an identity is deleted, this function is called with the
+ * (known) ego and "NULL" for the 'identifier'. In this case,
+ * the 'ego' is henceforth invalid (and the 'ctx' should also be
+ * cleaned up).
+ *
+ * @param cls plugin handle
+ * @param ego ego handle
+ * @param ctx context for application to store data for this ego
+ * (during the lifetime of this process, initially NULL)
+ * @param identifier identifier assigned by the user for this ego,
+ * NULL if the user just deleted the ego and it
+ * must thus no longer be used
+ */
+void
+ESCROW_list_ego (void *cls,
+ struct GNUNET_IDENTITY_Ego *ego,
+ void **ctx,
+ const char *identifier)
+{
+ struct ESCROW_PluginHandle *ph = cls;
+ struct EgoEntry *ego_entry;
+ struct GNUNET_CRYPTO_EcdsaPublicKey pk;
+
+ /* don't add/change/delete egos when we are already cleaning up */
+ if (ESCROW_PLUGIN_STATE_CLEANUP == ph->state)
+ return;
+
+ if ((NULL == ego) && (ESCROW_PLUGIN_STATE_INIT == ph->state))
+ {
+ ph->state = ESCROW_PLUGIN_STATE_POST_INIT;
+ /* call IdentityInitContinuation */
+ ph->id_init_cont ();
+ return;
+ }
+ GNUNET_assert (NULL != ego);
+
+ if (ESCROW_PLUGIN_STATE_INIT == ph->state)
+ {
+ ego_entry = GNUNET_new (struct EgoEntry);
+ GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
+ ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
+ ego_entry->ego = ego;
+ ego_entry->identifier = GNUNET_strdup (identifier);
+ GNUNET_CONTAINER_DLL_insert_tail (ph->ego_head,
+ ph->ego_tail,
+ ego_entry);
+ return;
+ }
+ /* Ego renamed or added */
+ if (identifier != NULL)
+ {
+ for (ego_entry = ph->ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ {
+ if (ego_entry->ego == ego)
+ {
+ /* Rename */
+ GNUNET_free (ego_entry->identifier);
+ ego_entry->identifier = GNUNET_strdup (identifier);
+ /* TODO: this handles an edge case when the user restores an ego
+ that already exists, i.e. with the same private key. In that case,
+ @param ego is the same for the new as for the existing ego and this
+ method thinks it is a rename. */
+ if (NULL != ph->ego_create_cont)
+ ph->ego_create_cont (ego);
+ break;
+ }
+ }
+ if (NULL == ego_entry)
+ {
+ /* Add */
+ ego_entry = GNUNET_new (struct EgoEntry);
+ GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
+ ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
+ ego_entry->ego = ego;
+ ego_entry->identifier = GNUNET_strdup (identifier);
+ GNUNET_CONTAINER_DLL_insert_tail (ph->ego_head,
+ ph->ego_tail,
+ ego_entry);
+ /* new ego is added to the list, call ego_create_cont if this was
+ because of an ESCROW_get operation, i.e. ego_create_cont != NULL */
+ if (NULL != ph->ego_create_cont)
+ ph->ego_create_cont (ego);
+ }
+ }
+ else
+ {
+ /* Delete */
+ for (ego_entry = ph->ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ {
+ if (ego_entry->ego == ego)
+ break;
+ }
+ if (NULL == ego_entry)
+ return; /* Not found */
+
+ GNUNET_CONTAINER_DLL_remove (ph->ego_head,
+ ph->ego_tail,
+ ego_entry);
+ GNUNET_free (ego_entry->identifier);
+ GNUNET_free (ego_entry->keystring);
+ GNUNET_free (ego_entry);
+ return;
+ }
+}
+
+
+/**
+ * Cleanup the ego list of an escrow plugin.
+ *
+ * @param ph handle for the plugin
+ */
+void
+ESCROW_cleanup_ego_list (struct ESCROW_PluginHandle *ph)
+{
+ struct EgoEntry *ego_entry;
+
+ while (NULL != (ego_entry = ph->ego_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ph->ego_head, ph->ego_tail, ego_entry);
+ GNUNET_free (ego_entry->identifier);
+ GNUNET_free (ego_entry->keystring);
+ GNUNET_free (ego_entry);
+ }
+}
+
+
+/**
+ * Build an anchor struct.
+ *
+ * @param method escrow method
+ * @param egoName name of the ego
+ * @param data anchor data
+ * @param data_size size of the anchor data
+ *
+ * @return a new anchor struct
+ */
+struct GNUNET_ESCROW_Anchor *
+ESCROW_build_anchor (enum GNUNET_ESCROW_Key_Escrow_Method method,
+ const char *egoName,
+ void *data,
+ uint32_t data_size)
+{
+ struct GNUNET_ESCROW_Anchor *anchor;
+ char *ptr;
+
+ anchor = GNUNET_malloc (sizeof (struct GNUNET_ESCROW_Anchor)
+ + data_size
+ + strlen (egoName) + 1);
+ anchor->method = method;
+ anchor->size = data_size;
+ ptr = (char *)&anchor[1];
+ GNUNET_memcpy (ptr, data, data_size);
+ ptr += data_size;
+ anchor->egoName = ptr;
+ strcpy (ptr, egoName);
+
+ return anchor;
+}
+
+
+static char *
+string_to_upper (const char *str)
+{
+ char *str_upper;
+ uint16_t i;
+
+ str_upper = GNUNET_strdup (str);
+
+ for (i = 0; i < strlen(str_upper); i++)
+ {
+ if (str_upper[i] >= 'a' && str_upper[i] <= 'z')
+ str_upper[i] -= 32; // 'a' - 'A' = 32
+ }
+
+ return str_upper;
+}
+
+
+static int
+write_config (struct GNUNET_ESCROW_Handle *h)
+{
+ char *conf_file;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_filename (h->cfg,
+ "PATHS",
+ "DEFAULTCONFIG",
+ &conf_file));
+ if (GNUNET_OK != GNUNET_CONFIGURATION_write (h->cfg, conf_file))
+ {
+ fprintf (stderr, "unable to write config file\n");
+ GNUNET_free (conf_file);
+ return GNUNET_NO;
+ }
+
+ GNUNET_free (conf_file);
+
+ return GNUNET_OK;
+}
+
+
+static char *
+get_config_section (struct GNUNET_IDENTITY_Ego *ego)
+{
+ struct GNUNET_CRYPTO_EcdsaPublicKey *pub;
+ char *config_section, *pubkey_string;
+
+ pub = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
+ GNUNET_IDENTITY_ego_get_public_key (ego, pub);
+ pubkey_string = GNUNET_CRYPTO_ecdsa_public_key_to_string (pub);
+
+ // allocate enough space for "escrow-PUBKEY"
+ config_section = GNUNET_malloc (7 + strlen (pubkey_string) + 1);
+ sprintf (config_section, "escrow-%s", pubkey_string);
+
+ GNUNET_free (pubkey_string);
+
+ return config_section;
+}
+
+
+/**
+ * Update the status of an escrow in the configuration after a successful
+ * VERIFY operation.
+ *
+ * @param h handle for the escrow component
+ * @param ego the ego of which the escrow status is updated
+ * @param plugin_name the name of the used plugin
+ *
+ * @return GNUNET_OK on success
+ */
+int
+ESCROW_update_escrow_status_verify (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *plugin_name)
+{
+ char *config_section, *config_option, *plugin_name_upper;
+ struct GNUNET_TIME_Absolute now, next_verification;
+ struct GNUNET_TIME_Relative escrow_interval;
+
+ config_section = get_config_section (ego);
+
+ // allocate enough space for "<plugin_name>_VERIFY_INTERVAL"
+ config_option = GNUNET_malloc (strlen (plugin_name) + 16 + 1);
+ plugin_name_upper = string_to_upper (plugin_name);
+ sprintf (config_option, "%s_VERIFY_INTERVAL", plugin_name_upper);
+
+ now = GNUNET_TIME_absolute_get ();
+ GNUNET_CONFIGURATION_set_value_number (h->cfg,
+ config_section,
+ "LAST_SUCCESSFUL_VERIFICATION_TIME",
+ (unsigned long long)now.abs_value_us);
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (h->cfg,
+ "escrow",
+ config_option,
+ &escrow_interval))
+ {
+ fprintf (stderr, "could not find config value for verification interval\n");
+ GNUNET_free (config_section);
+ GNUNET_free (config_option);
+ GNUNET_free (plugin_name_upper);
+ return GNUNET_NO;
+ }
+ next_verification = GNUNET_TIME_absolute_add (now, escrow_interval);
+ GNUNET_CONFIGURATION_set_value_number (h->cfg,
+ config_section,
+ "NEXT_RECOMMENDED_VERIFICATION_TIME",
+ (unsigned long long)next_verification.abs_value_us);
+
+ if (GNUNET_OK != write_config (h))
+ {
+ GNUNET_free (config_section);
+ GNUNET_free (config_option);
+ GNUNET_free (plugin_name_upper);
+ return GNUNET_NO;
+ }
+
+ GNUNET_free (config_section);
+ GNUNET_free (config_option);
+ GNUNET_free (plugin_name_upper);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Update the status of an escrow in the configuration after a PUT operation.
+ *
+ * @param h handle for the escrow component
+ * @param ego the ego of which the escrow status is updated
+ * @param plugin_name the name of the used plugin
+ *
+ * @return GNUNET_OK on success
+ */
+int
+ESCROW_update_escrow_status_put (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *plugin_name)
+{
+ char *config_section;
+
+ config_section = get_config_section (ego);
+
+ GNUNET_CONFIGURATION_set_value_string (h->cfg,
+ config_section,
+ "ESCROW_METHOD",
+ plugin_name);
+
+ if (GNUNET_OK != write_config (h))
+ {
+ GNUNET_free (config_section);
+ return GNUNET_NO;
+ }
+
+ GNUNET_free (config_section);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the status of an escrow from the configuration.
+ *
+ * @param h handle for the escrow component
+ * @param ego the ego of which the escrow status has to be obtained
+ *
+ * @return the status of the escrow, packed into a GNUNET_ESCROW_Status struct
+ */
+struct GNUNET_ESCROW_Status *
+ESCROW_get_escrow_status (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego)
+{
+ struct GNUNET_ESCROW_Status *status;
+ unsigned long long conf_last_verification, conf_next_verification;
+ char *config_section, *conf_escrow_method;
+
+ config_section = get_config_section (ego);
+
+ status = GNUNET_new (struct GNUNET_ESCROW_Status);
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (h->cfg,
+ config_section,
+ "LAST_SUCCESSFUL_VERIFICATION_TIME",
+ &conf_last_verification))
+ {
+ /* failed to get value from config, set last verification time to zero */
+ status->last_successful_verification_time = GNUNET_TIME_absolute_get_zero_();
+ }
+ else
+ status->last_successful_verification_time.abs_value_us = (uint64_t)conf_last_verification;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (h->cfg,
+ config_section,
+ "NEXT_RECOMMENDED_VERIFICATION_TIME",
+ &conf_next_verification))
+ {
+ /* failed to get value from config, set next recommended verification to now */
+ status->next_recommended_verification_time = GNUNET_TIME_absolute_get ();
+ }
+ else
+ status->next_recommended_verification_time.abs_value_us = (uint64_t)conf_next_verification;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
+ config_section,
+ "ESCROW_METHOD",
+ &conf_escrow_method))
+ {
+ /* failed to get value from config, set last method to NONE */
+ status->last_method = GNUNET_ESCROW_KEY_NONE;
+ }
+ else
+ status->last_method = GNUNET_ESCROW_method_string_to_number (conf_escrow_method);
+
+ GNUNET_free (config_section);
+ if (NULL != conf_escrow_method)
+ GNUNET_free (conf_escrow_method);
+
+ return status;
+}
+
+
+/* end of escrow_plugin.c */
diff --git a/src/escrow/escrow_plugin_helper.h b/src/escrow/escrow_plugin_helper.h
new file mode 100644
index 000000000..d911800e6
--- /dev/null
+++ b/src/escrow/escrow_plugin_helper.h
@@ -0,0 +1,142 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Johannes Späth
+ * @file escrow/escrow_plugin.h
+ *
+ * @brief helper functions for escrow plugins
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_escrow_plugin.h"
+#include "escrow.h"
+
+
+/**
+ * Maintains the ego list for an escrow plugin.
+ * This function is an implementation of GNUNET_IDENTITY_Callback.
+ *
+ * It is initially called for all egos and then again
+ * whenever a ego's identifier changes or if it is deleted. At the
+ * end of the initial pass over all egos, the function is once called
+ * with 'NULL' for 'ego'. That does NOT mean that the callback won't
+ * be invoked in the future or that there was an error.
+ *
+ * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
+ * this function is only called ONCE, and 'NULL' being passed in
+ * 'ego' does indicate an error (i.e. name is taken or no default
+ * value is known). If 'ego' is non-NULL and if '*ctx'
+ * is set in those callbacks, the value WILL be passed to a subsequent
+ * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
+ * that one was not NULL).
+ *
+ * When an identity is renamed, this function is called with the
+ * (known) ego but the NEW identifier.
+ *
+ * When an identity is deleted, this function is called with the
+ * (known) ego and "NULL" for the 'identifier'. In this case,
+ * the 'ego' is henceforth invalid (and the 'ctx' should also be
+ * cleaned up).
+ *
+ * @param cls plugin handle
+ * @param ego ego handle
+ * @param ctx context for application to store data for this ego
+ * (during the lifetime of this process, initially NULL)
+ * @param identifier identifier assigned by the user for this ego,
+ * NULL if the user just deleted the ego and it
+ * must thus no longer be used
+ */
+void
+ESCROW_list_ego (void *cls,
+ struct GNUNET_IDENTITY_Ego *ego,
+ void **ctx,
+ const char *identifier);
+
+
+/**
+ * Cleanup the ego list of an escrow plugin.
+ *
+ * @param ph handle for the plugin
+ */
+void
+ESCROW_cleanup_ego_list (struct ESCROW_PluginHandle *ph);
+
+
+/**
+ * Build an anchor struct.
+ *
+ * @param method escrow method
+ * @param egoName name of the ego
+ * @param data anchor data
+ * @param data_size size of the anchor data
+ *
+ * @return a new anchor struct
+ */
+struct GNUNET_ESCROW_Anchor *
+ESCROW_build_anchor (enum GNUNET_ESCROW_Key_Escrow_Method method,
+ const char *egoName,
+ void *data,
+ uint32_t data_size);
+
+
+/**
+ * Update the status of an escrow in the configuration after a VERIFY operation.
+ *
+ * @param h handle for the escrow component
+ * @param ego the ego of which the escrow status is updated
+ * @param plugin_name the name of the used plugin
+ *
+ * @return GNUNET_OK on success
+ */
+int
+ESCROW_update_escrow_status_verify (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *plugin_name);
+
+
+/**
+ * Update the status of an escrow in the configuration after a PUT operation.
+ *
+ * @param h handle for the escrow component
+ * @param ego the ego of which the escrow status is updated
+ * @param plugin_name the name of the used plugin
+ *
+ * @return GNUNET_OK on success
+ */
+int
+ESCROW_update_escrow_status_put (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *plugin_name);
+
+
+/**
+ * Get the status of an escrow from the configuration.
+ *
+ * @param h handle for the escrow component
+ * @param ego the ego of which the escrow status has to be obtained
+ *
+ * @return the status of the escrow, packed into a GNUNET_ESCROW_Status struct
+ */
+struct GNUNET_ESCROW_Status *
+ESCROW_get_escrow_status (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego);
diff --git a/src/escrow/gnunet-escrow.c b/src/escrow/gnunet-escrow.c
new file mode 100644
index 000000000..ad34dd8f3
--- /dev/null
+++ b/src/escrow/gnunet-escrow.c
@@ -0,0 +1,528 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Johannes Späth
+ * @file src/escrow/gnunet-escrow.c
+ * @brief Identity Escrow utility
+ *
+ */
+
+#include "platform.h"
+
+#include "gnunet_util_lib.h"
+#include "gnunet_escrow_lib.h"
+
+/**
+ * return value
+ */
+static int ret;
+
+/**
+ * -P option
+ */
+static char *put_ego;
+
+/**
+ * -V option
+ */
+static char *verify_ego;
+
+/**
+ * -G option
+ */
+static int get_flag;
+
+/**
+ * -S option
+ */
+static char *status_ego;
+
+/**
+ * The ego
+ */
+struct GNUNET_IDENTITY_Ego *ego;
+
+/**
+ * User secret string
+ */
+static char *user_secret_string;
+
+/**
+ * Anchor string
+ */
+static char *anchor_string;
+
+/**
+ * The escrow anchor
+ */
+struct GNUNET_ESCROW_Anchor *anchor;
+
+/**
+ * Plugin name
+ */
+static char *method_name;
+
+/**
+ * Escrow method
+ */
+enum GNUNET_ESCROW_Key_Escrow_Method method;
+
+/**
+ * Handle to the escrow component
+ */
+static struct GNUNET_ESCROW_Handle *escrow_handle;
+
+/**
+ * Escrow operation
+ */
+static struct GNUNET_ESCROW_Operation *escrow_op;
+
+/**
+ * Escrow status
+ */
+static struct GNUNET_ESCROW_Status *escrow_status;
+
+/**
+ * Handle to the identity service
+ */
+static struct GNUNET_IDENTITY_Handle *identity_handle;
+
+/**
+ * Cleanup task
+ */
+static struct GNUNET_SCHEDULER_Task *cleanup_task;
+
+
+/**
+ * Called to clean up the escrow component
+ */
+static void
+do_cleanup (void *cls)
+{
+ cleanup_task = NULL;
+ if (NULL != escrow_op)
+ {
+ GNUNET_ESCROW_cancel (escrow_op);
+ escrow_op = NULL;
+ }
+ if (NULL != escrow_handle)
+ GNUNET_ESCROW_fini (escrow_handle);
+ if (NULL != identity_handle)
+ GNUNET_IDENTITY_disconnect (identity_handle);
+ if (NULL != method_name)
+ {
+ GNUNET_free (method_name);
+ method_name = NULL;
+ }
+ if (NULL != user_secret_string)
+ {
+ GNUNET_free (user_secret_string);
+ user_secret_string = NULL;
+ }
+ if (NULL != anchor_string)
+ {
+ GNUNET_free (anchor_string);
+ anchor_string = NULL;
+ }
+ if (NULL != anchor)
+ {
+ GNUNET_free (anchor);
+ anchor = NULL;
+ }
+ if (NULL != put_ego)
+ {
+ GNUNET_free (put_ego);
+ put_ego = NULL;
+ }
+ if (NULL != verify_ego)
+ {
+ GNUNET_free (verify_ego);
+ verify_ego = NULL;
+ }
+ if (NULL != status_ego)
+ {
+ GNUNET_free (status_ego);
+ status_ego = NULL;
+ }
+ if (NULL != escrow_status)
+ {
+ GNUNET_free (escrow_status);
+ escrow_status = NULL;
+ }
+ if (NULL != ego)
+ {
+ /* does not have to be freed, as this is done when
+ cleaning up the ego list in the plugin */
+ ego = NULL;
+ }
+ method = -1;
+}
+
+
+static void
+put_cb (void *cls,
+ struct GNUNET_ESCROW_Anchor *anchor,
+ const char *emsg)
+{
+ char *anchorString;
+
+ escrow_op = NULL;
+
+ if (NULL == anchor)
+ {
+ ret = 1;
+ if (NULL != emsg)
+ fprintf (stderr, "Escrow failed: %s", emsg);
+ }
+ else
+ {
+ anchorString = GNUNET_ESCROW_anchor_data_to_string (anchor);
+
+ fprintf (stdout, "Escrow finished! Please keep the following anchor "
+ "in order to restore the key later!\n%s\n", anchorString);
+ }
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+ GNUNET_free (anchor);
+}
+
+
+static void
+verify_cb (void *cls,
+ int verificationResult,
+ const char *emsg)
+{
+ escrow_op = NULL;
+
+ if (NULL != emsg)
+ fprintf (stderr, "%s", emsg);
+
+ switch (verificationResult)
+ {
+ case GNUNET_ESCROW_VALID:
+ fprintf (stdout, "Escrow is valid!\n");
+ break;
+ case GNUNET_ESCROW_SHARES_MISSING:
+ fprintf (stdout, "Escrow can be restored, but some shares are missing! "
+ "Please perform a new escrow.\n");
+ break;
+ case GNUNET_ESCROW_INVALID:
+ ret = 2;
+ fprintf (stdout, "Escrow is INvalid! Please perform a new escrow.\n");
+ break;
+ default:
+ ret = 1;
+ fprintf (stderr, "invalid verificationResult");
+ }
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+}
+
+
+static void
+get_cb (void *cls,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *emsg)
+{
+ escrow_op = NULL;
+
+ if (NULL == ego)
+ {
+ ret = 1;
+ if (NULL != emsg)
+ fprintf (stderr, "Escrow failed: %s", emsg);
+ }
+ else
+ fprintf (stdout, "Identity %s could successfully be restored!\n", anchor->egoName);
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+}
+
+
+static void
+start_process ()
+{
+ /* put */
+ if (NULL != put_ego)
+ {
+ if (NULL == ego)
+ {
+ ret = 1;
+ fprintf (stderr, "Ego %s not found\n", put_ego);
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+ return;
+ }
+ escrow_op = GNUNET_ESCROW_put (escrow_handle,
+ ego,
+ user_secret_string,
+ method,
+ &put_cb,
+ NULL);
+ return;
+ }
+ /* verify */
+ if (NULL != verify_ego)
+ {
+ if (NULL == ego)
+ {
+ ret = 1;
+ fprintf (stderr, "Ego %s not found\n", verify_ego);
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+ return;
+ }
+ escrow_op = GNUNET_ESCROW_verify (escrow_handle,
+ ego,
+ anchor,
+ method,
+ &verify_cb,
+ NULL);
+ return;
+ }
+ /* get */
+ if (GNUNET_YES == get_flag)
+ {
+ escrow_op = GNUNET_ESCROW_get (escrow_handle,
+ anchor,
+ &get_cb,
+ NULL);
+ return;
+ }
+ /* status */
+ if (NULL != status_ego)
+ {
+ if (NULL == ego)
+ {
+ ret = 1;
+ fprintf (stderr, "Ego %s not found\n", status_ego);
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+ return;
+ }
+ escrow_status = GNUNET_ESCROW_get_status (escrow_handle,
+ ego,
+ method);
+
+ if (GNUNET_ESCROW_KEY_NONE == escrow_status->last_method)
+ fprintf (stdout, "No escrow has been performed for identity %s!\n", status_ego);
+ else
+ {
+ fprintf (stdout, "Escrow STATUS information for identity %s\n", status_ego);
+ fprintf (stdout, "=======================================================\n");
+ if (0 == escrow_status->last_successful_verification_time.abs_value_us)
+ fprintf (stdout, "No successful verification! Please VERIFY now.\n");
+ else
+ {
+ fprintf (stdout, "Last successful verification:\t%s\n",
+ GNUNET_STRINGS_absolute_time_to_string (escrow_status->last_successful_verification_time));
+ fprintf (stdout, "Next recommended verification:\t%s\n",
+ GNUNET_STRINGS_absolute_time_to_string (escrow_status->next_recommended_verification_time));
+ }
+ fprintf (stdout, "Last method:\t\t\t%s\n",
+ GNUNET_ESCROW_method_number_to_string (escrow_status->last_method));
+ }
+
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+ return;
+ }
+}
+
+
+static int init = GNUNET_YES;
+
+static void
+ego_cb (void *cls,
+ struct GNUNET_IDENTITY_Ego *e,
+ void **ctx,
+ const char *name)
+{
+ char *ego_name = cls;
+
+ if (NULL == name)
+ {
+ if (GNUNET_YES == init)
+ {
+ init = GNUNET_NO;
+ start_process ();
+ }
+ return;
+ }
+ if (NULL != ego_name && 0 != strcmp (name, ego_name))
+ return;
+ ego = e;
+}
+
+
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *c)
+{
+ char *ego_name;
+
+ ret = 0;
+
+ /* check if method is set (needed for all operations except GET) */
+ if (NULL == method_name && GNUNET_YES != get_flag)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("Escrow method (-m option) is missing\n"));
+ return;
+ }
+
+ if (NULL != put_ego)
+ {
+ if (NULL != verify_ego || GNUNET_YES == get_flag || NULL != status_ego)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("-P may only be used without -V, -G or -S!\n"));
+ return;
+ }
+ /* put */
+ ego_name = put_ego;
+ }
+ else if (NULL != verify_ego)
+ {
+ if (GNUNET_YES == get_flag || NULL != status_ego)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("-V may only be used without -P, -G or -S!\n"));
+ return;
+ }
+ /* verify */
+ if (NULL == anchor_string)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("-a is needed for -V!\n"));
+ return;
+ }
+ ego_name = verify_ego;
+ }
+ else if (GNUNET_YES == get_flag)
+ {
+ if (NULL != status_ego)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("-G may only be used without -P, -V or -S!\n"));
+ return;
+ }
+ /* get */
+ if (NULL == anchor_string)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("-a is needed for -G!\n"));
+ return;
+ }
+ ego_name = NULL;
+ }
+ else if (NULL != status_ego)
+ {
+ /* status */
+ ego_name = status_ego;
+ }
+ else
+ {
+ /* nothing */
+ ret = 1;
+ fprintf (stderr, _ ("-P, -V, -G or -S option must be specified!\n"));
+ return;
+ }
+
+ /* determine method */
+ if (NULL != method_name)
+ {
+ method = GNUNET_ESCROW_method_string_to_number (method_name);
+ if (GNUNET_ESCROW_KEY_NONE == method)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("unknown method name!\n"));
+ return;
+ }
+ }
+ else // initialize to error value (should not be used in this case)
+ method = GNUNET_ESCROW_KEY_NONE;
+
+ escrow_handle = GNUNET_ESCROW_init (c);
+
+ if (NULL != anchor_string)
+ {
+ /* parse anchor_string according to method */
+ anchor = GNUNET_ESCROW_anchor_string_to_data (anchor_string);
+ if (NULL == anchor)
+ {
+ ret = 1;
+ fprintf (stderr, _ ("failed to parse anchor string!\n"));
+ cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
+ return;
+ }
+ }
+
+ /* connect to identity service in order to get the egos */
+ identity_handle = GNUNET_IDENTITY_connect (c, &ego_cb, ego_name);
+}
+
+
+int
+main (int argc, char *const argv[])
+{
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_string ('P',
+ "put",
+ "NAME",
+ gettext_noop ("Put the ego NAME in escrow"),
+ &put_ego),
+ GNUNET_GETOPT_option_string ('V',
+ "verify",
+ "NAME",
+ gettext_noop ("Verify the escrow of the ego NAME"),
+ &verify_ego),
+ GNUNET_GETOPT_option_flag ('G',
+ "get",
+ gettext_noop ("Get an ego back from escrow"),
+ &get_flag),
+ GNUNET_GETOPT_option_string ('S',
+ "status",
+ "NAME",
+ gettext_noop ("Get the status of the escrow of ego NAME"),
+ &status_ego),
+ GNUNET_GETOPT_option_string ('u',
+ "userSecret",
+ "USER_SECRET",
+ gettext_noop ("The user secret string"),
+ &user_secret_string),
+ GNUNET_GETOPT_option_string ('a',
+ "anchor",
+ "ANCHOR",
+ gettext_noop ("The escrow anchor"),
+ &anchor_string),
+ GNUNET_GETOPT_option_string ('m',
+ "method",
+ "METHOD",
+ gettext_noop ("The escrow method (and plugin) to use"),
+ &method_name),
+ GNUNET_GETOPT_OPTION_END
+ };
+ if (GNUNET_OK != GNUNET_PROGRAM_run (argc,
+ argv,
+ "gnunet-escrow",
+ _ ("escrow command line tool"),
+ options,
+ &run,
+ NULL))
+ return 1;
+ else
+ return ret;
+}
diff --git a/src/escrow/plugin_escrow_anastasis.c b/src/escrow/plugin_escrow_anastasis.c
new file mode 100644
index 000000000..8a5c05e24
--- /dev/null
+++ b/src/escrow/plugin_escrow_anastasis.c
@@ -0,0 +1,231 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file escrow/plugin_escrow_anastasis.c
+ * @brief escrow-plugin-anastasis escrow plugin for escrow of the key using Anastasis
+ *
+ * @author Johannes Späth
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_escrow_plugin.h"
+#include "escrow_plugin_helper.h"
+#include <inttypes.h>
+
+
+/**
+ * Identity handle
+ */
+static struct GNUNET_IDENTITY_Handle *identity_handle;
+
+/**
+ * Handle for the plugin instance
+ */
+static struct ESCROW_PluginHandle ph;
+
+
+/**
+ * Start the Anastasis escrow of 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_anastasis_key_escrow (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *userSecret,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id)
+{
+ struct ESCROW_Plugin_AnchorContinuationWrapper *w;
+
+ w = GNUNET_new (struct ESCROW_Plugin_AnchorContinuationWrapper);
+ w->h = h;
+ w->op_id = op_id;
+
+ // TODO: implement
+ w->anchor = NULL;
+ w->emsg = _ ("Anastasis escrow is not yet implemented!\n");
+ GNUNET_SCHEDULER_add_now (cb, w);
+ return NULL;
+}
+
+
+/**
+ * Verify the Anastasis 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_anastasis_key_escrow (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id)
+{
+ struct ESCROW_Plugin_VerifyContinuationWrapper *w;
+
+ w = GNUNET_new (struct ESCROW_Plugin_VerifyContinuationWrapper);
+ w->h = h;
+ w->op_id = op_id;
+
+ // TODO: implement
+ w->verificationResult = GNUNET_ESCROW_INVALID;
+ w->emsg = _ ("Anastasis escrow is not yet implemented!\n");
+ GNUNET_SCHEDULER_add_now (cb, w);
+ return NULL;
+}
+
+
+/**
+ * Restore the key from Anastasis 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_anastasis_key_escrow (struct GNUNET_ESCROW_Handle *h,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id)
+{
+ struct ESCROW_Plugin_EgoContinuationWrapper *w;
+
+ w = GNUNET_new (struct ESCROW_Plugin_EgoContinuationWrapper);
+ w->h = h;
+ w->op_id = op_id;
+
+ // TODO: implement
+ w->ego = NULL;
+ w->emsg = _ ("Anastasis escrow is not yet implemented!\n");
+ GNUNET_SCHEDULER_add_now (cb, w);
+ return NULL;
+}
+
+
+/**
+ * Get the status of a Anastasis 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 *
+anastasis_get_status (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego)
+{
+ return ESCROW_get_escrow_status (h, ego);
+}
+
+
+/**
+ * Cancel an Anastasis plugin operation.
+ *
+ * @param plugin_op_wrap the plugin operation wrapper containing the operation
+ */
+void
+cancel_anastasis_operation (struct ESCROW_PluginOperationWrapper *plugin_op_wrap)
+{
+ // TODO: implement
+ return;
+}
+
+
+/**
+ * IdentityInitContinuation for the Anastasis plugin
+ */
+static void
+anastasis_cont_init ()
+{
+ return;
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls Config info
+ *
+ * @return the exported block API
+ */
+void *
+libgnunet_plugin_escrow_anastasis_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_anastasis_key_escrow;
+ api->verify_key_escrow = &verify_anastasis_key_escrow;
+ api->restore_key = &restore_anastasis_key_escrow;
+ api->get_status = &anastasis_get_status;
+ api->cancel_plugin_operation = &cancel_anastasis_operation;
+
+ ph.state = ESCROW_PLUGIN_STATE_INIT;
+ ph.id_init_cont = &anastasis_cont_init;
+
+ 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_anastasis_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_anastasis.c */
diff --git a/src/escrow/plugin_escrow_gns.c b/src/escrow/plugin_escrow_gns.c
new file mode 100644
index 000000000..6889d1dd0
--- /dev/null
+++ b/src/escrow/plugin_escrow_gns.c
@@ -0,0 +1,1842 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file escrow/plugin_escrow_gns.c
+ * @brief escrow-plugin-gns escrow plugin for the escrow of the key
+ * using GNS and escrow identities
+ *
+ * @author Johannes Späth
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_escrow_plugin.h"
+#include "escrow_plugin_helper.h"
+#include "gnunet_namestore_service.h"
+#include "gnunet_gns_service.h"
+#include "gnunet_gnsrecord_lib.h"
+#include "escrow.h"
+#include <sss.h>
+#include <inttypes.h>
+
+
+/**
+ * Continuation with a private key (used for restore_private_key)
+ */
+typedef void (*PkContinuation) (void *cls,
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk);
+
+
+struct IdentityOperationEntry
+{
+ /**
+ * DLL
+ */
+ struct IdentityOperationEntry *prev;
+
+ /**
+ * DLL
+ */
+ struct IdentityOperationEntry *next;
+
+ /**
+ * Identity operation
+ */
+ struct GNUNET_IDENTITY_Operation *id_op;
+
+ /**
+ * Private key of the respective ego
+ */
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *pk;
+
+ /**
+ * Name of the respective ego
+ */
+ char *name;
+
+ /**
+ * Index of the respective share
+ */
+ uint8_t i;
+
+ /**
+ * The plugin operation that started the identity operation
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+};
+
+
+struct PkEntry
+{
+ /**
+ * DLL
+ */
+ struct PkEntry *prev;
+
+ /**
+ * DLL
+ */
+ struct PkEntry *next;
+
+ /**
+ * private key
+ */
+ struct GNUNET_CRYPTO_EcdsaPrivateKey pk;
+
+ /**
+ * index of the respective share
+ */
+ uint8_t i;
+};
+
+
+struct NamestoreQueueEntry
+{
+ /**
+ * DLL
+ */
+ struct NamestoreQueueEntry *prev;
+
+ /**
+ * DLL
+ */
+ struct NamestoreQueueEntry *next;
+
+ /**
+ * Namestore queue entry
+ */
+ struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
+
+ /**
+ * Plugin operation that called the namestore operation
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+};
+
+
+// define TimeoutTaskEntry here, as it is already needed in GnsLookupRequest
+struct TimeoutTaskEntry;
+
+
+struct GnsLookupRequestEntry
+{
+ /**
+ * DLL
+ */
+ struct GnsLookupRequestEntry *prev;
+
+ /**
+ * DLL
+ */
+ struct GnsLookupRequestEntry *next;
+
+ /**
+ * GNS lookup request
+ */
+ struct GNUNET_GNS_LookupRequest *lr;
+
+ /**
+ * Plugin operation that started the lookup
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+
+ /**
+ * index of the respective share
+ */
+ uint8_t i;
+
+ /**
+ * Timeout task scheduled for this lookup request
+ */
+ struct TimeoutTaskEntry *tt;
+};
+
+
+struct TimeoutTaskEntry
+{
+ /**
+ * DLL
+ */
+ struct TimeoutTaskEntry *prev;
+
+ /**
+ * DLL
+ */
+ struct TimeoutTaskEntry *next;
+
+ /**
+ * Timeout task
+ */
+ struct GNUNET_SCHEDULER_Task *tt;
+
+ /**
+ * GNS lookup request this timeout is for
+ */
+ struct GnsLookupRequestEntry *gns_lr;
+
+ /**
+ * Plugin operation that started the timeout
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+};
+
+
+struct ESCROW_GnsPluginOperation
+{
+ /**
+ * 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;
+
+ /**
+ * Namestore handle
+ */
+ struct GNUNET_NAMESTORE_Handle *ns_h;
+
+ /**
+ * GNS handle
+ */
+ struct GNUNET_GNS_Handle *gns_h;
+
+ /**
+ * 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;
+
+ /**
+ * Counter for the created escrow identities
+ */
+ uint8_t escrow_id_counter;
+
+ /**
+ * Number of shares
+ */
+ uint8_t shares;
+
+ /**
+ * Share threshold
+ */
+ uint8_t share_threshold;
+
+ /**
+ * Share expiration time
+ */
+ struct GNUNET_TIME_Relative share_expiration_time;
+
+ /**
+ * Continuation to be called with the restored private key
+ */
+ PkContinuation restore_pk_cont;
+
+ /**
+ * Closure for @a cont
+ */
+ void *restore_pk_cont_cls;
+
+ /**
+ * Array for the restored keyshares
+ */
+ sss_Keyshare *restored_keyshares;
+
+ /**
+ * Identity operation for the create of the restored ego
+ */
+ struct GNUNET_IDENTITY_Operation *id_op;
+
+ /**
+ * The ego
+ */
+ struct GNUNET_IDENTITY_Ego *ego;
+
+ /**
+ * The anchor
+ */
+ const struct GNUNET_ESCROW_Anchor *anchor;
+
+ /**
+ * The name of the ego
+ */
+ char *egoName;
+
+ /**
+ * Private key of the ego
+ */
+ struct GNUNET_CRYPTO_EcdsaPrivateKey pk;
+
+ /**
+ * User secret string
+ */
+ char *userSecret;
+
+ /**
+ * DLL head for identity operations
+ */
+ struct IdentityOperationEntry *id_ops_head;
+
+ /**
+ * DLL tail for identity operations
+ */
+ struct IdentityOperationEntry *id_ops_tail;
+
+ /**
+ * DLL head for escrow private keys
+ */
+ struct PkEntry *escrow_pks_head;
+
+ /**
+ * DLL tail for escrow private keys
+ */
+ struct PkEntry *escrow_pks_tail;
+
+ /**
+ * DLL head for namestore queue entries
+ */
+ struct NamestoreQueueEntry *ns_qes_head;
+
+ /**
+ * DLL tail for namestore queue entries
+ */
+ struct NamestoreQueueEntry *ns_qes_tail;
+
+ /**
+ * DLL head for GNS lookup requests
+ */
+ struct GnsLookupRequestEntry *gns_lrs_head;
+
+ /**
+ * DLL tail for GNS lookup requests
+ */
+ struct GnsLookupRequestEntry *gns_lrs_tail;
+
+ /**
+ * DLL head for GNS timeout tasks
+ */
+ struct TimeoutTaskEntry *tts_head;
+
+ /**
+ * DLL tail for GNS timeout tasks
+ */
+ struct TimeoutTaskEntry *tts_tail;
+};
+
+/**
+ * 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_GnsPluginOperation *p_op;
+ struct IdentityOperationEntry *curr_id_op, *next_id_op;
+ struct PkEntry *curr_pk, *next_pk;
+ struct NamestoreQueueEntry *curr_ns_qe, *next_ns_qe;
+ struct GnsLookupRequestEntry *curr_gns_lr, *next_gns_lr;
+ struct TimeoutTaskEntry *curr_tt, *next_tt;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)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->userSecret)
+ GNUNET_free (p_op->userSecret);
+ /* clean up identity operation list */
+ for (curr_id_op = p_op->id_ops_head; NULL != curr_id_op; curr_id_op = next_id_op)
+ {
+ next_id_op = curr_id_op->next;
+ GNUNET_CONTAINER_DLL_remove (p_op->id_ops_head,
+ p_op->id_ops_tail,
+ curr_id_op);
+ GNUNET_IDENTITY_cancel (curr_id_op->id_op);
+ GNUNET_free (curr_id_op->pk);
+ GNUNET_free (curr_id_op->name);
+ GNUNET_free (curr_id_op);
+ }
+ /* clean up escrow pk list */
+ for (curr_pk = p_op->escrow_pks_head; NULL != curr_pk; curr_pk = next_pk)
+ {
+ next_pk = curr_pk->next;
+ GNUNET_CONTAINER_DLL_remove (p_op->escrow_pks_head,
+ p_op->escrow_pks_tail,
+ curr_pk);
+ GNUNET_free (curr_pk);
+ }
+ /* clean up namestore operation list */
+ for (curr_ns_qe = p_op->ns_qes_head; NULL != curr_ns_qe; curr_ns_qe = next_ns_qe)
+ {
+ next_ns_qe = curr_ns_qe->next;
+ GNUNET_CONTAINER_DLL_remove (p_op->ns_qes_head,
+ p_op->ns_qes_tail,
+ curr_ns_qe);
+ // also frees the curr_ns_qe->ns_qe
+ GNUNET_NAMESTORE_cancel (curr_ns_qe->ns_qe);
+ GNUNET_free (curr_ns_qe);
+ }
+ /* clean up GNS lookup request list */
+ for (curr_gns_lr = p_op->gns_lrs_head; NULL != curr_gns_lr; curr_gns_lr = next_gns_lr)
+ {
+ next_gns_lr = curr_gns_lr->next;
+ GNUNET_CONTAINER_DLL_remove (p_op->gns_lrs_head,
+ p_op->gns_lrs_tail,
+ curr_gns_lr);
+ GNUNET_GNS_lookup_cancel (curr_gns_lr->lr);
+ GNUNET_free (curr_gns_lr);
+ }
+ /* clean up timeout task list */
+ for (curr_tt = p_op->tts_head; NULL != curr_tt; curr_tt = next_tt)
+ {
+ next_tt = curr_tt->next;
+ GNUNET_CONTAINER_DLL_remove (p_op->tts_head,
+ p_op->tts_tail,
+ curr_tt);
+ GNUNET_SCHEDULER_cancel (curr_tt->tt);
+ GNUNET_free (curr_tt);
+ }
+ /* free the keyshares array */
+ if (NULL != p_op->restored_keyshares)
+ GNUNET_free (p_op->restored_keyshares);
+ /* disconnect from namestore service */
+ if (NULL != p_op->ns_h)
+ GNUNET_NAMESTORE_disconnect (p_op->ns_h);
+ /* disconnect from GNS service */
+ if (NULL != p_op->gns_h)
+ GNUNET_GNS_disconnect (p_op->gns_h);
+ /* cancel scheduled task */
+ if (NULL != p_op->sched_task)
+ GNUNET_SCHEDULER_cancel (p_op->sched_task);
+ /* cancel identity operation */
+ if (NULL != p_op->id_op)
+ GNUNET_IDENTITY_cancel (p_op->id_op);
+ 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_GnsPluginOperation *p_op;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)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
+verify_cont (void *cls)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)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
+handle_restore_error (void *cls)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+ p_op->sched_task = NULL;
+ p_op->cont (p_op->ego_wrap);
+
+ cleanup_plugin_operation (plugin_op_wrap);
+}
+
+
+sss_Keyshare *
+split_private_key (struct ESCROW_GnsPluginOperation *p_op)
+{
+ sss_Keyshare *keyshares;
+
+ keyshares = GNUNET_malloc (sizeof (sss_Keyshare) * p_op->shares);
+ sss_create_keyshares (keyshares,
+ p_op->pk.d,
+ p_op->shares,
+ p_op->share_threshold);
+
+ return keyshares;
+}
+
+
+static void
+keyshare_distribution_finished (void *cls)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All keyshares distributed\n");
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ p_op->anchor_wrap->anchor = ESCROW_build_anchor (GNUNET_ESCROW_KEY_GNS,
+ p_op->egoName,
+ p_op->userSecret,
+ strlen (p_op->userSecret));
+
+ /* update escrow status, i.e. set the last escrow method */
+ ESCROW_update_escrow_status_put (p_op->h, p_op->ego, "gns");
+
+ /* call the continuation */
+ start_cont (plugin_op_wrap);
+}
+
+
+static void
+keyshare_distributed (void *cls,
+ int32_t success,
+ const char *emsg)
+{
+ struct NamestoreQueueEntry *ns_qe = cls;
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Keyshare distributed\n");
+
+ plugin_op_wrap = ns_qe->plugin_op_wrap;
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ // remove qe from our list
+ GNUNET_CONTAINER_DLL_remove (p_op->ns_qes_head,
+ p_op->ns_qes_tail,
+ ns_qe);
+
+ if (GNUNET_SYSERR == success)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to store keyshare %s\n",
+ emsg);
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->anchor_wrap->emsg = _ ("Keyshare distribution failed!\n");
+ p_op->cont (p_op->anchor_wrap);
+ // this also cancels all running namestore operations
+ cleanup_plugin_operation (plugin_op_wrap);
+ return;
+ }
+
+ // check if all namestore operations are finished
+ GNUNET_free (ns_qe);
+ if (NULL == p_op->ns_qes_head)
+ {
+ // schedule the further execution, lets NAMESTORE clean up its operation list
+ GNUNET_SCHEDULER_add_now (&keyshare_distribution_finished, plugin_op_wrap);
+ }
+}
+
+
+static char *
+get_label (const char *userSecret)
+{
+ char *label;
+ struct GNUNET_HashCode hash;
+ struct GNUNET_CRYPTO_HashAsciiEncoded hashEnc;
+
+ // the label is the hash of the userSecret
+ GNUNET_CRYPTO_hash (userSecret, strlen (userSecret), &hash);
+ GNUNET_CRYPTO_hash_to_enc (&hash, &hashEnc);
+ label = GNUNET_strdup ((char *)hashEnc.encoding);
+
+ return label;
+}
+
+
+static int
+distribute_keyshares (struct ESCROW_PluginOperationWrapper *plugin_op_wrap,
+ sss_Keyshare *keyshares)
+{
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct GNUNET_NAMESTORE_Handle *ns_h;
+ struct NamestoreQueueEntry *curr_ns_qe;
+ struct PkEntry *curr_pk;
+ char *curr_label;
+ struct GNUNET_GNSRECORD_Data curr_rd[1];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Distributing keyshares\n");
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ ns_h = GNUNET_NAMESTORE_connect (p_op->h->cfg);
+ p_op->ns_h = ns_h;
+
+ for (curr_pk = p_op->escrow_pks_head; NULL != curr_pk; curr_pk = curr_pk->next)
+ {
+ curr_label = get_label (p_op->userSecret);
+ curr_ns_qe = GNUNET_new (struct NamestoreQueueEntry);
+
+ curr_rd[0].data_size = sizeof (sss_Keyshare);
+ curr_rd[0].data = keyshares[curr_pk->i];
+ curr_rd[0].record_type = GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE;
+ curr_rd[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
+ curr_rd[0].expiration_time = p_op->share_expiration_time.rel_value_us;
+
+ curr_ns_qe->plugin_op_wrap = plugin_op_wrap;
+ curr_ns_qe->ns_qe = GNUNET_NAMESTORE_records_store (ns_h,
+ &curr_pk->pk,
+ curr_label,
+ 1,
+ curr_rd,
+ &keyshare_distributed,
+ curr_ns_qe);
+ GNUNET_CONTAINER_DLL_insert_tail (p_op->ns_qes_head,
+ p_op->ns_qes_tail,
+ curr_ns_qe);
+ GNUNET_free (curr_label);
+ }
+
+ return GNUNET_OK;
+}
+
+
+void
+escrow_ids_finished (struct ESCROW_PluginOperationWrapper *plugin_op_wrap)
+{
+ struct ESCROW_GnsPluginOperation *p_op;
+ sss_Keyshare *keyshares;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All escrow identities created\n");
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ /* split the private key (SSS) */
+ keyshares = split_private_key (p_op);
+ if (NULL == keyshares)
+ {
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->anchor_wrap->emsg = _ ("Failed to split the key!\n");
+ start_cont (plugin_op_wrap);
+ return;
+ }
+
+ /* distribute the shares to the identities */
+ if (GNUNET_OK != distribute_keyshares (plugin_op_wrap, keyshares))
+ {
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->anchor_wrap->emsg = _ ("Failed to distribute the keyshares!\n");
+ start_cont (plugin_op_wrap);
+ return;
+ }
+
+ /* operation continues in keyshare_distribution_finished
+ after all keyshares have been distributed */
+}
+
+
+void
+escrow_id_created (void *cls,
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk,
+ const char *emsg)
+{
+ struct IdentityOperationEntry *id_op = cls;
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct PkEntry *pk_entry;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Escrow identity %d created\n", id_op->i);
+
+ plugin_op_wrap = id_op->plugin_op_wrap;
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ if (NULL == pk)
+ {
+ if (NULL != emsg)
+ {
+ fprintf (stderr,
+ "Identity create operation returned with error: %s\n",
+ emsg);
+ p_op->anchor_wrap->emsg = _ ("Identity create failed!\n");
+ }
+ else
+ p_op->anchor_wrap->emsg = _ ("Failed to create ego!\n");
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->cont (p_op->anchor_wrap);
+ // this also cancels all running identity operations
+ cleanup_plugin_operation (plugin_op_wrap);
+ return;
+ }
+
+ /* escrow identity successfully created */
+ GNUNET_CONTAINER_DLL_remove (p_op->id_ops_head,
+ p_op->id_ops_tail,
+ id_op);
+
+ /* insert pk into our list */
+ pk_entry = GNUNET_new (struct PkEntry);
+ pk_entry->pk = *pk;
+ pk_entry->i = id_op->i;
+ GNUNET_CONTAINER_DLL_insert_tail (p_op->escrow_pks_head,
+ p_op->escrow_pks_tail,
+ pk_entry);
+
+ GNUNET_free (id_op);
+
+ /* check if this was the last id_op */
+ p_op->escrow_id_counter++;
+ if (p_op->escrow_id_counter == p_op->shares)
+ {
+ escrow_ids_finished (plugin_op_wrap);
+ }
+}
+
+
+static uint8_t
+count_digits (uint8_t n)
+{
+ uint8_t i = 0;
+
+ do
+ {
+ i++;
+ n /= 10;
+ } while (n != 0);
+
+ return i;
+}
+
+
+static char *
+get_escrow_id_name (const char *name,
+ uint8_t i)
+{
+ char *str, *prefix, *number;
+ uint8_t j = 0;
+
+ prefix = "escrow-id_";
+ number = GNUNET_malloc (count_digits (i) + 1);
+ sprintf (number, "%d", i);
+
+ str = GNUNET_malloc (strlen (prefix)
+ + strlen (name)
+ + 1
+ + strlen (number)
+ + 1);
+
+ memcpy (str, prefix, strlen (prefix));
+ j += strlen (prefix);
+ memcpy (str + j, name, strlen (name));
+ j += strlen (name);
+ str[j++] = '_';
+ memcpy (str + j, number, strlen (number));
+ j += strlen (number);
+ str[j] = '\0';
+
+ GNUNET_free (number);
+
+ return str;
+}
+
+
+static int
+escrow_id_exists (const char *name,
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk)
+{
+ struct EgoEntry *curr;
+
+ for (curr = ph.ego_head; NULL != curr; curr = curr->next)
+ {
+ if (0 == strcmp (name, curr->identifier))
+ {
+ if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (curr->ego),
+ pk,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
+ return GNUNET_YES;
+ else // the escrow id's name exists for an ego, but the pk is wrong
+ return GNUNET_SYSERR;
+ }
+ }
+
+ return GNUNET_NO;
+}
+
+
+static struct GNUNET_CRYPTO_EcdsaPrivateKey *
+derive_private_key (const char *name,
+ const char *password,
+ uint8_t i)
+{
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *pk;
+ static const char ctx[] = "gnunet-escrow-id-ctx";
+
+ pk = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
+ GNUNET_CRYPTO_kdf (pk,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
+ ctx, strlen (ctx),
+ password, strlen (password),
+ name, strlen (name),
+ &i, 1,
+ NULL);
+
+ pk->d[0] &= 248;
+ pk->d[31] &= 127;
+ pk->d[31] |= 64;
+
+ return pk;
+}
+
+
+static void
+handle_existing_wrong_ego_deletion (void *cls,
+ const char *emsg)
+{
+ struct IdentityOperationEntry *curr_id_op = cls;
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ plugin_op_wrap = curr_id_op->plugin_op_wrap;
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ if (NULL != emsg)
+ {
+ fprintf (stderr,
+ "Identity create operation returned with error: %s\n",
+ emsg);
+ p_op->anchor_wrap->emsg = _ ("Identity delete of wrong existing ego failed!\n");
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->cont (p_op->anchor_wrap);
+ // this also cancels all running identity operations
+ cleanup_plugin_operation (plugin_op_wrap);
+ return;
+ }
+
+ /* no error occured, so create the new identity */
+ // the IdentityOperationEntry is reused, so only the id_op is updated
+ curr_id_op->id_op = GNUNET_IDENTITY_create (identity_handle,
+ curr_id_op->name,
+ curr_id_op->pk,
+ &escrow_id_created,
+ curr_id_op);
+}
+
+
+static void
+create_escrow_identities (struct ESCROW_PluginOperationWrapper *plugin_op_wrap,
+ const char *name)
+{
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *curr_pk;
+ char *curr_name;
+ struct IdentityOperationEntry *curr_id_op;
+ struct PkEntry *curr_pk_entry;
+ int exists_ret;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating escrow identities\n");
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ for (uint8_t i = 0; i < p_op->shares; i++)
+ {
+ curr_pk = derive_private_key (name, p_op->userSecret, i);
+ curr_name = get_escrow_id_name (name, i);
+
+ // check if the escrow identity already exists
+ exists_ret = escrow_id_exists (curr_name, curr_pk);
+ if (GNUNET_SYSERR == exists_ret)
+ {
+ /* an ego with identifier name but the wrong pk exists, delete it first */
+ curr_id_op = GNUNET_new (struct IdentityOperationEntry);
+ curr_id_op->pk = curr_pk;
+ curr_id_op->name = curr_name;
+ curr_id_op->i = i;
+ curr_id_op->plugin_op_wrap = plugin_op_wrap;
+ curr_id_op->id_op = GNUNET_IDENTITY_delete (identity_handle,
+ curr_name,
+ &handle_existing_wrong_ego_deletion,
+ curr_id_op);
+ GNUNET_CONTAINER_DLL_insert (p_op->id_ops_head,
+ p_op->id_ops_tail,
+ curr_id_op);
+ }
+ else if (GNUNET_YES == exists_ret)
+ {
+ // the escrow id already exists, so insert the pk into our list
+ curr_pk_entry = GNUNET_new (struct PkEntry);
+ curr_pk_entry->pk = *curr_pk;
+ curr_pk_entry->i = i;
+ GNUNET_CONTAINER_DLL_insert (p_op->escrow_pks_head,
+ p_op->escrow_pks_tail,
+ curr_pk_entry);
+
+ p_op->escrow_id_counter++;
+ if (p_op->escrow_id_counter == p_op->shares)
+ {
+ escrow_ids_finished (plugin_op_wrap);
+ }
+ }
+ else // GNUNET_NO
+ {
+ /* store the identity operation in our list */
+ curr_id_op = GNUNET_new (struct IdentityOperationEntry);
+ curr_id_op->pk = curr_pk;
+ curr_id_op->name = curr_name;
+ curr_id_op->i = i;
+ curr_id_op->plugin_op_wrap = plugin_op_wrap;
+ curr_id_op->id_op = GNUNET_IDENTITY_create (identity_handle,
+ curr_name,
+ curr_pk,
+ &escrow_id_created,
+ curr_id_op);
+ GNUNET_CONTAINER_DLL_insert (p_op->id_ops_head,
+ p_op->id_ops_tail,
+ curr_id_op);
+ }
+ }
+}
+
+
+static void
+handle_config_load_error (struct ESCROW_PluginOperationWrapper *plugin_op_wrap,
+ char *emsg)
+{
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ if (NULL != p_op->anchor_wrap)
+ {
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->anchor_wrap->emsg = emsg;
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
+ return;
+ }
+
+ if (NULL != p_op->verify_wrap)
+ {
+ p_op->verify_wrap->verificationResult = GNUNET_ESCROW_INVALID;
+ p_op->verify_wrap->emsg = emsg;
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_cont, plugin_op_wrap);
+ return;
+ }
+
+ if (NULL != p_op->ego_wrap)
+ {
+ p_op->ego_wrap->ego = NULL;
+ p_op->ego_wrap->emsg = emsg;
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&handle_restore_error, plugin_op_wrap);
+ return;
+ }
+}
+
+
+static int
+load_keyshare_config (struct ESCROW_PluginOperationWrapper *plugin_op_wrap)
+{
+ struct ESCROW_GnsPluginOperation *p_op;
+ unsigned long long shares, share_threshold;
+ struct GNUNET_TIME_Relative share_expiration_time;
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (p_op->h->cfg,
+ "escrow",
+ "gns_shares",
+ &shares))
+ {
+ handle_config_load_error (plugin_op_wrap,
+ "Number of shares not specified in config!\n");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (p_op->h->cfg,
+ "escrow",
+ "gns_share_threshold",
+ &share_threshold))
+ {
+ handle_config_load_error (plugin_op_wrap,
+ "Share threshold not specified in config\n");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (p_op->h->cfg,
+ "escrow",
+ "gns_share_expiration_time",
+ &share_expiration_time))
+ {
+ handle_config_load_error (plugin_op_wrap,
+ "Share expiration time not specified in config\n");
+ return GNUNET_SYSERR;
+ }
+ p_op->shares = (uint8_t)shares;
+ p_op->share_threshold = (uint8_t)share_threshold;
+ p_op->share_expiration_time.rel_value_us = share_expiration_time.rel_value_us;
+
+ return GNUNET_OK;
+}
+
+
+static void
+continue_start (void *cls)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct GNUNET_TIME_Relative delay;
+ struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub;
+ struct EgoEntry *ego_entry;
+ char *pub_keystring;
+
+ p_op = (struct ESCROW_GnsPluginOperation *)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);
+
+ /* load config */
+ if (GNUNET_OK != load_keyshare_config (plugin_op_wrap))
+ {
+ p_op->anchor_wrap->anchor = NULL;
+ p_op->anchor_wrap->emsg = _ ("Failed to load keyshare configuration parameters!\n");
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
+ return;
+ }
+
+ /* create the escrow identities */
+ create_escrow_identities (plugin_op_wrap, p_op->egoName);
+
+ /* operation continues in escrow_ids_finished
+ after all escrow identities are created */
+}
+
+
+/**
+ * Start the GNS escrow of the key
+ *
+ * @param h the handle for the escrow component
+ * @param ego the identity ego containing the private key
+ * @param userSecret the user secret (used for derivation of escrow identities)
+ * this has to be UNIQUE in the whole network!
+ * @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_gns_key_escrow (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *userSecret,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct ESCROW_Plugin_AnchorContinuationWrapper *w;
+ struct GNUNET_TIME_Relative delay;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting GNS escrow\n");
+
+ // create a new GNS 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_GnsPluginOperation);
+ GNUNET_CONTAINER_DLL_insert_tail (ph.plugin_op_head,
+ ph.plugin_op_tail,
+ plugin_op_wrap);
+
+ p_op = (struct ESCROW_GnsPluginOperation *)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 || NULL == userSecret)
+ {
+ w->anchor = NULL;
+ if (NULL == ego)
+ w->emsg = _ ("ESCROW_put was called with ego == NULL\n");
+ else if (NULL == userSecret)
+ w->emsg = _ ("GNS escrow needs a user secret!\n");
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&start_cont, plugin_op_wrap);
+ return plugin_op_wrap;
+ }
+ p_op->pk = *GNUNET_IDENTITY_ego_get_private_key (ego);
+ p_op->userSecret = GNUNET_strdup (userSecret);
+
+ 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 uint8_t
+count_keyshares (const sss_Keyshare *keyshares, uint8_t n)
+{
+ uint8_t i, c;
+ sss_Keyshare null_ks;
+
+ memset (null_ks, 0, sizeof (sss_Keyshare));
+
+ c = 0;
+ for (i = 0; i < n; i++)
+ {
+ if (0 != memcmp (keyshares[i], &null_ks, sizeof (sss_Keyshare)))
+ c++;
+ }
+
+ return c;
+}
+
+
+static void
+remove_empty_keyshares (sss_Keyshare *keyshares, uint8_t n)
+{
+ uint8_t i, j;
+ sss_Keyshare null_ks;
+
+ memset (null_ks, 0, sizeof (sss_Keyshare));
+
+ for (i = j = 0; i < n; i++)
+ {
+ if (0 != memcmp (keyshares[i], &null_ks, sizeof (sss_Keyshare)))
+ {
+ memcpy (keyshares[j], keyshares[i], sizeof (sss_Keyshare));
+ j++;
+ }
+ }
+}
+
+
+static void
+process_keyshares (void *cls)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *pk;
+ uint8_t keyshares_count;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+
+ /* check if enough keyshares have been restored */
+ keyshares_count = count_keyshares (p_op->restored_keyshares, p_op->shares);
+ if (keyshares_count < p_op->share_threshold)
+ {
+ /* the key cannot be restored */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "need %hhu shares, but could only get %hhu\n",
+ p_op->share_threshold,
+ keyshares_count);
+
+ if (NULL != p_op->verify_wrap) // this was called by a verify operation
+ {
+ p_op->verify_wrap->emsg =
+ _ ("key could not be restored, failed to get enough keyshares\n");
+ p_op->restore_pk_cont (p_op->restore_pk_cont_cls, NULL);
+ return;
+ }
+
+ if (NULL != p_op->ego_wrap) // this was called by a restore operation
+ {
+ p_op->ego_wrap->emsg =
+ _ ("key could not be restored, failed to get enough keyshares\n");
+ p_op->restore_pk_cont (p_op->restore_pk_cont_cls, NULL);
+ return;
+ }
+ }
+
+ /* combine the shares */
+ if (keyshares_count != p_op->shares)
+ remove_empty_keyshares (p_op->restored_keyshares, p_op->shares);
+
+ pk = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
+ sss_combine_keyshares (pk->d,
+ p_op->restored_keyshares,
+ keyshares_count);
+
+ p_op->restore_pk_cont (p_op->restore_pk_cont_cls, pk);
+}
+
+
+static void
+process_gns_lookup_result (void *cls,
+ uint32_t rd_count,
+ const struct GNUNET_GNSRECORD_Data *rd)
+{
+ struct GnsLookupRequestEntry *gns_lr = cls;
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+ sss_Keyshare keyshare;
+ char keyshare_string[64], *end;
+
+ plugin_op_wrap = gns_lr->plugin_op_wrap;
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+
+ /* remove gns_lr from our list */
+ GNUNET_CONTAINER_DLL_remove (p_op->gns_lrs_head,
+ p_op->gns_lrs_tail,
+ gns_lr);
+
+ /* cancel the timeout for that lookup */
+ GNUNET_CONTAINER_DLL_remove (p_op->tts_head,
+ p_op->tts_tail,
+ gns_lr->tt);
+ GNUNET_SCHEDULER_cancel (gns_lr->tt->tt);
+ GNUNET_free (gns_lr->tt);
+
+ if (1 != rd_count)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "did not get exactly _one_ record from GNS\n");
+ goto END;
+ }
+
+ if (sizeof (sss_Keyshare) != rd[0].data_size)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "the size of the GNS record differs from the size of a keyshare\n");
+ goto END;
+ }
+
+ GNUNET_memcpy (&keyshare,
+ rd[0].data,
+ rd[0].data_size);
+
+ end = GNUNET_STRINGS_data_to_string (&keyshare,
+ sizeof (sss_Keyshare),
+ keyshare_string,
+ sizeof (keyshare_string));
+ GNUNET_break (NULL != end);
+ *end = '\0';
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "got keyshare %s from GNS\n", keyshare_string);
+
+ GNUNET_memcpy (p_op->restored_keyshares[gns_lr->i],
+ &keyshare,
+ sizeof (sss_Keyshare));
+
+ END:
+ GNUNET_free (gns_lr);
+
+ // check if this was the last gns_lr, i.e. our list is empty
+ if (NULL == p_op->gns_lrs_head)
+ {
+ // schedule the further execution, lets GNS clean up its operation list
+ GNUNET_SCHEDULER_add_now (&process_keyshares, plugin_op_wrap);
+ }
+}
+
+
+static void
+timeout_gns_request (void *cls)
+{
+ struct TimeoutTaskEntry *curr_tt = cls;
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ plugin_op_wrap = curr_tt->plugin_op_wrap;
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+
+ /* remove timeout task from our list */
+ GNUNET_CONTAINER_DLL_remove (p_op->tts_head,
+ p_op->tts_tail,
+ curr_tt);
+
+ /* cancel the GNS lookup and remove it from our list */
+ GNUNET_GNS_lookup_cancel (curr_tt->gns_lr->lr);
+ GNUNET_CONTAINER_DLL_remove (p_op->gns_lrs_head,
+ p_op->gns_lrs_tail,
+ curr_tt->gns_lr);
+ GNUNET_free (curr_tt->gns_lr);
+
+ GNUNET_free (curr_tt);
+
+ /* check if this was the last pending GNS lookup */
+ if (NULL == p_op->gns_lrs_head)
+ {
+ // no need to schedule, as the timeout is already scheduled
+ process_keyshares (plugin_op_wrap);
+ }
+}
+
+
+static char *
+get_user_secret_from_anchor (const struct GNUNET_ESCROW_Anchor *anchor)
+{
+ char *userSecret;
+
+ userSecret = GNUNET_malloc (anchor->size);
+ GNUNET_memcpy (userSecret, &anchor[1], anchor->size);
+
+ return userSecret;
+}
+
+
+static void
+restore_private_key (struct ESCROW_PluginOperationWrapper *plugin_op_wrap,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ PkContinuation cont,
+ void *cont_cls)
+{
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *curr_escrow_pk;
+ struct GNUNET_CRYPTO_EcdsaPublicKey curr_escrow_pub;
+ char *label;
+ struct GnsLookupRequestEntry *curr_gns_lr;
+ struct GNUNET_TIME_Relative delay;
+ struct TimeoutTaskEntry *curr_tt;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+
+ p_op->gns_h = GNUNET_GNS_connect (p_op->h->cfg);
+ p_op->restore_pk_cont = cont;
+ p_op->restore_pk_cont_cls = cont_cls;
+ p_op->restored_keyshares = GNUNET_malloc (sizeof (sss_Keyshare) * p_op->shares);
+ // ensure that the array is initialized with 0, as this is needed for counting the shares
+ memset (p_op->restored_keyshares, 0, sizeof (sss_Keyshare) * p_op->shares);
+ p_op->userSecret = get_user_secret_from_anchor (anchor);
+
+ label = get_label (p_op->userSecret);
+
+ // init delay to 2s
+ delay.rel_value_us = 2 * GNUNET_TIME_relative_get_second_().rel_value_us;
+
+ for (uint8_t i = 0; i < p_op->shares; i++)
+ {
+ curr_escrow_pk = derive_private_key (anchor->egoName, p_op->userSecret, i);
+
+ curr_gns_lr = GNUNET_new (struct GnsLookupRequestEntry);
+ curr_gns_lr->plugin_op_wrap = plugin_op_wrap;
+ curr_gns_lr->i = i;
+ GNUNET_CRYPTO_ecdsa_key_get_public (curr_escrow_pk, &curr_escrow_pub);
+ curr_gns_lr->lr = GNUNET_GNS_lookup (p_op->gns_h,
+ label,
+ &curr_escrow_pub,
+ GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE,
+ GNUNET_GNS_LO_DEFAULT,
+ &process_gns_lookup_result,
+ curr_gns_lr);
+ GNUNET_CONTAINER_DLL_insert_tail (p_op->gns_lrs_head,
+ p_op->gns_lrs_tail,
+ curr_gns_lr);
+
+ /* start timeout for GNS lookup (cancels the lr if not yet finished) */
+ curr_tt = GNUNET_new (struct TimeoutTaskEntry);
+ curr_tt->gns_lr = curr_gns_lr;
+ curr_tt->plugin_op_wrap = plugin_op_wrap;
+ curr_tt->tt = GNUNET_SCHEDULER_add_delayed (delay,
+ &timeout_gns_request,
+ curr_tt);
+ GNUNET_CONTAINER_DLL_insert_tail (p_op->tts_head,
+ p_op->tts_tail,
+ curr_tt);
+
+ // set the timeout task for the current gns lr entry
+ curr_gns_lr->tt = curr_tt;
+ }
+ GNUNET_free (label);
+}
+
+
+static void
+verify_restored_pk (void *cls,
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *ego_pk;
+ int verificationResult;
+
+ p_op = (struct ESCROW_GnsPluginOperation *)plugin_op_wrap->plugin_op;
+
+ if (NULL == pk)
+ verificationResult = GNUNET_ESCROW_INVALID;
+ else
+ {
+ ego_pk = GNUNET_IDENTITY_ego_get_private_key (p_op->ego);
+ verificationResult = memcmp (pk,
+ ego_pk,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
+ == 0 ? GNUNET_ESCROW_VALID : GNUNET_ESCROW_INVALID;
+ }
+
+ // check if all shares could be restored
+ if (GNUNET_ESCROW_VALID == verificationResult &&
+ count_keyshares (p_op->restored_keyshares, p_op->shares) < p_op->shares)
+ verificationResult = GNUNET_ESCROW_SHARES_MISSING;
+
+ /* update the escrow status if valid */
+ if (GNUNET_ESCROW_VALID == verificationResult)
+ ESCROW_update_escrow_status_verify (p_op->h, p_op->ego, "gns");
+
+ p_op->verify_wrap->verificationResult = verificationResult;
+ verify_cont (plugin_op_wrap);
+}
+
+
+static void
+continue_verify (void *cls)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct GNUNET_TIME_Relative delay;
+ struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub;
+ struct EgoEntry *ego_entry;
+ char *pub_keystring;
+
+ p_op = (struct ESCROW_GnsPluginOperation *)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->verify_wrap->verificationResult = GNUNET_ESCROW_INVALID;
+ p_op->verify_wrap->emsg = _ ("Identity was not found in plugin!\n");
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_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;
+ }
+
+ /* load config */
+ if (GNUNET_OK != load_keyshare_config (plugin_op_wrap))
+ {
+ p_op->verify_wrap->verificationResult = GNUNET_ESCROW_INVALID;
+ p_op->verify_wrap->emsg = _ ("Failed to load keyshare configuration parameters!\n");
+ p_op->sched_task = GNUNET_SCHEDULER_add_now (&verify_cont, plugin_op_wrap);
+ return;
+ }
+
+ restore_private_key (plugin_op_wrap,
+ p_op->anchor,
+ &verify_restored_pk,
+ plugin_op_wrap);
+}
+
+
+/**
+ * Verify the GNS 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_gns_key_escrow (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct ESCROW_Plugin_VerifyContinuationWrapper *w;
+ struct GNUNET_TIME_Relative delay;
+
+ // create a new GNS 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_GnsPluginOperation);
+ GNUNET_CONTAINER_DLL_insert_tail (ph.plugin_op_head,
+ ph.plugin_op_tail,
+ plugin_op_wrap);
+
+ p_op = (struct ESCROW_GnsPluginOperation *)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_GNS != anchor->method)
+ {
+ w->verificationResult = GNUNET_ESCROW_INVALID;
+ w->emsg = _ ("This anchor was not created using GNS 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;
+}
+
+
+void
+ego_created (struct GNUNET_IDENTITY_Ego *ego)
+{
+ struct ESCROW_PluginOperationWrapper *curr;
+ struct ESCROW_GnsPluginOperation *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_GnsPluginOperation *)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);
+}
+
+
+static void
+id_create_finished (void *cls,
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk,
+ const char *emsg)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+
+ 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
+restore_ego_from_pk (void *cls,
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap = cls;
+ struct ESCROW_GnsPluginOperation *p_op;
+
+ p_op = (struct ESCROW_GnsPluginOperation*)plugin_op_wrap->plugin_op;
+
+ if (NULL == pk)
+ {
+ p_op->ego_wrap->ego = NULL;
+ handle_restore_error (plugin_op_wrap);
+ return;
+ }
+
+ p_op->id_op = GNUNET_IDENTITY_create (identity_handle,
+ p_op->egoName,
+ pk,
+ &id_create_finished,
+ plugin_op_wrap);
+}
+
+
+/**
+ * Restore the key from GNS 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_gns_key_escrow (struct GNUNET_ESCROW_Handle *h,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id)
+{
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+ struct ESCROW_GnsPluginOperation *p_op;
+ struct ESCROW_Plugin_EgoContinuationWrapper *w;
+
+ // create a new GNS 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_GnsPluginOperation);
+ GNUNET_CONTAINER_DLL_insert_tail (ph.plugin_op_head,
+ ph.plugin_op_tail,
+ plugin_op_wrap);
+
+ p_op = (struct ESCROW_GnsPluginOperation *)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;
+ p_op->egoName = GNUNET_strdup (anchor->egoName);
+ p_op->anchor = anchor;
+
+ 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;
+ }
+
+ /* load config */
+ if (GNUNET_OK != load_keyshare_config (plugin_op_wrap))
+ return plugin_op_wrap;
+
+ restore_private_key (plugin_op_wrap,
+ anchor,
+ &restore_ego_from_pk,
+ plugin_op_wrap);
+
+ return plugin_op_wrap;
+}
+
+
+/**
+ * Get the status of a GNS 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 *
+gns_get_status (struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego)
+{
+ return ESCROW_get_escrow_status (h, ego);
+}
+
+
+/**
+ * Cancel a GNS plugin operation.
+ *
+ * @param plugin_op_wrap the plugin operation wrapper containing the operation
+ */
+void
+cancel_gns_operation (struct ESCROW_PluginOperationWrapper *plugin_op_wrap)
+{
+ struct ESCROW_PluginOperationWrapper *curr;
+
+ 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);
+ cleanup_plugin_operation (curr);
+ return;
+ }
+ }
+}
+
+
+/**
+ * IdentityInitContinuation for the GNS plugin
+ */
+void
+gns_cont_init ()
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNS plugin initialized\n");
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls Config info
+ *
+ * @return the exported block API
+ */
+void *
+libgnunet_plugin_escrow_gns_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_gns_key_escrow;
+ api->verify_key_escrow = &verify_gns_key_escrow;
+ api->restore_key = &restore_gns_key_escrow;
+ api->get_status = &gns_get_status;
+ api->cancel_plugin_operation = &cancel_gns_operation;
+
+ ph.state = ESCROW_PLUGIN_STATE_INIT;
+ ph.id_init_cont = &gns_cont_init;
+
+ 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_gns_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_gns.c */
diff --git a/src/escrow/plugin_escrow_plaintext.c b/src/escrow/plugin_escrow_plaintext.c
new file mode 100644
index 000000000..86f49955f
--- /dev/null
+++ b/src/escrow/plugin_escrow_plaintext.c
@@ -0,0 +1,673 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ 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 <inttypes.h>
+
+
+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 */
diff --git a/src/escrow/plugin_gnsrecord_escrow.c b/src/escrow/plugin_gnsrecord_escrow.c
new file mode 100644
index 000000000..82ed3af96
--- /dev/null
+++ b/src/escrow/plugin_gnsrecord_escrow.c
@@ -0,0 +1,173 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file escrow/plugin_gnsrecord_escrow.c
+ * @brief gnsrecord plugin to provide the API for escrow records
+ *
+ * @author Johannes Späth
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+#include "gnunet_gnsrecord_lib.h"
+#include "gnunet_gnsrecord_plugin.h"
+
+/**
+ * Convert the 'value' of a record to a string.
+ *
+ * @param cls closure, unused
+ * @param type type of the record
+ * @param data value in binary encoding
+ * @param data_size number of bytes in @a data
+ * @return NULL on error, otherwise human-readable representation of the value
+ */
+static char *
+value_to_string (void *cls, uint32_t type, const void *data, size_t data_size)
+{
+ switch (type)
+ {
+ case GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE:
+ return GNUNET_STRINGS_data_to_string_alloc (data, data_size);
+
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Convert human-readable version of a 'value' of a record to the binary
+ * representation.
+ *
+ * @param cls closure, unused
+ * @param type type of the record
+ * @param s human-readable string
+ * @param data set to value in binary encoding (will be allocated)
+ * @param data_size set to number of bytes in @a data
+ * @return #GNUNET_OK on success
+ */
+static int
+string_to_value (void *cls, uint32_t type, const char *s, void **data,
+ size_t *data_size)
+{
+ if (NULL == s)
+ return GNUNET_SYSERR;
+ switch (type)
+ {
+ case GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE:
+ return GNUNET_STRINGS_string_to_data (s, strlen (s), *data, *data_size);
+
+ default:
+ return GNUNET_SYSERR;
+ }
+}
+
+
+/**
+ * Mapping of record type numbers to human-readable
+ * record type names.
+ */
+static struct
+{
+ const char *name;
+ uint32_t number;
+} name_map[] = {
+ { "GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE", GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE },
+ { NULL, UINT32_MAX }
+};
+
+
+/**
+ * Convert a type name (i.e. "AAAA") to the corresponding number.
+ *
+ * @param cls closure, unused
+ * @param dns_typename name to convert
+ * @return corresponding number, UINT32_MAX on error
+ */
+static uint32_t
+typename_to_number (void *cls, const char *dns_typename)
+{
+ unsigned int i;
+
+ i = 0;
+ while ((NULL != name_map[i].name) &&
+ (0 != strcasecmp (dns_typename, name_map[i].name)))
+ i++;
+ return name_map[i].number;
+}
+
+
+/**
+ * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
+ *
+ * @param cls closure, unused
+ * @param type number of a type to convert
+ * @return corresponding typestring, NULL on error
+ */
+static const char *
+number_to_typename (void *cls, uint32_t type)
+{
+ unsigned int i;
+
+ i = 0;
+ while ((NULL != name_map[i].name) && (type != name_map[i].number))
+ i++;
+ return name_map[i].name;
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls NULL
+ * @return the exported block API
+ */
+void *
+libgnunet_plugin_gnsrecord_escrow_init (void *cls)
+{
+ struct GNUNET_GNSRECORD_PluginFunctions *api;
+
+ api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
+ api->value_to_string = &value_to_string;
+ api->string_to_value = &string_to_value;
+ api->typename_to_number = &typename_to_number;
+ api->number_to_typename = &number_to_typename;
+ return api;
+}
+
+
+/**
+ * Exit point from the plugin.
+ *
+ * @param cls the return value from #libgnunet_plugin_block_test_init
+ * @return NULL
+ */
+void *
+libgnunet_plugin_gnsrecord_escrow_done (void *cls)
+{
+ struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
+
+ GNUNET_free (api);
+ return NULL;
+}
+
+
+/* end of plugin_gnsrecord_escrow.c */
diff --git a/src/escrow/plugin_rest_escrow.c b/src/escrow/plugin_rest_escrow.c
new file mode 100644
index 000000000..796578dd6
--- /dev/null
+++ b/src/escrow/plugin_rest_escrow.c
@@ -0,0 +1,1311 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Johannes Späth
+ * @file escrow/plugin_rest_escrow.c
+ * @brief GNUnet Escrow REST plugin
+ */
+
+#include "platform.h"
+#include "gnunet_rest_plugin.h"
+#include "gnunet_escrow_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_rest_lib.h"
+#include "microhttpd.h"
+#include <jansson.h>
+
+/**
+ * Escrow Namespace
+ */
+#define GNUNET_REST_API_NS_ESCROW "/escrow"
+
+/**
+ * Escrow Put Namespace
+ */
+#define GNUNET_REST_API_NS_ESCROW_PUT "/escrow/put"
+
+/**
+ * Escrow Get Namespace
+ */
+#define GNUNET_REST_API_NS_ESCROW_GET "/escrow/get"
+
+/**
+ * Escrow Verify Namespace
+ */
+#define GNUNET_REST_API_NS_ESCROW_VERIFY "/escrow/verify"
+
+/**
+ * Escrow Status Namespace
+ */
+#define GNUNET_REST_API_NS_ESCROW_STATUS "/escrow/status"
+
+/**
+ * Error message Unknown Error
+ */
+#define GNUNET_REST_ESCROW_ERROR_UNKNOWN "Unknown Error"
+
+/**
+ * Error message Missing identity name
+ */
+#define GNUNET_REST_ESCROW_MISSING_NAME "Missing identity name"
+
+/**
+ * Error message Missing escrow anchor
+ */
+#define GNUNET_REST_ESCROW_MISSING_ANCHOR "Missing escrow anchor"
+
+/**
+ * Error message Identity not found
+ */
+#define GNUNET_REST_ESCROW_ID_NOT_FOUND "Identity not found"
+
+/**
+ * Error message Method not found
+ */
+#define GNUNET_REST_ESCROW_METHOD_NOT_FOUND "Method not found"
+
+/**
+ * Error message Escrow failed
+ */
+#define GNUNET_REST_ESCROW_ESCROW_FAILED "Escrow failed"
+
+/**
+ * Error message Restoration failed
+ */
+#define GNUNET_REST_ESCROW_RESTORE_FAILED "Restoration failed"
+
+/**
+ * Error message Got invalid status
+ */
+#define GNUNET_REST_ESCROW_INVALID_STATUS "Got invalid status"
+
+/**
+ * Error message No data
+ */
+#define GNUNET_REST_ERROR_NO_DATA "No data"
+
+/**
+ * Error message Data invalid
+ */
+#define GNUNET_REST_ERROR_DATA_INVALID "Data invalid"
+
+/**
+ * Error message Failed to parse anchor
+ */
+#define GNUNET_REST_ESCROW_ANCHOR_ERROR "Failed to parse anchor"
+
+/**
+ * Parameter anchor-data
+ */
+#define GNUNET_REST_ESCROW_PARAM_ANCHOR_DATA "anchorData"
+
+/**
+ * Parameter method
+ */
+#define GNUNET_REST_ESCROW_PARAM_METHOD "method"
+
+/**
+ * Parameter user-secret
+ */
+#define GNUNET_REST_ESCROW_PARAM_USER_SECRET "userSecret"
+
+/**
+ * Parameter pubkey
+ */
+#define GNUNET_REST_ESCROW_PARAM_PUBKEY "pubkey"
+
+/**
+ * Parameter name
+ */
+#define GNUNET_REST_ESCROW_PARAM_NAME "name"
+
+/**
+ * Parameter verification-result
+ */
+#define GNUNET_REST_ESCROW_PARAM_VERIFICATION_RESULT "verificationResult"
+
+/**
+ * Parameter last-method
+ */
+#define GNUNET_REST_ESCROW_PARAM_LAST_METHOD "lastMethod"
+
+/**
+ * Parameter last-successful-verification
+ */
+#define GNUNET_REST_ESCROW_PARAM_LAST_VERIF "lastSuccessfulVerification"
+
+/**
+ * Parameter next-recommended-verification
+ */
+#define GNUNET_REST_ESCROW_PARAM_NEXT_VERIF "nextRecommendedVerification"
+
+/**
+ * State while collecting all egos
+ */
+#define ID_REST_STATE_INIT 0
+
+/**
+ * Done collecting egos
+ */
+#define ID_REST_STATE_POST_INIT 1
+
+/**
+ * The configuration handle
+ */
+const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * HTTP methods allows for this plugin
+ */
+static char *allow_methods;
+
+/**
+ * Ego list
+ */
+static struct EgoEntry *ego_head;
+
+/**
+ * Ego list
+ */
+static struct EgoEntry *ego_tail;
+
+/**
+ * The processing state
+ */
+static int state;
+
+/**
+ * Handle to the identity service
+ */
+static struct GNUNET_IDENTITY_Handle *identity_handle;
+
+/**
+ * Handle to the escrow component
+ */
+static struct GNUNET_ESCROW_Handle *escrow_handle;
+
+/**
+ * @brief struct returned by the initialization function of the plugin
+ */
+struct Plugin
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+};
+
+/**
+ * The ego list
+ */
+struct EgoEntry
+{
+ /**
+ * DLL
+ */
+ struct EgoEntry *next;
+
+ /**
+ * DLL
+ */
+ struct EgoEntry *prev;
+
+ /**
+ * Ego Identifier
+ */
+ char *identifier;
+
+ /**
+ * Public key string
+ */
+ char *keystring;
+
+ /**
+ * The Ego
+ */
+ struct GNUNET_IDENTITY_Ego *ego;
+};
+
+/**
+ * The request handle
+ */
+struct RequestHandle
+{
+ /**
+ * DLL
+ */
+ struct RequestHandle *next;
+
+ /**
+ * DLL
+ */
+ struct RequestHandle *prev;
+
+ /**
+ * The data from the REST request
+ */
+ const char *data;
+
+ /**
+ * The name to look up
+ */
+ char *name;
+
+ /**
+ * the length of the REST data
+ */
+ size_t data_size;
+
+ /**
+ * ESCROW Operation
+ */
+ struct GNUNET_ESCROW_Operation *op;
+
+ /**
+ * Rest connection
+ */
+ struct GNUNET_REST_RequestHandle *rest_handle;
+
+ /**
+ * Desired timeout for the lookup (default is no timeout).
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * ID of a task associated with the resolution process.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+
+ /**
+ * The plugin result processor
+ */
+ GNUNET_REST_ResultProcessor proc;
+
+ /**
+ * The closure of the result processor
+ */
+ void *proc_cls;
+
+ /**
+ * The url
+ */
+ char *url;
+
+ /**
+ * Error response message
+ */
+ char *emsg;
+
+ /**
+ * Response code
+ */
+ int response_code;
+
+ /**
+ * Response object
+ */
+ json_t *resp_object;
+};
+
+/**
+ * DLL
+ */
+static struct RequestHandle *requests_head;
+
+/**
+ * DLL
+ */
+static struct RequestHandle *requests_tail;
+
+/**
+ * Cleanup lookup handle
+ * @param handle Handle to clean up
+ */
+static void
+cleanup_handle (void *cls)
+{
+ struct RequestHandle *handle = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
+ if (NULL != handle->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (handle->timeout_task);
+ handle->timeout_task = NULL;
+ }
+
+ if (NULL != handle->url)
+ GNUNET_free (handle->url);
+ if (NULL != handle->emsg)
+ GNUNET_free (handle->emsg);
+ if (NULL != handle->name)
+ GNUNET_free (handle->name);
+ GNUNET_CONTAINER_DLL_remove (requests_head,
+ requests_tail,
+ handle);
+ GNUNET_free (handle);
+}
+
+
+/**
+ * Task run on errors. Reports an error and cleans up everything.
+ *
+ * @param cls the `struct RequestHandle`
+ */
+static void
+do_error (void *cls)
+{
+ struct RequestHandle *handle = cls;
+ struct MHD_Response *resp;
+ json_t *json_error = json_object ();
+ char *response;
+
+ if (NULL == handle->emsg)
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ERROR_UNKNOWN);
+
+ json_object_set_new (json_error, "error", json_string (handle->emsg));
+
+ if (0 == handle->response_code)
+ handle->response_code = MHD_HTTP_OK;
+ response = json_dumps (json_error, 0);
+ resp = GNUNET_REST_create_response (response);
+ MHD_add_response_header (resp, "Content-Type", "application/json");
+ handle->proc (handle->proc_cls, resp, handle->response_code);
+ json_decref (json_error);
+ GNUNET_free (response);
+ GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
+}
+
+
+static enum GNUNET_ESCROW_Key_Escrow_Method
+determine_escrow_method (struct GNUNET_CONTAINER_MultiHashMap *url_param_map)
+{
+ struct GNUNET_HashCode method_key;
+ char *method_string;
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+
+ GNUNET_CRYPTO_hash ("method", strlen ("method"), &method_key);
+ method_string = GNUNET_CONTAINER_multihashmap_get (url_param_map,
+ &method_key);
+ // default method is plaintext
+ if (NULL == method_string)
+ method = GNUNET_ESCROW_KEY_PLAINTEXT;
+ else
+ method = GNUNET_ESCROW_method_string_to_number (method_string);
+
+ return method;
+}
+
+
+static char *
+get_user_secret_from_payload (struct RequestHandle *handle)
+{
+ json_t *json_data;
+ json_error_t err;
+ char *user_secret, *user_secret_cpy;
+ int json_unpack_state;
+ char term_data[handle->data_size + 1];
+
+ if (0 >= handle->data_size)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_NO_DATA);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return NULL;
+ }
+
+ term_data[handle->data_size] = '\0';
+ GNUNET_memcpy (term_data, handle->data, handle->data_size);
+ json_data = json_loads (term_data, JSON_DECODE_ANY, &err);
+ if (NULL == json_data)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_NO_DATA);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+
+ json_unpack_state = 0;
+ json_unpack_state =
+ json_unpack (json_data, "{s:s}",
+ GNUNET_REST_ESCROW_PARAM_USER_SECRET, &user_secret);
+ if (0 != json_unpack_state)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_DATA_INVALID);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+
+ if (NULL == user_secret)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_DATA_INVALID);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+ if (0 >= strlen (user_secret))
+ {
+ json_decref (json_data);
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_DATA_INVALID);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return NULL;
+ }
+
+ user_secret_cpy = GNUNET_strdup (user_secret);
+ json_decref (json_data);
+
+ return user_secret_cpy;
+}
+
+
+static void
+escrow_finished (void *cls,
+ struct GNUNET_ESCROW_Anchor *anchor,
+ const char *emsg)
+{
+ struct RequestHandle *handle = cls;
+ struct MHD_Response *resp;
+ json_t *json_anchor;
+ const char*anchor_data;
+ char *anchor_data_enc, *result_string;
+
+ if (NULL == anchor)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to escrow ego.\n");
+ handle->response_code = MHD_HTTP_NO_CONTENT;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ESCROW_FAILED);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ json_anchor = json_object ();
+ json_object_set_new (json_anchor,
+ GNUNET_REST_ESCROW_PARAM_METHOD,
+ json_string (GNUNET_ESCROW_method_number_to_string (
+ anchor->method)));
+ json_object_set_new (json_anchor,
+ GNUNET_REST_ESCROW_PARAM_NAME,
+ json_string (anchor->egoName));
+ anchor_data = (const char *)&anchor[1];
+ GNUNET_STRINGS_urlencode (anchor_data, anchor->size, &anchor_data_enc);
+ json_object_set_new (json_anchor,
+ GNUNET_REST_ESCROW_PARAM_ANCHOR_DATA,
+ json_string (anchor_data_enc));
+
+ result_string = json_dumps (json_anchor, 0);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_string);
+ resp = GNUNET_REST_create_response (result_string);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ MHD_add_response_header (resp, "Content-Type", "application/json");
+
+ json_decref (json_anchor);
+ GNUNET_free (result_string);
+ GNUNET_free (anchor_data_enc);
+
+ GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
+}
+
+
+/**
+ * Respond to PUT (start_escrow) request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+escrow_identity (struct GNUNET_REST_RequestHandle *con_handle,
+ const char *url,
+ void *cls)
+{
+ struct RequestHandle *handle = cls;
+ struct EgoEntry *ego_entry;
+ char *identity, *userSecret;
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Putting %s into escrow.\n",
+ handle->url);
+
+ if (strlen (GNUNET_REST_API_NS_ESCROW_PUT) >= strlen (handle->url))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_MISSING_NAME);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+ identity = handle->url + strlen (GNUNET_REST_API_NS_ESCROW_PUT) + 1;
+
+ for (ego_entry = ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ if (0 == strcmp (identity, ego_entry->identifier))
+ break;
+
+ if (NULL == ego_entry)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Identity %s not found.\n",
+ identity);
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ID_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ /* determine method */
+ method = determine_escrow_method (handle->rest_handle->url_param_map);
+ if (GNUNET_ESCROW_KEY_NONE == method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Method not found.\n");
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_METHOD_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ /* get user secret */
+ if (GNUNET_ESCROW_KEY_PLAINTEXT != method)
+ {
+ userSecret = get_user_secret_from_payload (handle);
+ if (NULL == userSecret)
+ // get_user_secret_from_payload () already cleaned up
+ return;
+ }
+ else
+ userSecret = NULL;
+
+ handle->op = GNUNET_ESCROW_put (escrow_handle,
+ ego_entry->ego,
+ userSecret,
+ method,
+ &escrow_finished,
+ handle);
+
+ if (NULL != userSecret)
+ GNUNET_free (userSecret);
+}
+
+
+static struct GNUNET_ESCROW_Anchor *
+build_anchor (const char *method_string,
+ const char *ego_name,
+ const char *anchor_data_enc)
+{
+ struct GNUNET_ESCROW_Anchor *anchor;
+ char *ptr;
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+ char *anchor_data;
+
+ method = GNUNET_ESCROW_method_string_to_number (method_string);
+ if (GNUNET_ESCROW_KEY_NONE == method)
+ return NULL;
+ GNUNET_STRINGS_urldecode (anchor_data_enc,
+ strlen (anchor_data_enc),
+ &anchor_data);
+
+ anchor = GNUNET_malloc (sizeof (struct GNUNET_ESCROW_Anchor)
+ + strlen (anchor_data)
+ + strlen (ego_name) + 1);
+ anchor->method = method;
+ anchor->size = strlen (anchor_data);
+ ptr = (char *)&anchor[1];
+ GNUNET_memcpy (ptr, anchor_data, strlen (anchor_data));
+ ptr += strlen (anchor_data);
+ anchor->egoName = ptr;
+ strcpy (ptr, ego_name);
+
+ GNUNET_free (anchor_data);
+
+ return anchor;
+}
+
+
+static struct GNUNET_ESCROW_Anchor *
+get_anchor_from_payload (struct RequestHandle *handle)
+{
+ json_t *json_data;
+ json_error_t err;
+ char *method, *ego_name, *anchor_data_enc;
+ int json_unpack_state;
+ char term_data[handle->data_size + 1];
+ struct GNUNET_ESCROW_Anchor *anchor;
+
+ if (0 >= handle->data_size)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_NO_DATA);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return NULL;
+ }
+
+ term_data[handle->data_size] = '\0';
+ GNUNET_memcpy (term_data, handle->data, handle->data_size);
+ json_data = json_loads (term_data, JSON_DECODE_ANY, &err);
+ if (NULL == json_data)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_NO_DATA);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+
+ json_unpack_state = 0;
+ json_unpack_state =
+ json_unpack (json_data, "{s:s, s:s, s:s}",
+ GNUNET_REST_ESCROW_PARAM_METHOD, &method,
+ GNUNET_REST_ESCROW_PARAM_NAME, &ego_name,
+ GNUNET_REST_ESCROW_PARAM_ANCHOR_DATA, &anchor_data_enc);
+ if (0 != json_unpack_state)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_DATA_INVALID);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+
+ if (NULL == method || NULL == ego_name || NULL == anchor_data_enc)
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_DATA_INVALID);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+ if (0 >= strlen (method) || 0 >= strlen (ego_name) || 0 >= strlen (anchor_data_enc))
+ {
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_DATA_INVALID);
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+
+ anchor = build_anchor (method, ego_name, anchor_data_enc);
+ if (NULL == anchor)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to parse anchor.\n");
+ handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ANCHOR_ERROR);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ json_decref (json_data);
+ return NULL;
+ }
+
+ json_decref (json_data);
+
+ return anchor;
+}
+
+
+static void
+restore_finished (void *cls,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *emsg)
+{
+ struct RequestHandle *handle = cls;
+ struct EgoEntry *ego_entry;
+ struct MHD_Response *resp;
+ struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub;
+ json_t *json_ego;
+ char *keystring, *result_string;
+
+ if (NULL == ego)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to restore ego.\n");
+ handle->response_code = MHD_HTTP_NO_CONTENT;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_RESTORE_FAILED);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ GNUNET_IDENTITY_ego_get_public_key (ego, &ego_pub);
+ keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_pub);
+
+ for (ego_entry = ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ if (0 == strcmp (keystring, ego_entry->keystring))
+ break;
+
+ if (NULL == ego_entry)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Identity not found despite successful restoration.\n");
+ handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ID_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ GNUNET_free (keystring);
+ return;
+ }
+
+ json_ego = json_object ();
+ json_object_set_new (json_ego,
+ GNUNET_REST_ESCROW_PARAM_NAME,
+ json_string (ego_entry->identifier));
+ json_object_set_new (json_ego,
+ GNUNET_REST_ESCROW_PARAM_PUBKEY,
+ json_string (ego_entry->keystring));
+
+ result_string = json_dumps (json_ego, 0);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_string);
+ resp = GNUNET_REST_create_response (result_string);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ MHD_add_response_header (resp, "Content-Type", "application/json");
+
+ json_decref (json_ego);
+ GNUNET_free (result_string);
+ GNUNET_free (keystring);
+
+ GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
+}
+
+
+/**
+ * Respond to GET (restore) request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+get_escrowed_identity (struct GNUNET_REST_RequestHandle *con_handle,
+ const char *url,
+ void *cls)
+{
+ struct RequestHandle *handle = cls;
+ struct GNUNET_ESCROW_Anchor *anchor;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Getting %s from escrow.\n",
+ handle->url);
+
+ /* get anchor */
+ anchor = get_anchor_from_payload (handle);
+ if (NULL == anchor)
+ // get_anchor_from_payload () already cleaned up
+ return;
+
+ handle->op = GNUNET_ESCROW_get (escrow_handle,
+ anchor,
+ &restore_finished,
+ handle);
+}
+
+
+static void
+verify_finished (void *cls,
+ int verificationResult,
+ const char *emsg)
+{
+ struct RequestHandle *handle = cls;
+ struct MHD_Response *resp;
+ json_t *json_verif;
+ const char *verif_string;
+ char *result_string;
+
+ switch (verificationResult)
+ {
+ case GNUNET_ESCROW_VALID:
+ verif_string = "valid";
+ break;
+ case GNUNET_ESCROW_INVALID:
+ verif_string = "invalid";
+ break;
+ case GNUNET_ESCROW_SHARES_MISSING:
+ verif_string = "shares_missing";
+ break;
+ default:
+ verif_string = "unknown";
+ }
+
+ json_verif = json_object ();
+ json_object_set_new (json_verif,
+ GNUNET_REST_ESCROW_PARAM_VERIFICATION_RESULT,
+ json_string (verif_string));
+
+ result_string = json_dumps (json_verif, 0);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_string);
+ resp = GNUNET_REST_create_response (result_string);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ MHD_add_response_header (resp, "Content-Type", "application/json");
+
+ json_decref (json_verif);
+ GNUNET_free (result_string);
+
+ GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
+}
+
+
+/**
+ * Respond to VERIFY request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+verify_escrow (struct GNUNET_REST_RequestHandle *con_handle,
+ const char *url,
+ void *cls)
+{
+ struct RequestHandle *handle = cls;
+ struct EgoEntry *ego_entry;
+ struct GNUNET_ESCROW_Anchor *anchor;
+ char *identity;
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Verifying escrow of %s.\n",
+ handle->url);
+
+ if (strlen (GNUNET_REST_API_NS_ESCROW_VERIFY) >= strlen (handle->url))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_MISSING_NAME);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+ identity = handle->url + strlen (GNUNET_REST_API_NS_ESCROW_VERIFY) + 1;
+
+ for (ego_entry = ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ if (0 == strcmp (identity, ego_entry->identifier))
+ break;
+
+ if (NULL == ego_entry)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Identity %s not found.\n",
+ identity);
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ID_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ /* determine method */
+ method = determine_escrow_method (handle->rest_handle->url_param_map);
+ if (GNUNET_ESCROW_KEY_NONE == method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Method not found.\n");
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_METHOD_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ /* get anchor */
+ anchor = get_anchor_from_payload (handle);
+ if (NULL == anchor)
+ // get_anchor_from_payload () already cleaned up
+ return;
+
+ handle->op = GNUNET_ESCROW_verify (escrow_handle,
+ ego_entry->ego,
+ anchor,
+ method,
+ &verify_finished,
+ handle);
+}
+
+
+/**
+ * Respond to STATUS request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+get_escrow_status (struct GNUNET_REST_RequestHandle *con_handle,
+ const char *url,
+ void *cls)
+{
+ struct RequestHandle *handle = cls;
+ struct EgoEntry *ego_entry;
+ struct GNUNET_ESCROW_Status *status;
+ struct MHD_Response *resp;
+ char *identity;
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+ json_t *json_status;
+ char *result_string;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Getting escrow status of %s.\n",
+ handle->url);
+
+ if (strlen (GNUNET_REST_API_NS_ESCROW_STATUS) >= strlen (handle->url))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_MISSING_NAME);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+ identity = handle->url + strlen (GNUNET_REST_API_NS_ESCROW_STATUS) + 1;
+
+ for (ego_entry = ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ if (0 == strcmp (identity, ego_entry->identifier))
+ break;
+
+ if (NULL == ego_entry)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Identity %s not found.\n",
+ identity);
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_ID_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ /* determine method */
+ method = determine_escrow_method (handle->rest_handle->url_param_map);
+ if (GNUNET_ESCROW_KEY_NONE == method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Method not found.\n");
+ handle->response_code = MHD_HTTP_NOT_FOUND;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_METHOD_NOT_FOUND);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ status = GNUNET_ESCROW_get_status (escrow_handle,
+ ego_entry->ego,
+ method);
+
+ if (NULL == status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got invalid status.\n");
+ handle->response_code = MHD_HTTP_NO_CONTENT;
+ handle->emsg = GNUNET_strdup (GNUNET_REST_ESCROW_INVALID_STATUS);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ /* create and send response */
+ json_status = json_object ();
+ if (GNUNET_ESCROW_KEY_NONE == status->last_method)
+ json_object_set_new (json_status,
+ GNUNET_REST_ESCROW_PARAM_LAST_METHOD,
+ json_string ("none"));
+ else
+ {
+ json_object_set_new (json_status,
+ GNUNET_REST_ESCROW_PARAM_LAST_METHOD,
+ json_string (
+ GNUNET_ESCROW_method_number_to_string (status->last_method)));
+ json_object_set_new (json_status,
+ GNUNET_REST_ESCROW_PARAM_LAST_VERIF,
+ json_integer (
+ status->last_successful_verification_time.abs_value_us / 1000));
+ json_object_set_new (json_status,
+ GNUNET_REST_ESCROW_PARAM_NEXT_VERIF,
+ json_integer (
+ status->next_recommended_verification_time.abs_value_us / 1000));
+ }
+
+ result_string = json_dumps (json_status, 0);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_string);
+ resp = GNUNET_REST_create_response (result_string);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ MHD_add_response_header (resp, "Content-Type", "application/json");
+
+ json_decref (json_status);
+ GNUNET_free (result_string);
+
+ GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
+}
+
+
+/**
+ * Respond to OPTIONS request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+options_cont (struct GNUNET_REST_RequestHandle *con_handle,
+ const char *url,
+ void *cls)
+{
+ struct MHD_Response *resp;
+ struct RequestHandle *handle = cls;
+
+ // For now, independent of path return all options
+ resp = GNUNET_REST_create_response (NULL);
+ MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
+ return;
+}
+
+
+/**
+ * If listing is enabled, prints information about the egos.
+ *
+ * This function is initially called for all egos and then again
+ * whenever a ego's identifier changes or if it is deleted. At the
+ * end of the initial pass over all egos, the function is once called
+ * with 'NULL' for 'ego'. That does NOT mean that the callback won't
+ * be invoked in the future or that there was an error.
+ *
+ * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
+ * this function is only called ONCE, and 'NULL' being passed in
+ * 'ego' does indicate an error (i.e. name is taken or no default
+ * value is known). If 'ego' is non-NULL and if '*ctx'
+ * is set in those callbacks, the value WILL be passed to a subsequent
+ * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
+ * that one was not NULL).
+ *
+ * When an identity is renamed, this function is called with the
+ * (known) ego but the NEW identifier.
+ *
+ * When an identity is deleted, this function is called with the
+ * (known) ego and "NULL" for the 'identifier'. In this case,
+ * the 'ego' is henceforth invalid (and the 'ctx' should also be
+ * cleaned up).
+ *
+ * @param cls closure
+ * @param ego ego handle
+ * @param ctx context for application to store data for this ego
+ * (during the lifetime of this process, initially NULL)
+ * @param identifier identifier assigned by the user for this ego,
+ * NULL if the user just deleted the ego and it
+ * must thus no longer be used
+ */
+static void
+list_ego (void *cls,
+ struct GNUNET_IDENTITY_Ego *ego,
+ void **ctx,
+ const char *identifier)
+{
+ struct EgoEntry *ego_entry;
+ struct GNUNET_CRYPTO_EcdsaPublicKey pk;
+
+ if ((NULL == ego) && (ID_REST_STATE_INIT == state))
+ {
+ state = ID_REST_STATE_POST_INIT;
+ return;
+ }
+ if (ID_REST_STATE_INIT == state)
+ {
+ ego_entry = GNUNET_new (struct EgoEntry);
+ GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
+ ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
+ ego_entry->ego = ego;
+ ego_entry->identifier = GNUNET_strdup (identifier);
+ GNUNET_CONTAINER_DLL_insert_tail (ego_head,
+ ego_tail,
+ ego_entry);
+ }
+ /* Ego renamed or added */
+ if (identifier != NULL)
+ {
+ for (ego_entry = ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ {
+ if (ego_entry->ego == ego)
+ {
+ /* Rename */
+ GNUNET_free (ego_entry->identifier);
+ ego_entry->identifier = GNUNET_strdup (identifier);
+ break;
+ }
+ }
+ if (NULL == ego_entry)
+ {
+ /* Add */
+ ego_entry = GNUNET_new (struct EgoEntry);
+ GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
+ ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
+ ego_entry->ego = ego;
+ ego_entry->identifier = GNUNET_strdup (identifier);
+ GNUNET_CONTAINER_DLL_insert_tail (ego_head,
+ ego_tail,
+ ego_entry);
+ }
+ }
+ else
+ {
+ /* Delete */
+ for (ego_entry = ego_head; NULL != ego_entry;
+ ego_entry = ego_entry->next)
+ {
+ if (ego_entry->ego == ego)
+ break;
+ }
+ if (NULL == ego_entry)
+ return; /* Not found */
+
+ GNUNET_CONTAINER_DLL_remove (ego_head,
+ ego_tail,
+ ego_entry);
+ GNUNET_free (ego_entry->identifier);
+ GNUNET_free (ego_entry->keystring);
+ GNUNET_free (ego_entry);
+ return;
+ }
+
+}
+
+
+/**
+ * Function processing the REST call
+ *
+ * @param method HTTP method
+ * @param url URL of the HTTP request
+ * @param data body of the HTTP request (optional)
+ * @param data_size length of the body
+ * @param proc callback function for the result
+ * @param proc_cls closure for callback function
+ * @return GNUNET_OK if request accepted
+ */
+static enum GNUNET_GenericReturnValue
+rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
+ GNUNET_REST_ResultProcessor proc,
+ void *proc_cls)
+{
+ struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
+ struct GNUNET_REST_RequestHandlerError err;
+ static const struct GNUNET_REST_RequestHandler handlers[] =
+ { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_ESCROW_STATUS, &get_escrow_status },
+ { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_ESCROW_VERIFY, &verify_escrow },
+ { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_ESCROW_GET, &get_escrowed_identity },
+ { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_ESCROW_PUT, &escrow_identity },
+ { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_ESCROW, &options_cont },
+ GNUNET_REST_HANDLER_END };
+
+
+ handle->response_code = 0;
+ handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+ handle->proc_cls = proc_cls;
+ handle->proc = proc;
+ handle->rest_handle = rest_handle;
+ handle->data = rest_handle->data;
+ handle->data_size = rest_handle->data_size;
+
+ handle->url = GNUNET_strdup (rest_handle->url);
+ if (handle->url[strlen (handle->url) - 1] == '/')
+ handle->url[strlen (handle->url) - 1] = '\0';
+ handle->timeout_task =
+ GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
+ GNUNET_CONTAINER_DLL_insert (requests_head,
+ requests_tail,
+ handle);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
+ if (GNUNET_NO ==
+ GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
+ {
+ cleanup_handle (handle);
+ return GNUNET_NO;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
+ return GNUNET_YES;
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls Config info
+ * @return NULL on error, otherwise the plugin context
+ */
+void *
+libgnunet_plugin_rest_escrow_init (void *cls)
+{
+ static struct Plugin plugin;
+ struct GNUNET_REST_Plugin *api;
+
+ cfg = cls;
+ if (NULL != plugin.cfg)
+ return NULL; /* can only initialize once! */
+ memset (&plugin, 0, sizeof(struct Plugin));
+ plugin.cfg = cfg;
+ api = GNUNET_new (struct GNUNET_REST_Plugin);
+ api->cls = &plugin;
+ api->name = GNUNET_REST_API_NS_ESCROW;
+ api->process_request = &rest_process_request;
+ GNUNET_asprintf (&allow_methods,
+ "%s, %s, %s",
+ MHD_HTTP_METHOD_GET,
+ MHD_HTTP_METHOD_POST,
+ MHD_HTTP_METHOD_OPTIONS);
+ state = ID_REST_STATE_INIT;
+ identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
+ escrow_handle = GNUNET_ESCROW_init (cfg);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Escrow REST API initialized\n"));
+ return api;
+}
+
+
+/**
+ * Exit point from the plugin.
+ *
+ * @param cls the plugin context (as returned by "init")
+ * @return always NULL
+ */
+void *
+libgnunet_plugin_rest_escrow_done (void *cls)
+{
+ struct GNUNET_REST_Plugin *api = cls;
+ struct Plugin *plugin = api->cls;
+ struct EgoEntry *ego_entry;
+ struct EgoEntry *ego_tmp;
+
+ plugin->cfg = NULL;
+ while (NULL != requests_head)
+ cleanup_handle (requests_head);
+ if (NULL != escrow_handle)
+ GNUNET_ESCROW_fini (escrow_handle);
+ if (NULL != identity_handle)
+ GNUNET_IDENTITY_disconnect (identity_handle);
+ for (ego_entry = ego_head; NULL != ego_entry;)
+ {
+ ego_tmp = ego_entry;
+ ego_entry = ego_entry->next;
+ GNUNET_free (ego_tmp->identifier);
+ GNUNET_free (ego_tmp->keystring);
+ GNUNET_free (ego_tmp);
+ }
+
+ GNUNET_free (allow_methods);
+ GNUNET_free (api);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Escrow REST plugin is finished\n");
+ return NULL;
+}
+
+
+/* end of plugin_rest_escrow.c */
diff --git a/src/escrow/test_gns_escrow.sh b/src/escrow/test_gns_escrow.sh
new file mode 100755
index 000000000..ea0d591dd
--- /dev/null
+++ b/src/escrow/test_gns_escrow.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+LOCATION=$(which gnunet-config)
+if [ -z $LOCATION ]
+then
+ LOCATION="gnunet-config"
+fi
+$LOCATION --version 1> /dev/null
+if test $? != 0
+then
+ echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
+ exit 77
+fi
+
+rm -rf `gnunet-config -c test_escrow.conf -s PATHS -o GNUNET_HOME -f`
+
+which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30"
+
+gnunet-arm -s -c test_escrow.conf
+gnunet-identity -C testego -c test_escrow.conf
+ANCHOR=$(gnunet-escrow -m gns -P testego -u secret -c test_escrow.conf)
+if test $? != 0
+then
+ echo "GNS escrow failed!"
+ gnunet-arm -e -c test_escrow.conf
+ exit 1
+else
+ ANCHOR=$(echo $ANCHOR | awk 'NF>1{print $NF}')
+ echo "using anchor $ANCHOR"
+fi
+gnunet-escrow -m gns -V testego -a $ANCHOR -c test_escrow.conf
+if test $? != 0
+then
+ echo "GNS verification failed!"
+ gnunet-arm -e -c test_escrow.conf
+ exit 1
+fi
+gnunet-identity -D testego -c test_escrow.conf
+gnunet-escrow -G -a $ANCHOR -c test_escrow.conf
+if test $? != 0
+then
+ echo "GNS restore failed!"
+ gnunet-arm -e -c test_escrow.conf
+ exit 1
+fi
+gnunet-arm -e -c test_escrow.conf
+
+exit 0
diff --git a/src/escrow/test_plaintext_escrow.sh b/src/escrow/test_plaintext_escrow.sh
new file mode 100755
index 000000000..00072b1da
--- /dev/null
+++ b/src/escrow/test_plaintext_escrow.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+LOCATION=$(which gnunet-config)
+if [ -z $LOCATION ]
+then
+ LOCATION="gnunet-config"
+fi
+$LOCATION --version 1> /dev/null
+if test $? != 0
+then
+ echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
+ exit 77
+fi
+
+rm -rf `gnunet-config -c test_escrow.conf -s PATHS -o GNUNET_HOME -f`
+
+which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30"
+
+gnunet-arm -s -c test_escrow.conf
+gnunet-identity -C testego -c test_escrow.conf
+ANCHOR=$(gnunet-escrow -m plaintext -P testego -c test_escrow.conf)
+if test $? != 0
+then
+ echo "Plaintext escrow failed!"
+ gnunet-arm -e -c test_escrow.conf
+ exit 1
+else
+ ANCHOR=$(echo $ANCHOR | awk 'NF>1{print $NF}')
+fi
+gnunet-escrow -m plaintext -V testego -a $ANCHOR -c test_escrow.conf
+if test $? != 0
+then
+ echo "Plaintext verification failed!"
+ gnunet-arm -e -c test_escrow.conf
+ exit 1
+fi
+gnunet-identity -D testego -c test_escrow.conf
+gnunet-escrow -G -a $ANCHOR -c test_escrow.conf
+if test $? != 0
+then
+ echo "Plaintext restore failed!"
+ gnunet-arm -e -c test_escrow.conf
+ exit 1
+fi
+gnunet-arm -e -c test_escrow.conf
+
+exit 0
diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h
index 03fb16a43..085d7187f 100644
--- a/src/include/gnunet_crypto_lib.h
+++ b/src/include/gnunet_crypto_lib.h
@@ -1198,6 +1198,21 @@ GNUNET_CRYPTO_eddsa_public_key_to_string (
/**
+ * Convert a string representing a private key to a private key.
+ *
+ * @param enc encoded private key
+ * @param enclen number of bytes in @a enc (without 0-terminator)
+ * @param priv where to store the private key
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_CRYPTO_ecdsa_private_key_from_string (
+ const char *enc,
+ size_t enclen,
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *pub);
+
+
+/**
* Convert a string representing a public key to a public key.
*
* @param enc encoded public key
@@ -1215,7 +1230,7 @@ GNUNET_CRYPTO_ecdsa_public_key_from_string (
/**
* Convert a string representing a private key to a private key.
*
- * @param enc encoded public key
+ * @param enc encoded private key
* @param enclen number of bytes in @a enc (without 0-terminator)
* @param priv where to store the private key
* @return #GNUNET_OK on success
diff --git a/src/include/gnunet_escrow_lib.h b/src/include/gnunet_escrow_lib.h
new file mode 100644
index 000000000..546fdfeb9
--- /dev/null
+++ b/src/include/gnunet_escrow_lib.h
@@ -0,0 +1,437 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Johannes Späth
+ *
+ * @file
+ * Escrow definitions
+ *
+ * @defgroup escrow escrow component
+ * @{
+ */
+#ifndef GNUNET_ESCROW_LIB_H
+#define GNUNET_ESCROW_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "gnunet_util_lib.h"
+#include "gnunet_identity_service.h"
+
+
+/**
+ * Enum for the different key escrow methods
+ */
+enum GNUNET_ESCROW_Key_Escrow_Method
+{
+ GNUNET_ESCROW_KEY_NONE, // error value
+ GNUNET_ESCROW_KEY_PLAINTEXT,
+ GNUNET_ESCROW_KEY_GNS,
+ GNUNET_ESCROW_KEY_ANASTASIS
+};
+
+
+/**
+ * Enum for the different verification results
+ */
+enum GNUNET_ESCROW_Verification_Result
+{
+ GNUNET_ESCROW_VALID,
+ GNUNET_ESCROW_INVALID,
+ GNUNET_ESCROW_SHARES_MISSING
+};
+
+
+/**
+ * Struct for the escrow anchor
+ */
+struct GNUNET_ESCROW_Anchor
+{
+ /**
+ * The escrow method.
+ */
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+
+ /**
+ * The name of the ego that was put in escrow.
+ */
+ const char *egoName;
+
+ /**
+ * The size of the anchor data.
+ */
+ uint32_t size;
+};
+
+
+/**
+ * Struct for the escrow status
+ */
+struct GNUNET_ESCROW_Status
+{
+ /**
+ * The time of the last successful verification.
+ */
+ struct GNUNET_TIME_Absolute last_successful_verification_time;
+
+ /**
+ * The time of the next recommended verification.
+ */
+ struct GNUNET_TIME_Absolute next_recommended_verification_time;
+
+ /**
+ * The escrow method used for the last escrow of the identity,
+ * GNUNET_ESCROW_KEY_NONE if no escrow has been performed.
+ */
+ enum GNUNET_ESCROW_Key_Escrow_Method last_method;
+};
+
+
+/**
+ * Function called after the initialization of the identity service.
+ * Passed via cls to the callback of GNUNET_IDENTITY_connect
+ */
+typedef void (*GNUNET_ESCROW_IdentityInitContinuation) ();
+
+/**
+ * Function called after the creation of an ego in case that happened
+ * because of an escrow GET operation.
+ */
+typedef void (*GNUNET_ESCROW_EgoCreateContinuation) (
+ struct GNUNET_IDENTITY_Ego *ego);
+
+/**
+ * Continuation for PUT operations.
+ *
+ * @param cls closure
+ * @param anchor the escrow anchor needed to get the data back
+ * @param emsg error message, NULL on success
+ */
+typedef void (*GNUNET_ESCROW_AnchorContinuation) (
+ void *cls,
+ struct GNUNET_ESCROW_Anchor *anchor,
+ const char *emsg);
+
+/**
+ * Continuation for a GET operation.
+ *
+ * @param cls closure
+ * @param ego a new identity ego restored from the escrow
+ * @param emsg error message, NULL on success
+ */
+typedef void (*GNUNET_ESCROW_EgoContinuation) (
+ void *cls,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *emsg);
+
+/**
+ * Continuation for a VERIFY operation.
+ *
+ * @param cls closure
+ * @param verificationResult the result of the verification, i.e.
+ * GNUNET_ESCROW_VALID if the escrow could successfully by restored,
+ * GNUNET_ESCROW_SHARES_MISSING if it could be restored, but some of
+ * the shares are missing
+ * GNUNET_ESCROW_INVALID otherwise
+ * @param emsg error message, NULL on success
+ */
+typedef void (*GNUNET_ESCROW_VerifyContinuation) (
+ void *cls,
+ int verificationResult,
+ const char *emsg);
+
+
+/**
+ * Handle for the escrow component.
+ */
+struct GNUNET_ESCROW_Handle
+{
+ /**
+ * Configuration to use.
+ */
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Head of active operations.
+ */
+ struct GNUNET_ESCROW_Operation *op_head;
+
+ /**
+ * Tail of active operations.
+ */
+ struct GNUNET_ESCROW_Operation *op_tail;
+
+ /**
+ * The last operation id used for an ESCROW operation.
+ */
+ uint32_t last_op_id_used;
+};
+
+
+/**
+ * Handle for an operation with the escrow component.
+ */
+struct GNUNET_ESCROW_Operation
+{
+ /**
+ * Main escrow handle.
+ */
+ struct GNUNET_ESCROW_Handle *h;
+
+ /**
+ * ID of the operation.
+ */
+ uint32_t id;
+
+ /**
+ * We keep operations in a DLL.
+ */
+ struct GNUNET_ESCROW_Operation *next;
+
+ /**
+ * We keep operations in a DLL.
+ */
+ struct GNUNET_ESCROW_Operation *prev;
+
+ /**
+ * The used escrow method.
+ */
+ enum GNUNET_ESCROW_Key_Escrow_Method method;
+
+ /**
+ * The respective plugin operation
+ */
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap;
+
+ /**
+ * The ego.
+ */
+ const struct GNUNET_IDENTITY_Ego *ego;
+
+ /**
+ * The verification result.
+ */
+ enum GNUNET_ESCROW_Verification_Result verification_result;
+
+ /**
+ * Continuation for a PUT operation.
+ */
+ GNUNET_ESCROW_AnchorContinuation cb_put;
+
+ /**
+ * Continuation for a GET operation.
+ */
+ GNUNET_ESCROW_EgoContinuation cb_get;
+
+ /**
+ * Continuation for a VERIFY operation.
+ */
+ GNUNET_ESCROW_VerifyContinuation cb_verify;
+
+ /**
+ * Closure for the callback
+ */
+ void *cb_cls;
+};
+
+
+/**
+ * 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);
+
+
+/**
+ * Unload all loaded plugins on destruction.
+ *
+ * @param h the escrow handle
+ */
+void
+GNUNET_ESCROW_fini (
+ struct GNUNET_ESCROW_Handle *h);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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:
+ * <method>:<egoName>:<anchorData>
+ * with <method>, <egoName> and <anchorData> 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+#if 0 /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+
+/* ifndef GNUNET_ESCROW_LIB_H */
+#endif
+
+/** @} */ /* end of group escrow */
+
+/* end of gnunet_escrow_lib.h */
diff --git a/src/include/gnunet_escrow_plugin.h b/src/include/gnunet_escrow_plugin.h
new file mode 100644
index 000000000..ec0bbdcdb
--- /dev/null
+++ b/src/include/gnunet_escrow_plugin.h
@@ -0,0 +1,171 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Johannes Späth
+ *
+ * @file
+ * Plugin API for escrow methods
+ *
+ * @defgroup escrow-plugin escrow plugin API for escrow methods
+ * @{
+ */
+#ifndef GNUNET_ESCROW_PLUGIN_H
+#define GNUNET_ESCROW_PLUGIN_H
+
+#include "gnunet_util_lib.h"
+#include "gnunet_escrow_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_scheduler_lib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+/**
+ * Function called to start the escrow of 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)
+ * for GNS escrow, this has to be UNIQUE in the whole network!
+ * @param cb the function called upon completion
+ * @param op_id unique ID of the respective ESCROW_Operation
+ *
+ * @return a wrapper for the plugin operation
+ */
+typedef struct ESCROW_PluginOperationWrapper *(*GNUNET_ESCROW_StartKeyEscrowFunction) (
+ struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const char *userSecret,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id);
+
+/**
+ * Function called to verify the 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 a wrapper for the plugin operation
+ */
+typedef struct ESCROW_PluginOperationWrapper *(*GNUNET_ESCROW_VerifyKeyEscrowFunction) (
+ struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id);
+
+/**
+ * Function called to restore a key from an 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 a wrapper for the plugin operation
+ */
+typedef struct ESCROW_PluginOperationWrapper *(*GNUNET_ESCROW_RestoreKeyFunction) (
+ struct GNUNET_ESCROW_Handle *h,
+ const struct GNUNET_ESCROW_Anchor *anchor,
+ GNUNET_SCHEDULER_TaskCallback cb,
+ uint32_t op_id);
+
+
+/**
+ * Function called to get the status of an escrow, i.e.
+ * -> when the last successful escrow was
+ * -> when the next recommended escrow is
+ *
+ * @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
+ */
+typedef struct GNUNET_ESCROW_Status *(*GNUNET_ESCROW_GetEscrowStatusFunction) (
+ struct GNUNET_ESCROW_Handle *h,
+ struct GNUNET_IDENTITY_Ego *ego);
+
+
+/**
+ * Function called to cancel a plugin operation
+ *
+ * @param plugin_op_wrap plugin operation wrapper containing the plugin operation
+ */
+typedef void (*GNUNET_ESCROW_CancelPluginOperationFunction) (
+ struct ESCROW_PluginOperationWrapper *plugin_op_wrap);
+
+
+/**
+ * Each plugin is required to return a pointer to a struct of this
+ * type as the return value from its entry point.
+ */
+struct GNUNET_ESCROW_KeyPluginFunctions
+{
+ /**
+ * Closure for all of the callbacks.
+ */
+ void *cls;
+
+ /**
+ * Start key escrow
+ */
+ GNUNET_ESCROW_StartKeyEscrowFunction start_key_escrow;
+
+ /**
+ * Verify key escrow
+ */
+ GNUNET_ESCROW_VerifyKeyEscrowFunction verify_key_escrow;
+
+ /**
+ * Restore key escrow
+ */
+ GNUNET_ESCROW_RestoreKeyFunction restore_key;
+
+ /**
+ * Get the status of an escrow
+ */
+ GNUNET_ESCROW_GetEscrowStatusFunction get_status;
+
+ /**
+ * Cancel plugin operation
+ */
+ GNUNET_ESCROW_CancelPluginOperationFunction cancel_plugin_operation;
+};
+
+
+#if 0 /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/** @} */ /* end of group */
diff --git a/src/include/gnunet_gnsrecord_lib.h b/src/include/gnunet_gnsrecord_lib.h
index 960203fb1..db524ec5b 100644
--- a/src/include/gnunet_gnsrecord_lib.h
+++ b/src/include/gnunet_gnsrecord_lib.h
@@ -151,6 +151,11 @@ extern "C" {
*/
#define GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION 65555
+/**
+ * Record type for an escrow key share (escrow component)
+ */
+#define GNUNET_GNSRECORD_TYPE_ESCROW_KEYSHARE 65556
+
/**
* Flags that can be set for a record.
diff --git a/src/include/gnunet_reclaim_plugin.h b/src/include/gnunet_reclaim_plugin.h
index 2ba8fc8a0..ed6663bb5 100644
--- a/src/include/gnunet_reclaim_plugin.h
+++ b/src/include/gnunet_reclaim_plugin.h
@@ -32,6 +32,7 @@
#include "gnunet_util_lib.h"
#include "gnunet_reclaim_lib.h"
+#include "gnunet_identity_service.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c
index 019dbe94e..ef52d9588 100644
--- a/src/util/crypto_ecc.c
+++ b/src/util/crypto_ecc.c
@@ -388,6 +388,39 @@ GNUNET_CRYPTO_ecdsa_public_key_from_string (
/**
+ * Convert a string representing a private key to a private key.
+ *
+ * @param enc encoded private key
+ * @param enclen number of bytes in @a enc (without 0-terminator)
+ * @param priv where to store the private key
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_CRYPTO_ecdsa_private_key_from_string (
+ const char *enc,
+ size_t enclen,
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *priv)
+{
+ size_t keylen = (sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey)) * 8;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ if (enclen != keylen)
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (enc,
+ enclen,
+ priv,
+ sizeof(
+ struct GNUNET_CRYPTO_EcdsaPrivateKey)))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
* Convert a string representing a public key to a public key.
*
* @param enc encoded public key
@@ -423,7 +456,7 @@ GNUNET_CRYPTO_eddsa_public_key_from_string (
/**
* Convert a string representing a private key to a private key.
*
- * @param enc encoded public key
+ * @param enc encoded private key
* @param enclen number of bytes in @a enc (without 0-terminator)
* @param priv where to store the private key
* @return #GNUNET_OK on success
diff --git a/src/util/strings.c b/src/util/strings.c
index 9d6f4039e..014302c01 100644
--- a/src/util/strings.c
+++ b/src/util/strings.c
@@ -2148,7 +2148,7 @@ GNUNET_STRINGS_urlencode (const char *data, size_t len, char **out)
struct GNUNET_Buffer buf = { 0 };
const uint8_t *i8 = (uint8_t *) data;
- while (0 != *i8)
+ while ((i8 - (uint8_t *) data) < len && 0 != *i8)
{
if (0 == (0x80 & *i8))
{