From df8660ffec05d108d59de191ddc5114c4678a362 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Fri, 14 Jan 2022 18:24:13 +0100 Subject: -move to reclaim --- src/did/.gitignore | 2 - src/did/Makefile.am | 37 -- src/did/default_did_document.json | 15 - src/did/gnunet-did.c | 922 ------------------------------------ src/did/test_w3c_ed25519_2020.c | 69 --- src/reclaim/Makefile.am | 22 +- src/reclaim/gnunet-did.c | 922 ++++++++++++++++++++++++++++++++++++ src/reclaim/test_w3c_ed25519_2020.c | 69 +++ 8 files changed, 1011 insertions(+), 1047 deletions(-) delete mode 100644 src/did/.gitignore delete mode 100644 src/did/Makefile.am delete mode 100644 src/did/default_did_document.json delete mode 100644 src/did/gnunet-did.c delete mode 100644 src/did/test_w3c_ed25519_2020.c create mode 100644 src/reclaim/gnunet-did.c create mode 100644 src/reclaim/test_w3c_ed25519_2020.c diff --git a/src/did/.gitignore b/src/did/.gitignore deleted file mode 100644 index d47f720ff..000000000 --- a/src/did/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -test_w3c_ed25519_2020 -gnunet-did diff --git a/src/did/Makefile.am b/src/did/Makefile.am deleted file mode 100644 index 3f5e3f34b..000000000 --- a/src/did/Makefile.am +++ /dev/null @@ -1,37 +0,0 @@ -# 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 - -bin_PROGRAMS = \ - gnunet-did - -gnunet_did_SOURCES = \ - gnunet-did.c -gnunet_did_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/gns/libgnunetgns.la \ - $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/identity/libgnunetidentity.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ - -ljansson - - -check_PROGRAMS = \ - test_w3c_ed25519_2020 - -test_w3c_ed25519_2020_SOURCES = \ - test_w3c_ed25519_2020.c -test_w3c_ed25519_2020_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la - -if ENABLE_TEST_RUN -TESTS = $(check_PROGRAMS) -endif - - diff --git a/src/did/default_did_document.json b/src/did/default_did_document.json deleted file mode 100644 index 61675a886..000000000 --- a/src/did/default_did_document.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/did/v1", - "https://w3id.org/security/suites/ed25519-2020/v1" - ], - "id": "did:example:123", - "authentication": [ - { - "id": "did:example:123#z6MkecaLyHuYWkayBDLw5ihndj3T1m6zKTGqau3A51G7RBf3", - "type": "Ed25519VerificationKey2020", - "controller": "did:example:123", - "publicKeyMultibase": "zAKJP3f7BD6W4iWEQ9jwndVTCBq8ua2Utt8EEjJ6Vxsf" - } - ] -} \ No newline at end of file diff --git a/src/did/gnunet-did.c b/src/did/gnunet-did.c deleted file mode 100644 index 2ebef7601..000000000 --- a/src/did/gnunet-did.c +++ /dev/null @@ -1,922 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2021 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * FIXME: Do we only want to handle EdDSA identities? - * TODO: Own GNS record type - * TODO: Fix overwrite of records in @ if present look for other with same sub - * TODO. Tests - * TODO: Move constants to did.h - * FIXME: Remove and lookup require differnt representations (did vs egoname) - */ - -/** - * @author Tristan Schwieren - * @file src/did/gnunet-did.c - * @brief DID Method Wrapper - * - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gns_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "jansson.h" - -#define GNUNET_DID_METHOD_RECLAIM_PREFIX "did:reclaim:" -#define GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME "1d" - -/** - * return value - */ -static int ret; - -/** - * Replace DID Document Flag - */ -static int replace; - -/** - * Remove DID Document Flag - */ -static int remove_did; - -/** - * Get DID Documement for DID Flag - */ -static int get; - -/** - * Create DID Document Flag - */ -static int create; - -/** - * Show DID for Ego Flag - */ -static int show; - -/** - * Show DID for Ego Flag - */ -static int show_all; - -/** - * DID Attribut String - */ -static char *did; - -/** - * DID Document Attribut String - */ -static char *didd; - -/** - * Ego Attribut String - */ -static char *egoname; - -/** - * DID Document expiration Date Attribut String - */ -static char *expire; - -/* - * Handle to the GNS service - */ -static struct GNUNET_GNS_Handle *gns_handle; - -/* - * Handle to the NAMESTORE service - */ -static struct GNUNET_NAMESTORE_Handle *namestore_handle; - -/* - * Handle to the IDENTITY service - */ -static struct GNUNET_IDENTITY_Handle *identity_handle; - - -/* - * The configuration - */ -const static struct GNUNET_CONFIGURATION_Handle *my_cfg; - -/** - * Give ego exists - */ -static int ego_exists = 0; - -/** - * @brief Disconnect and shutdown - * @param cls closure - */ -static void -cleanup (void *cls) -{ - if (NULL != gns_handle) - GNUNET_GNS_disconnect (gns_handle); - if (NULL != namestore_handle) - GNUNET_NAMESTORE_disconnect (namestore_handle); - if (NULL != identity_handle) - GNUNET_IDENTITY_disconnect (identity_handle); - - GNUNET_free (did); - GNUNET_free (didd); - GNUNET_free (egoname); - GNUNET_free (expire); - - GNUNET_SCHEDULER_shutdown (); -} - -char* -ego_to_did (struct GNUNET_IDENTITY_Ego *ego) -{ - struct GNUNET_IDENTITY_PublicKey pkey; // Get Public key - char *pkey_str; - char *did_str; - size_t pkey_len; - - GNUNET_IDENTITY_ego_get_public_key (ego, &pkey); - - pkey_str = GNUNET_IDENTITY_public_key_to_string (&pkey); - GNUNET_asprintf (&did_str, "%s%s", - GNUNET_DID_METHOD_RECLAIM_PREFIX, - pkey_str); - - free (pkey_str); - return did_str; -} - -/** - * @brief Callback for ego loockup of get_did_for_ego() - * - * @param cls closure - * @param ego the returned ego - */ -static void -get_did_for_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) -{ - char *did_str; - - if (ego == NULL) - { - printf ("EGO not found\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } - did_str = ego_to_did (ego); - - printf ("%s\n", did_str); - - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 0; - return; -} - -/** - * @brief Get the DID for a given EGO - * - */ -static void -get_did_for_ego () -{ - if (egoname != NULL) - { - GNUNET_IDENTITY_ego_lookup (my_cfg, - egoname, - &get_did_for_ego_lookup_cb, - NULL); - } - else { - printf ("Set the EGO argument to get the DID for a given EGO\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } -} - - -/** - * @brief Get the public key from did attribute given by the user - * - * @param pkey place to write the public key to - */ -static void -get_pkey_from_attr_did (struct GNUNET_IDENTITY_PublicKey *pkey) -{ - /* FIXME-MSC: I suggest introducing a - * #define MAX_DID_LENGTH - * here and use it for parsing - */ - char pkey_str[59]; - - if ((1 != (sscanf (did, GNUNET_DID_METHOD_RECLAIM_PREFIX"%58s", pkey_str))) || - (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (pkey_str, pkey))) - { - fprintf (stderr, _("Invalid DID `%s'\n"), pkey_str); - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 1; - return; - } -} - -/** - * @brief GNS lookup callback. Prints the DID Document to standard out. - * Fails if there is more than one DID record. - * - * @param cls closure - * @param rd_count number of records in @a rd - * @param rd the records in the reply - */ -static void -print_did_document ( - void *cls, - uint32_t rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - /* - * FIXME-MSC: The user may decide to put other records here. - * In general I am fine with the constraint here, but not when - * we move it to "@" - */ - if (rd_count != 1) - { - printf ("An ego should only have one DID Document\n"); - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 1; - return; - } - - if (rd[0].record_type == GNUNET_DNSPARSER_TYPE_TXT) - { - printf ("%s\n", (char *) rd[0].data); - } - else { - printf ("DID Document is not a TXT record\n"); - } - - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 0; - return; -} - -/** - * @brief Resolve a DID given by the user. - */ -static void -resolve_did_document () -{ - struct GNUNET_IDENTITY_PublicKey pkey; - - if (did == NULL) - { - printf ("Set DID option to resolve DID\n"); - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 1; - return; - } - - get_pkey_from_attr_did (&pkey); - - GNUNET_GNS_lookup (gns_handle, GNUNET_GNS_EMPTY_LABEL_AT, &pkey, GNUNET_DNSPARSER_TYPE_TXT, - GNUNET_GNS_LO_DEFAULT, &print_did_document, NULL); -} - - -/** - * @brief Signature of a callback function that is called after a did has been removed - */ -typedef void -(*remove_did_document_callback) (void *cls); - -/** - * @brief A Structure containing a cont and cls. Can be passed as a cls to a callback function - * - */ -struct Event -{ - remove_did_document_callback cont; - void *cls; -}; - -/** - * @brief Implements the GNUNET_NAMESTORE_ContinuationWithStatus - * Calls the callback function and cls in the event struct - * - * @param cls closure containing the event struct - * @param success - * @param emgs - */ -static void -remove_did_document_namestore_cb (void *cls, int32_t success, const char *emgs) -{ - struct Event *event; - - if (success != GNUNET_SYSERR) - { - event = (struct Event *) cls; - - if (event->cont != NULL) - { - event->cont (event->cls); - free (event); - } - else { - free (event); - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 0; - return; - } - } else { - printf ("Something went wrong when deleting the DID Document\n"); - - if (emgs != NULL) - { - printf ("%s\n", emgs); - } - - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 0; - return; - } -} - -/** - * @brief Callback called after the ego has been locked up - * - * @param cls closure - * @param ego the ego returned by the identity service - */ -static void -remove_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) -{ - const struct GNUNET_IDENTITY_PrivateKey *skey = - GNUNET_IDENTITY_ego_get_private_key (ego); - - GNUNET_NAMESTORE_records_store (namestore_handle, - skey, - GNUNET_GNS_EMPTY_LABEL_AT, - 0, - NULL, - &remove_did_document_namestore_cb, - cls); -} - -/** - * @brief Remove a DID Document - */ -static void -remove_did_document (remove_did_document_callback cont, void *cls) -{ - struct Event *event; - - if (egoname == NULL) - { - printf ("Remove requieres an ego option\n"); - GNUNET_SCHEDULER_add_now (cleanup, NULL); - ret = 1; - return; - } - else { - event = malloc (sizeof(*event)); - event->cont = cont; - event->cls = cls; - - GNUNET_IDENTITY_ego_lookup (my_cfg, - egoname, - &remove_did_document_ego_lookup_cb, - (void *) event); - } -} - - -/** - * @brief Create a did generate did object - * - * @param pkey - * @return void* Return pointer to the DID Document - */ -char * -create_did_generate (struct GNUNET_IDENTITY_PublicKey pkey) -{ - /* FIXME-MSC: I would prefer constants instead of magic numbers */ - char *pkey_str; // Convert public key to string - char did_str[71]; // 58 + 12 + 1 = 71 - char *didd_str; - char verify_id_str[77]; // did_str len + "#key-1" = 71 + 6 = 77 - char *pkey_multibase_str; - - /* FIXME-MSC: This screams for a GNUNET_DID_identity_key_to_string() */ - char *b64; - char pkx[34]; - pkx[0] = 0xed; - pkx[1] = 0x01; - memcpy (pkx + 2, &(pkey.eddsa_key), sizeof(pkey.eddsa_key)); - GNUNET_STRINGS_base64_encode (pkx, sizeof(pkx), &b64); - - GNUNET_asprintf (&pkey_multibase_str, "u%s", b64); - - json_t *didd; - json_t *did_json; - json_t *pkey_multibase_json; - json_t *context_json; - json_t *context_1_json; - json_t *context_2_json; - json_t *verify_json; - json_t *verify_1_json; - json_t *verify_1_type_json; - json_t *verify_1_id_json; - json_t *verify_relative_ref_json; - json_t *auth_json; - json_t *assert_json; - - /* FIXME-MSC: This screams for GNUNET_DID_identity_to_did() */ - pkey_str = GNUNET_IDENTITY_public_key_to_string (&pkey); // Convert public key to string - sprintf (did_str, "did:reclaim:%s", pkey_str); // Convert the public key to a DID str - sprintf (verify_id_str, "did:reclaim:%s#key-1", pkey_str); // Convert the public key to a DID str - - // sprintf(pkey_multibase_str, "V%s", pkey_str); // Convert the public key to MultiBase data format - - /* FIXME-MSC: This is effectively creating a DID Document default template for - * the initial document. - * Maybe this can be refactored to generate such a template for an identity? - * Even if higher layers add/modify it, there should probably still be a - * GNUNET_DID_document_template_from_identity() - */ - // Create Json Strings - did_json = json_string (did_str); - pkey_multibase_json = json_string (pkey_multibase_str); - - context_1_json = json_string ("https://www.w3.org/ns/did/v1"); - context_2_json = json_string ( - "https://w3id.org/security/suites/ed25519-2020/v1"); - verify_1_id_json = json_string (verify_id_str); - verify_1_type_json = json_string ("Ed25519VerificationKey2020"); - - // Add a relative DID URL to reference a verifiation method - // https://www.w3.org/TR/did-core/#relative-did-urls` - verify_relative_ref_json = json_string ("#key-1"); - - // Create DID Document - didd = json_object (); - - // Add context - context_json = json_array (); - json_array_append (context_json, context_1_json); - json_array_append (context_json, context_2_json); - json_object_set (didd, "@context", context_json); - - // Add id - json_object_set (didd, "id", did_json); - - // Add verification method - verify_json = json_array (); - verify_1_json = json_object (); - json_object_set (verify_1_json, "id", verify_1_id_json); - json_object_set (verify_1_json, "type", verify_1_type_json); - json_object_set (verify_1_json, "controller", did_json); - json_object_set (verify_1_json, "publicKeyMultiBase", pkey_multibase_json); - json_array_append (verify_json, verify_1_json); - json_object_set (didd, "verificationMethod", verify_json); - - // Add authentication method - auth_json = json_array (); - json_array_append (auth_json, verify_relative_ref_json); - json_object_set (didd, "authentication", auth_json); - - // Add assertion method to issue a Verifiable Credential - assert_json = json_array (); - json_array_append (assert_json, verify_relative_ref_json); - json_object_set (didd, "assertionMethod", assert_json); - - // Encode DID Document as JSON string - didd_str = json_dumps (didd, JSON_INDENT (2)); - if (didd_str == NULL) - { - printf ("DID Document could not be encoded"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return NULL; - } - - // TODO: MORE FREEEEEEEE - /* FIXME-MSC: json_t's are free'd using "json_decref". Also json_t usually - * keeps a reference counter. Check jansson docs for how to use it. - * Also: Use valgrind to find leaks. - */ - free (pkey_multibase_str); - free (b64); - - free (didd); - free (did_json); - free (pkey_multibase_json); - free (context_json); - free (context_1_json); - free (context_2_json); - free (verify_json); - free (verify_1_json); - free (verify_1_type_json); - free (verify_1_id_json); - free (auth_json); - free (assert_json); - free (verify_relative_ref_json); - - return didd_str; -} - -/** - * @brief Create a DID. Store DID in Namestore cb - * - */ -static void -create_did_store_cb (void *cls, int32_t success, const char *emsg) -{ - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 0; - return; -} - -/** - * @brief Create a did. Store DID in Namestore - * - * @param didd_str String endoced DID Docuement - * @param ego Identity whos DID Document is stored - */ -static void -create_did_store (char *didd_str, struct GNUNET_IDENTITY_Ego *ego) -{ - - struct GNUNET_TIME_Relative expire_time; - struct GNUNET_GNSRECORD_Data record_data; - const struct GNUNET_IDENTITY_PrivateKey *skey; - - if (GNUNET_STRINGS_fancy_time_to_relative ((NULL != expire) ? - expire : - GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME, - &expire_time) == GNUNET_OK) - { - record_data.data = didd_str; - record_data.expiration_time = expire_time.rel_value_us; - record_data.data_size = strlen (didd_str) + 1; - record_data.record_type = GNUNET_GNSRECORD_typename_to_number ("TXT"), - record_data.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - - skey = GNUNET_IDENTITY_ego_get_private_key (ego); - - GNUNET_NAMESTORE_records_store (namestore_handle, - skey, - GNUNET_GNS_EMPTY_LABEL_AT, - 1, //FIXME what if GNUNET_GNS_EMPTY_LABEL_AT has records - &record_data, - &create_did_store_cb, - NULL); - } - else { - printf ("Failed to read given expiration time\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } -} - -/** - * @brief Create a did ego lockup cb - * - * @param cls - * @param ego - */ -static void -create_did_ego_lockup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) -{ - struct GNUNET_IDENTITY_PublicKey pkey; - char *didd_str; - - if (ego == NULL) - { - printf ("EGO not found\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } - - GNUNET_IDENTITY_ego_get_public_key (ego, &pkey); - - if (ntohl (pkey.type) != GNUNET_GNSRECORD_TYPE_EDKEY) - { - printf ("The EGO has to have an EDDSA key pair\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } - - if (didd != NULL) - { - printf ( - "DID Docuement is read from \"did-document\" argument (EXPERIMENTAL)\n"); - didd_str = strdup (didd); - } - else { - // Generate DID Docuement from public key - didd_str = create_did_generate (pkey); - } - - // Print DID Document to stdout - printf ("%s\n", didd_str); - - // Store the DID Document - create_did_store (didd_str, ego); - - // Save DID Document String to GNS - free (didd_str); -} - -/** - * @brief Create a did document - Create a new identity first - */ -static void -create_did_document_ego_create_cb (void *cls, - const struct GNUNET_IDENTITY_PrivateKey *pk, - const char *emsg) -{ - - if (emsg != NULL) - { - printf ("%s\n", emsg); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } - - GNUNET_IDENTITY_ego_lookup (my_cfg, - egoname, - &create_did_ego_lockup_cb, - NULL); -} - -/** - * @brief Create a did document - * - */ -static void -create_did_document () -{ - if ((egoname != NULL) && (expire != NULL)) - { - GNUNET_IDENTITY_create (identity_handle, - egoname, - NULL, - GNUNET_IDENTITY_TYPE_EDDSA, - &create_did_document_ego_create_cb, - egoname); - } - else { - printf ( - "Set the EGO and the Expiration-time argument to create a new DID(-Document)\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } -} - - -/** - * @brief Replace a DID Docuemnt. Callback function after ego lockup - * - * @param cls - * @param ego - */ -static void -replace_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) -{ - create_did_store (didd, ego); -} - -/** - * @brief Replace a DID Document. Callback functiona after remove - * - * @param cls - */ -static void -replace_did_document_remove_cb (void *cls) -{ - GNUNET_IDENTITY_ego_lookup (my_cfg, - egoname, - &replace_did_document_ego_lookup_cb, - NULL); -} - -/** - * @brief Replace a DID Docuemnt - * - */ -static void -replace_did_document () -{ - if ((didd != NULL) && (expire != NULL)) - { - remove_did_document (&replace_did_document_remove_cb, NULL); - } - else { - printf ( - "Set the DID Document and expiration time argument to replace the DID Document\n"); - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } -} - -static void -post_ego_iteration (void *cls) -{ - if (1 == replace) - { - replace_did_document (); - } - else if (1 == get) - { - resolve_did_document (); - } - else if (1 == remove_did) - { - remove_did_document (NULL, NULL); - } - else if (1 == create) - { - create_did_document (); - } - else { - // No Argument found - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - return; - } -} - -static void -process_dids (void *cls, struct GNUNET_IDENTITY_Ego *ego, - void **ctx, const char*name) -{ - char *did_str; - - if (ego == NULL) - { - if (1 == ego_exists) - { - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - return; - } - GNUNET_SCHEDULER_add_now (&post_ego_iteration, NULL); - return; - } - if (NULL == name) - return; - if ((1 == create) && - (0 == strncmp (name, egoname, strlen (egoname))) && - (1 != ego_exists)) - { - fprintf(stderr, "%s already exists!\n", egoname); - ego_exists = 1; - return; - } - if (1 == show_all) - { - did_str = ego_to_did (ego); - printf ("%s\n", did_str); - GNUNET_free (did_str); - return; - } - if (1 == show) - { - if (0 == strncmp (name, egoname, strlen (egoname))) - { - did_str = ego_to_did (ego); - printf ("%s\n", did_str); - GNUNET_free (did_str); - return; - } - } -} - - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) -{ - gns_handle = GNUNET_GNS_connect (c); - namestore_handle = GNUNET_NAMESTORE_connect (c); - my_cfg = c; - - // check if GNS_handle could connect - if (gns_handle == NULL) - { - ret = 1; - return; - } - - // check if NAMESTORE_handle could connect - if (namestore_handle == NULL) - { - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } - - identity_handle = GNUNET_IDENTITY_connect (c, &process_dids, NULL); - if (identity_handle == NULL) - { - GNUNET_SCHEDULER_add_now (&cleanup, NULL); - ret = 1; - return; - } -} - -int -main (int argc, char *const argv[]) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_flag ('C', - "create", - gettext_noop ( - "Create a DID Document and display its DID"), - &create), - GNUNET_GETOPT_option_flag ('g', - "get", - gettext_noop ( - "Get the DID Document associated with the given DID"), - &get), - GNUNET_GETOPT_option_flag ('s', - "show", - gettext_noop ("Show the DID for a given ego"), - &show), - GNUNET_GETOPT_option_flag ('r', - "remove", - gettext_noop ( - "Remove the DID"), - &remove_did), - GNUNET_GETOPT_option_flag ('R', - "replace", - gettext_noop ("Replace the DID Document."), - &replace), - GNUNET_GETOPT_option_flag ('A', - "--show-all", - gettext_noop ("Replace the DID Document."), - &show_all), - GNUNET_GETOPT_option_string ('d', - "did", - "DID", - gettext_noop ( - "The Decentralized Identity (DID)"), - &did), - GNUNET_GETOPT_option_string ('D', - "--did-document", - "JSON", - gettext_noop ( - "The DID Document to store in GNUNET"), - &didd), - GNUNET_GETOPT_option_string ('e', - "ego", - "EGO", - gettext_noop ("The name of the EGO"), - &egoname), - GNUNET_GETOPT_option_string ('t', - "expiration-time", - "TIME", - gettext_noop ( - "The time until the DID Document is going to expire (e.g. 5d)"), - &expire), - GNUNET_GETOPT_OPTION_END - }; - - if (GNUNET_OK != GNUNET_PROGRAM_run (argc, - argv, - "gnunet-did", - _ ( - "Manage Decentralized Identities (DIDs)"), - options, - &run, - NULL)) - return 1; - else - return ret; -} diff --git a/src/did/test_w3c_ed25519_2020.c b/src/did/test_w3c_ed25519_2020.c deleted file mode 100644 index e2534e6ab..000000000 --- a/src/did/test_w3c_ed25519_2020.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2021 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - - -/** - * @file src/did/test_w3c_ed25519_2020.c - * @brief Testcases for the w3c Ed25519 formats for SSIs https://w3c-ccg.github.io/lds-ed25519-2020 - * @author Martin Schanzenbach - */ - -#include "platform.h" -#include "gnunet_crypto_lib.h" -#include "gnunet_strings_lib.h" - -static char test_privkey[32] = { - 0x9b, 0x93, 0x7b, 0x81, 0x32, 0x2d, 0x81, 0x6c, - 0xfa, 0xb9, 0xd5, 0xa3, 0xba, 0xac, 0xc9, 0xb2, - 0xa5, 0xfe, 0xbe, 0x4b, 0x14, 0x9f, 0x12, 0x6b, - 0x36, 0x30, 0xf9, 0x3a, 0x29, 0x52, 0x70, 0x17 -}; - -static char *targetPublicKeyMultibase = "u7QEJX5oaWV3edV2CeGhkrQPfpaT71ogyVmNk4rZeE8yeRA"; - -int -main () -{ - struct GNUNET_CRYPTO_EddsaPrivateKey privkey; - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - - memcpy (&privkey, test_privkey, sizeof (privkey)); - GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey); - - //This is how to convert out pubkeys to W3c Ed25519-2020 multibase (base64url no padding) - char *b64; - char pkx[34]; - pkx[0] = 0xed; - pkx[1] = 0x01; - memcpy (pkx+2, &pubkey, sizeof (pubkey)); - GNUNET_STRINGS_base64url_encode (pkx, - sizeof (pkx), - &b64); - printf ("u%s\n%s\n", b64, targetPublicKeyMultibase); - // FIXME convert pubkey to target - char *res; - GNUNET_asprintf (&res, "u%s", b64); - GNUNET_assert (0 == strcmp (res, - targetPublicKeyMultibase)); - - GNUNET_free (b64); - GNUNET_free (res); - return 0; -} diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am index 350d77d4b..6b5934a2f 100644 --- a/src/reclaim/Makefile.am +++ b/src/reclaim/Makefile.am @@ -43,7 +43,8 @@ plugin_LTLIBRARIES = \ $(REST_PLUGIN) bin_PROGRAMS = \ - gnunet-reclaim + gnunet-reclaim \ + gnunet-did libexec_PROGRAMS = \ gnunet-service-reclaim @@ -187,13 +188,30 @@ test_reclaim_attribute_LDADD = \ libgnunetreclaim.la \ $(GN_LIBINTL) +gnunet_did_SOURCES = \ + gnunet-did.c +gnunet_did_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/gns/libgnunetgns.la \ + $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + $(top_builddir)/src/namestore/libgnunetnamestore.la \ + -ljansson + + +test_w3c_ed25519_2020_SOURCES = \ + test_w3c_ed25519_2020.c +test_w3c_ed25519_2020_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + check_SCRIPTS = \ test_reclaim_attribute.sh \ test_reclaim_issue.sh \ test_reclaim_consume.sh check_PROGRAMS = \ - test_reclaim_attribute + test_reclaim_attribute \ + test_w3c_ed25519_2020 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; diff --git a/src/reclaim/gnunet-did.c b/src/reclaim/gnunet-did.c new file mode 100644 index 000000000..2ebef7601 --- /dev/null +++ b/src/reclaim/gnunet-did.c @@ -0,0 +1,922 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * FIXME: Do we only want to handle EdDSA identities? + * TODO: Own GNS record type + * TODO: Fix overwrite of records in @ if present look for other with same sub + * TODO. Tests + * TODO: Move constants to did.h + * FIXME: Remove and lookup require differnt representations (did vs egoname) + */ + +/** + * @author Tristan Schwieren + * @file src/did/gnunet-did.c + * @brief DID Method Wrapper + * + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_namestore_service.h" +#include "gnunet_gns_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "jansson.h" + +#define GNUNET_DID_METHOD_RECLAIM_PREFIX "did:reclaim:" +#define GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME "1d" + +/** + * return value + */ +static int ret; + +/** + * Replace DID Document Flag + */ +static int replace; + +/** + * Remove DID Document Flag + */ +static int remove_did; + +/** + * Get DID Documement for DID Flag + */ +static int get; + +/** + * Create DID Document Flag + */ +static int create; + +/** + * Show DID for Ego Flag + */ +static int show; + +/** + * Show DID for Ego Flag + */ +static int show_all; + +/** + * DID Attribut String + */ +static char *did; + +/** + * DID Document Attribut String + */ +static char *didd; + +/** + * Ego Attribut String + */ +static char *egoname; + +/** + * DID Document expiration Date Attribut String + */ +static char *expire; + +/* + * Handle to the GNS service + */ +static struct GNUNET_GNS_Handle *gns_handle; + +/* + * Handle to the NAMESTORE service + */ +static struct GNUNET_NAMESTORE_Handle *namestore_handle; + +/* + * Handle to the IDENTITY service + */ +static struct GNUNET_IDENTITY_Handle *identity_handle; + + +/* + * The configuration + */ +const static struct GNUNET_CONFIGURATION_Handle *my_cfg; + +/** + * Give ego exists + */ +static int ego_exists = 0; + +/** + * @brief Disconnect and shutdown + * @param cls closure + */ +static void +cleanup (void *cls) +{ + if (NULL != gns_handle) + GNUNET_GNS_disconnect (gns_handle); + if (NULL != namestore_handle) + GNUNET_NAMESTORE_disconnect (namestore_handle); + if (NULL != identity_handle) + GNUNET_IDENTITY_disconnect (identity_handle); + + GNUNET_free (did); + GNUNET_free (didd); + GNUNET_free (egoname); + GNUNET_free (expire); + + GNUNET_SCHEDULER_shutdown (); +} + +char* +ego_to_did (struct GNUNET_IDENTITY_Ego *ego) +{ + struct GNUNET_IDENTITY_PublicKey pkey; // Get Public key + char *pkey_str; + char *did_str; + size_t pkey_len; + + GNUNET_IDENTITY_ego_get_public_key (ego, &pkey); + + pkey_str = GNUNET_IDENTITY_public_key_to_string (&pkey); + GNUNET_asprintf (&did_str, "%s%s", + GNUNET_DID_METHOD_RECLAIM_PREFIX, + pkey_str); + + free (pkey_str); + return did_str; +} + +/** + * @brief Callback for ego loockup of get_did_for_ego() + * + * @param cls closure + * @param ego the returned ego + */ +static void +get_did_for_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + char *did_str; + + if (ego == NULL) + { + printf ("EGO not found\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } + did_str = ego_to_did (ego); + + printf ("%s\n", did_str); + + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 0; + return; +} + +/** + * @brief Get the DID for a given EGO + * + */ +static void +get_did_for_ego () +{ + if (egoname != NULL) + { + GNUNET_IDENTITY_ego_lookup (my_cfg, + egoname, + &get_did_for_ego_lookup_cb, + NULL); + } + else { + printf ("Set the EGO argument to get the DID for a given EGO\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } +} + + +/** + * @brief Get the public key from did attribute given by the user + * + * @param pkey place to write the public key to + */ +static void +get_pkey_from_attr_did (struct GNUNET_IDENTITY_PublicKey *pkey) +{ + /* FIXME-MSC: I suggest introducing a + * #define MAX_DID_LENGTH + * here and use it for parsing + */ + char pkey_str[59]; + + if ((1 != (sscanf (did, GNUNET_DID_METHOD_RECLAIM_PREFIX"%58s", pkey_str))) || + (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (pkey_str, pkey))) + { + fprintf (stderr, _("Invalid DID `%s'\n"), pkey_str); + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 1; + return; + } +} + +/** + * @brief GNS lookup callback. Prints the DID Document to standard out. + * Fails if there is more than one DID record. + * + * @param cls closure + * @param rd_count number of records in @a rd + * @param rd the records in the reply + */ +static void +print_did_document ( + void *cls, + uint32_t rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + /* + * FIXME-MSC: The user may decide to put other records here. + * In general I am fine with the constraint here, but not when + * we move it to "@" + */ + if (rd_count != 1) + { + printf ("An ego should only have one DID Document\n"); + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 1; + return; + } + + if (rd[0].record_type == GNUNET_DNSPARSER_TYPE_TXT) + { + printf ("%s\n", (char *) rd[0].data); + } + else { + printf ("DID Document is not a TXT record\n"); + } + + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 0; + return; +} + +/** + * @brief Resolve a DID given by the user. + */ +static void +resolve_did_document () +{ + struct GNUNET_IDENTITY_PublicKey pkey; + + if (did == NULL) + { + printf ("Set DID option to resolve DID\n"); + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 1; + return; + } + + get_pkey_from_attr_did (&pkey); + + GNUNET_GNS_lookup (gns_handle, GNUNET_GNS_EMPTY_LABEL_AT, &pkey, GNUNET_DNSPARSER_TYPE_TXT, + GNUNET_GNS_LO_DEFAULT, &print_did_document, NULL); +} + + +/** + * @brief Signature of a callback function that is called after a did has been removed + */ +typedef void +(*remove_did_document_callback) (void *cls); + +/** + * @brief A Structure containing a cont and cls. Can be passed as a cls to a callback function + * + */ +struct Event +{ + remove_did_document_callback cont; + void *cls; +}; + +/** + * @brief Implements the GNUNET_NAMESTORE_ContinuationWithStatus + * Calls the callback function and cls in the event struct + * + * @param cls closure containing the event struct + * @param success + * @param emgs + */ +static void +remove_did_document_namestore_cb (void *cls, int32_t success, const char *emgs) +{ + struct Event *event; + + if (success != GNUNET_SYSERR) + { + event = (struct Event *) cls; + + if (event->cont != NULL) + { + event->cont (event->cls); + free (event); + } + else { + free (event); + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 0; + return; + } + } else { + printf ("Something went wrong when deleting the DID Document\n"); + + if (emgs != NULL) + { + printf ("%s\n", emgs); + } + + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 0; + return; + } +} + +/** + * @brief Callback called after the ego has been locked up + * + * @param cls closure + * @param ego the ego returned by the identity service + */ +static void +remove_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + const struct GNUNET_IDENTITY_PrivateKey *skey = + GNUNET_IDENTITY_ego_get_private_key (ego); + + GNUNET_NAMESTORE_records_store (namestore_handle, + skey, + GNUNET_GNS_EMPTY_LABEL_AT, + 0, + NULL, + &remove_did_document_namestore_cb, + cls); +} + +/** + * @brief Remove a DID Document + */ +static void +remove_did_document (remove_did_document_callback cont, void *cls) +{ + struct Event *event; + + if (egoname == NULL) + { + printf ("Remove requieres an ego option\n"); + GNUNET_SCHEDULER_add_now (cleanup, NULL); + ret = 1; + return; + } + else { + event = malloc (sizeof(*event)); + event->cont = cont; + event->cls = cls; + + GNUNET_IDENTITY_ego_lookup (my_cfg, + egoname, + &remove_did_document_ego_lookup_cb, + (void *) event); + } +} + + +/** + * @brief Create a did generate did object + * + * @param pkey + * @return void* Return pointer to the DID Document + */ +char * +create_did_generate (struct GNUNET_IDENTITY_PublicKey pkey) +{ + /* FIXME-MSC: I would prefer constants instead of magic numbers */ + char *pkey_str; // Convert public key to string + char did_str[71]; // 58 + 12 + 1 = 71 + char *didd_str; + char verify_id_str[77]; // did_str len + "#key-1" = 71 + 6 = 77 + char *pkey_multibase_str; + + /* FIXME-MSC: This screams for a GNUNET_DID_identity_key_to_string() */ + char *b64; + char pkx[34]; + pkx[0] = 0xed; + pkx[1] = 0x01; + memcpy (pkx + 2, &(pkey.eddsa_key), sizeof(pkey.eddsa_key)); + GNUNET_STRINGS_base64_encode (pkx, sizeof(pkx), &b64); + + GNUNET_asprintf (&pkey_multibase_str, "u%s", b64); + + json_t *didd; + json_t *did_json; + json_t *pkey_multibase_json; + json_t *context_json; + json_t *context_1_json; + json_t *context_2_json; + json_t *verify_json; + json_t *verify_1_json; + json_t *verify_1_type_json; + json_t *verify_1_id_json; + json_t *verify_relative_ref_json; + json_t *auth_json; + json_t *assert_json; + + /* FIXME-MSC: This screams for GNUNET_DID_identity_to_did() */ + pkey_str = GNUNET_IDENTITY_public_key_to_string (&pkey); // Convert public key to string + sprintf (did_str, "did:reclaim:%s", pkey_str); // Convert the public key to a DID str + sprintf (verify_id_str, "did:reclaim:%s#key-1", pkey_str); // Convert the public key to a DID str + + // sprintf(pkey_multibase_str, "V%s", pkey_str); // Convert the public key to MultiBase data format + + /* FIXME-MSC: This is effectively creating a DID Document default template for + * the initial document. + * Maybe this can be refactored to generate such a template for an identity? + * Even if higher layers add/modify it, there should probably still be a + * GNUNET_DID_document_template_from_identity() + */ + // Create Json Strings + did_json = json_string (did_str); + pkey_multibase_json = json_string (pkey_multibase_str); + + context_1_json = json_string ("https://www.w3.org/ns/did/v1"); + context_2_json = json_string ( + "https://w3id.org/security/suites/ed25519-2020/v1"); + verify_1_id_json = json_string (verify_id_str); + verify_1_type_json = json_string ("Ed25519VerificationKey2020"); + + // Add a relative DID URL to reference a verifiation method + // https://www.w3.org/TR/did-core/#relative-did-urls` + verify_relative_ref_json = json_string ("#key-1"); + + // Create DID Document + didd = json_object (); + + // Add context + context_json = json_array (); + json_array_append (context_json, context_1_json); + json_array_append (context_json, context_2_json); + json_object_set (didd, "@context", context_json); + + // Add id + json_object_set (didd, "id", did_json); + + // Add verification method + verify_json = json_array (); + verify_1_json = json_object (); + json_object_set (verify_1_json, "id", verify_1_id_json); + json_object_set (verify_1_json, "type", verify_1_type_json); + json_object_set (verify_1_json, "controller", did_json); + json_object_set (verify_1_json, "publicKeyMultiBase", pkey_multibase_json); + json_array_append (verify_json, verify_1_json); + json_object_set (didd, "verificationMethod", verify_json); + + // Add authentication method + auth_json = json_array (); + json_array_append (auth_json, verify_relative_ref_json); + json_object_set (didd, "authentication", auth_json); + + // Add assertion method to issue a Verifiable Credential + assert_json = json_array (); + json_array_append (assert_json, verify_relative_ref_json); + json_object_set (didd, "assertionMethod", assert_json); + + // Encode DID Document as JSON string + didd_str = json_dumps (didd, JSON_INDENT (2)); + if (didd_str == NULL) + { + printf ("DID Document could not be encoded"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return NULL; + } + + // TODO: MORE FREEEEEEEE + /* FIXME-MSC: json_t's are free'd using "json_decref". Also json_t usually + * keeps a reference counter. Check jansson docs for how to use it. + * Also: Use valgrind to find leaks. + */ + free (pkey_multibase_str); + free (b64); + + free (didd); + free (did_json); + free (pkey_multibase_json); + free (context_json); + free (context_1_json); + free (context_2_json); + free (verify_json); + free (verify_1_json); + free (verify_1_type_json); + free (verify_1_id_json); + free (auth_json); + free (assert_json); + free (verify_relative_ref_json); + + return didd_str; +} + +/** + * @brief Create a DID. Store DID in Namestore cb + * + */ +static void +create_did_store_cb (void *cls, int32_t success, const char *emsg) +{ + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 0; + return; +} + +/** + * @brief Create a did. Store DID in Namestore + * + * @param didd_str String endoced DID Docuement + * @param ego Identity whos DID Document is stored + */ +static void +create_did_store (char *didd_str, struct GNUNET_IDENTITY_Ego *ego) +{ + + struct GNUNET_TIME_Relative expire_time; + struct GNUNET_GNSRECORD_Data record_data; + const struct GNUNET_IDENTITY_PrivateKey *skey; + + if (GNUNET_STRINGS_fancy_time_to_relative ((NULL != expire) ? + expire : + GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME, + &expire_time) == GNUNET_OK) + { + record_data.data = didd_str; + record_data.expiration_time = expire_time.rel_value_us; + record_data.data_size = strlen (didd_str) + 1; + record_data.record_type = GNUNET_GNSRECORD_typename_to_number ("TXT"), + record_data.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + + skey = GNUNET_IDENTITY_ego_get_private_key (ego); + + GNUNET_NAMESTORE_records_store (namestore_handle, + skey, + GNUNET_GNS_EMPTY_LABEL_AT, + 1, //FIXME what if GNUNET_GNS_EMPTY_LABEL_AT has records + &record_data, + &create_did_store_cb, + NULL); + } + else { + printf ("Failed to read given expiration time\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } +} + +/** + * @brief Create a did ego lockup cb + * + * @param cls + * @param ego + */ +static void +create_did_ego_lockup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + struct GNUNET_IDENTITY_PublicKey pkey; + char *didd_str; + + if (ego == NULL) + { + printf ("EGO not found\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } + + GNUNET_IDENTITY_ego_get_public_key (ego, &pkey); + + if (ntohl (pkey.type) != GNUNET_GNSRECORD_TYPE_EDKEY) + { + printf ("The EGO has to have an EDDSA key pair\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } + + if (didd != NULL) + { + printf ( + "DID Docuement is read from \"did-document\" argument (EXPERIMENTAL)\n"); + didd_str = strdup (didd); + } + else { + // Generate DID Docuement from public key + didd_str = create_did_generate (pkey); + } + + // Print DID Document to stdout + printf ("%s\n", didd_str); + + // Store the DID Document + create_did_store (didd_str, ego); + + // Save DID Document String to GNS + free (didd_str); +} + +/** + * @brief Create a did document - Create a new identity first + */ +static void +create_did_document_ego_create_cb (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *pk, + const char *emsg) +{ + + if (emsg != NULL) + { + printf ("%s\n", emsg); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } + + GNUNET_IDENTITY_ego_lookup (my_cfg, + egoname, + &create_did_ego_lockup_cb, + NULL); +} + +/** + * @brief Create a did document + * + */ +static void +create_did_document () +{ + if ((egoname != NULL) && (expire != NULL)) + { + GNUNET_IDENTITY_create (identity_handle, + egoname, + NULL, + GNUNET_IDENTITY_TYPE_EDDSA, + &create_did_document_ego_create_cb, + egoname); + } + else { + printf ( + "Set the EGO and the Expiration-time argument to create a new DID(-Document)\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } +} + + +/** + * @brief Replace a DID Docuemnt. Callback function after ego lockup + * + * @param cls + * @param ego + */ +static void +replace_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + create_did_store (didd, ego); +} + +/** + * @brief Replace a DID Document. Callback functiona after remove + * + * @param cls + */ +static void +replace_did_document_remove_cb (void *cls) +{ + GNUNET_IDENTITY_ego_lookup (my_cfg, + egoname, + &replace_did_document_ego_lookup_cb, + NULL); +} + +/** + * @brief Replace a DID Docuemnt + * + */ +static void +replace_did_document () +{ + if ((didd != NULL) && (expire != NULL)) + { + remove_did_document (&replace_did_document_remove_cb, NULL); + } + else { + printf ( + "Set the DID Document and expiration time argument to replace the DID Document\n"); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } +} + +static void +post_ego_iteration (void *cls) +{ + if (1 == replace) + { + replace_did_document (); + } + else if (1 == get) + { + resolve_did_document (); + } + else if (1 == remove_did) + { + remove_did_document (NULL, NULL); + } + else if (1 == create) + { + create_did_document (); + } + else { + // No Argument found + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + return; + } +} + +static void +process_dids (void *cls, struct GNUNET_IDENTITY_Ego *ego, + void **ctx, const char*name) +{ + char *did_str; + + if (ego == NULL) + { + if (1 == ego_exists) + { + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + return; + } + GNUNET_SCHEDULER_add_now (&post_ego_iteration, NULL); + return; + } + if (NULL == name) + return; + if ((1 == create) && + (0 == strncmp (name, egoname, strlen (egoname))) && + (1 != ego_exists)) + { + fprintf(stderr, "%s already exists!\n", egoname); + ego_exists = 1; + return; + } + if (1 == show_all) + { + did_str = ego_to_did (ego); + printf ("%s\n", did_str); + GNUNET_free (did_str); + return; + } + if (1 == show) + { + if (0 == strncmp (name, egoname, strlen (egoname))) + { + did_str = ego_to_did (ego); + printf ("%s\n", did_str); + GNUNET_free (did_str); + return; + } + } +} + + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + gns_handle = GNUNET_GNS_connect (c); + namestore_handle = GNUNET_NAMESTORE_connect (c); + my_cfg = c; + + // check if GNS_handle could connect + if (gns_handle == NULL) + { + ret = 1; + return; + } + + // check if NAMESTORE_handle could connect + if (namestore_handle == NULL) + { + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } + + identity_handle = GNUNET_IDENTITY_connect (c, &process_dids, NULL); + if (identity_handle == NULL) + { + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 1; + return; + } +} + +int +main (int argc, char *const argv[]) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_flag ('C', + "create", + gettext_noop ( + "Create a DID Document and display its DID"), + &create), + GNUNET_GETOPT_option_flag ('g', + "get", + gettext_noop ( + "Get the DID Document associated with the given DID"), + &get), + GNUNET_GETOPT_option_flag ('s', + "show", + gettext_noop ("Show the DID for a given ego"), + &show), + GNUNET_GETOPT_option_flag ('r', + "remove", + gettext_noop ( + "Remove the DID"), + &remove_did), + GNUNET_GETOPT_option_flag ('R', + "replace", + gettext_noop ("Replace the DID Document."), + &replace), + GNUNET_GETOPT_option_flag ('A', + "--show-all", + gettext_noop ("Replace the DID Document."), + &show_all), + GNUNET_GETOPT_option_string ('d', + "did", + "DID", + gettext_noop ( + "The Decentralized Identity (DID)"), + &did), + GNUNET_GETOPT_option_string ('D', + "--did-document", + "JSON", + gettext_noop ( + "The DID Document to store in GNUNET"), + &didd), + GNUNET_GETOPT_option_string ('e', + "ego", + "EGO", + gettext_noop ("The name of the EGO"), + &egoname), + GNUNET_GETOPT_option_string ('t', + "expiration-time", + "TIME", + gettext_noop ( + "The time until the DID Document is going to expire (e.g. 5d)"), + &expire), + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != GNUNET_PROGRAM_run (argc, + argv, + "gnunet-did", + _ ( + "Manage Decentralized Identities (DIDs)"), + options, + &run, + NULL)) + return 1; + else + return ret; +} diff --git a/src/reclaim/test_w3c_ed25519_2020.c b/src/reclaim/test_w3c_ed25519_2020.c new file mode 100644 index 000000000..e2534e6ab --- /dev/null +++ b/src/reclaim/test_w3c_ed25519_2020.c @@ -0,0 +1,69 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + + +/** + * @file src/did/test_w3c_ed25519_2020.c + * @brief Testcases for the w3c Ed25519 formats for SSIs https://w3c-ccg.github.io/lds-ed25519-2020 + * @author Martin Schanzenbach + */ + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_strings_lib.h" + +static char test_privkey[32] = { + 0x9b, 0x93, 0x7b, 0x81, 0x32, 0x2d, 0x81, 0x6c, + 0xfa, 0xb9, 0xd5, 0xa3, 0xba, 0xac, 0xc9, 0xb2, + 0xa5, 0xfe, 0xbe, 0x4b, 0x14, 0x9f, 0x12, 0x6b, + 0x36, 0x30, 0xf9, 0x3a, 0x29, 0x52, 0x70, 0x17 +}; + +static char *targetPublicKeyMultibase = "u7QEJX5oaWV3edV2CeGhkrQPfpaT71ogyVmNk4rZeE8yeRA"; + +int +main () +{ + struct GNUNET_CRYPTO_EddsaPrivateKey privkey; + struct GNUNET_CRYPTO_EddsaPublicKey pubkey; + + memcpy (&privkey, test_privkey, sizeof (privkey)); + GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey); + + //This is how to convert out pubkeys to W3c Ed25519-2020 multibase (base64url no padding) + char *b64; + char pkx[34]; + pkx[0] = 0xed; + pkx[1] = 0x01; + memcpy (pkx+2, &pubkey, sizeof (pubkey)); + GNUNET_STRINGS_base64url_encode (pkx, + sizeof (pkx), + &b64); + printf ("u%s\n%s\n", b64, targetPublicKeyMultibase); + // FIXME convert pubkey to target + char *res; + GNUNET_asprintf (&res, "u%s", b64); + GNUNET_assert (0 == strcmp (res, + targetPublicKeyMultibase)); + + GNUNET_free (b64); + GNUNET_free (res); + return 0; +} -- cgit v1.2.3