diff options
author | Martin Schanzenbach <schanzen@gnunet.org> | 2022-01-14 18:42:27 +0100 |
---|---|---|
committer | Martin Schanzenbach <schanzen@gnunet.org> | 2022-01-14 18:42:27 +0100 |
commit | 7e43ac55443b309d193f5c9f3dea02452205880d (patch) | |
tree | 958a12e9fa0cea19c62e001ec0ca29b63a5e10a3 | |
parent | 004c81e6a357baac6f3092710f0e866d0ea90881 (diff) | |
download | gnunet-7e43ac55443b309d193f5c9f3dea02452205880d.tar.gz gnunet-7e43ac55443b309d193f5c9f3dea02452205880d.zip |
RECLAIM: Add DIDs - trizuz
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/reclaim/Makefile.am | 22 | ||||
-rw-r--r-- | src/reclaim/gnunet-did.c | 922 | ||||
-rw-r--r-- | src/reclaim/test_w3c_ed25519_2020.c | 69 |
5 files changed, 1014 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index feee06f9f..096a9f768 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -1367,6 +1367,7 @@ src/dht/Makefile | |||
1367 | src/dht/dht.conf | 1367 | src/dht/dht.conf |
1368 | src/dhtu/Makefile | 1368 | src/dhtu/Makefile |
1369 | src/dns/Makefile | 1369 | src/dns/Makefile |
1370 | src/did/Makefile | ||
1370 | src/dns/dns.conf | 1371 | src/dns/dns.conf |
1371 | src/exit/Makefile | 1372 | src/exit/Makefile |
1372 | src/fragmentation/Makefile | 1373 | src/fragmentation/Makefile |
diff --git a/src/Makefile.am b/src/Makefile.am index 4c665c3b3..06d018c56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
@@ -6,7 +6,8 @@ TESTBED = testbed-logger testbed | |||
6 | if HAVE_EXPERIMENTAL | 6 | if HAVE_EXPERIMENTAL |
7 | EXP_DIR = \ | 7 | EXP_DIR = \ |
8 | rps \ | 8 | rps \ |
9 | abd | 9 | abd \ |
10 | did | ||
10 | if HAVE_ABE | 11 | if HAVE_ABE |
11 | EXP_DIR += \ | 12 | EXP_DIR += \ |
12 | abe | 13 | abe |
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 = \ | |||
43 | $(REST_PLUGIN) | 43 | $(REST_PLUGIN) |
44 | 44 | ||
45 | bin_PROGRAMS = \ | 45 | bin_PROGRAMS = \ |
46 | gnunet-reclaim | 46 | gnunet-reclaim \ |
47 | gnunet-did | ||
47 | 48 | ||
48 | libexec_PROGRAMS = \ | 49 | libexec_PROGRAMS = \ |
49 | gnunet-service-reclaim | 50 | gnunet-service-reclaim |
@@ -187,13 +188,30 @@ test_reclaim_attribute_LDADD = \ | |||
187 | libgnunetreclaim.la \ | 188 | libgnunetreclaim.la \ |
188 | $(GN_LIBINTL) | 189 | $(GN_LIBINTL) |
189 | 190 | ||
191 | gnunet_did_SOURCES = \ | ||
192 | gnunet-did.c | ||
193 | gnunet_did_LDADD = \ | ||
194 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
195 | $(top_builddir)/src/gns/libgnunetgns.la \ | ||
196 | $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ | ||
197 | $(top_builddir)/src/identity/libgnunetidentity.la \ | ||
198 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ | ||
199 | -ljansson | ||
200 | |||
201 | |||
202 | test_w3c_ed25519_2020_SOURCES = \ | ||
203 | test_w3c_ed25519_2020.c | ||
204 | test_w3c_ed25519_2020_LDADD = \ | ||
205 | $(top_builddir)/src/util/libgnunetutil.la | ||
206 | |||
190 | check_SCRIPTS = \ | 207 | check_SCRIPTS = \ |
191 | test_reclaim_attribute.sh \ | 208 | test_reclaim_attribute.sh \ |
192 | test_reclaim_issue.sh \ | 209 | test_reclaim_issue.sh \ |
193 | test_reclaim_consume.sh | 210 | test_reclaim_consume.sh |
194 | 211 | ||
195 | check_PROGRAMS = \ | 212 | check_PROGRAMS = \ |
196 | test_reclaim_attribute | 213 | test_reclaim_attribute \ |
214 | test_w3c_ed25519_2020 | ||
197 | 215 | ||
198 | if ENABLE_TEST_RUN | 216 | if ENABLE_TEST_RUN |
199 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | 217 | 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 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012-2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * FIXME: Do we only want to handle EdDSA identities? | ||
23 | * TODO: Own GNS record type | ||
24 | * TODO: Fix overwrite of records in @ if present look for other with same sub | ||
25 | * TODO. Tests | ||
26 | * TODO: Move constants to did.h | ||
27 | * FIXME: Remove and lookup require differnt representations (did vs egoname) | ||
28 | */ | ||
29 | |||
30 | /** | ||
31 | * @author Tristan Schwieren | ||
32 | * @file src/did/gnunet-did.c | ||
33 | * @brief DID Method Wrapper | ||
34 | * | ||
35 | */ | ||
36 | #include "platform.h" | ||
37 | #include "gnunet_util_lib.h" | ||
38 | #include "gnunet_namestore_service.h" | ||
39 | #include "gnunet_gns_service.h" | ||
40 | #include "gnunet_gnsrecord_lib.h" | ||
41 | #include "jansson.h" | ||
42 | |||
43 | #define GNUNET_DID_METHOD_RECLAIM_PREFIX "did:reclaim:" | ||
44 | #define GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME "1d" | ||
45 | |||
46 | /** | ||
47 | * return value | ||
48 | */ | ||
49 | static int ret; | ||
50 | |||
51 | /** | ||
52 | * Replace DID Document Flag | ||
53 | */ | ||
54 | static int replace; | ||
55 | |||
56 | /** | ||
57 | * Remove DID Document Flag | ||
58 | */ | ||
59 | static int remove_did; | ||
60 | |||
61 | /** | ||
62 | * Get DID Documement for DID Flag | ||
63 | */ | ||
64 | static int get; | ||
65 | |||
66 | /** | ||
67 | * Create DID Document Flag | ||
68 | */ | ||
69 | static int create; | ||
70 | |||
71 | /** | ||
72 | * Show DID for Ego Flag | ||
73 | */ | ||
74 | static int show; | ||
75 | |||
76 | /** | ||
77 | * Show DID for Ego Flag | ||
78 | */ | ||
79 | static int show_all; | ||
80 | |||
81 | /** | ||
82 | * DID Attribut String | ||
83 | */ | ||
84 | static char *did; | ||
85 | |||
86 | /** | ||
87 | * DID Document Attribut String | ||
88 | */ | ||
89 | static char *didd; | ||
90 | |||
91 | /** | ||
92 | * Ego Attribut String | ||
93 | */ | ||
94 | static char *egoname; | ||
95 | |||
96 | /** | ||
97 | * DID Document expiration Date Attribut String | ||
98 | */ | ||
99 | static char *expire; | ||
100 | |||
101 | /* | ||
102 | * Handle to the GNS service | ||
103 | */ | ||
104 | static struct GNUNET_GNS_Handle *gns_handle; | ||
105 | |||
106 | /* | ||
107 | * Handle to the NAMESTORE service | ||
108 | */ | ||
109 | static struct GNUNET_NAMESTORE_Handle *namestore_handle; | ||
110 | |||
111 | /* | ||
112 | * Handle to the IDENTITY service | ||
113 | */ | ||
114 | static struct GNUNET_IDENTITY_Handle *identity_handle; | ||
115 | |||
116 | |||
117 | /* | ||
118 | * The configuration | ||
119 | */ | ||
120 | const static struct GNUNET_CONFIGURATION_Handle *my_cfg; | ||
121 | |||
122 | /** | ||
123 | * Give ego exists | ||
124 | */ | ||
125 | static int ego_exists = 0; | ||
126 | |||
127 | /** | ||
128 | * @brief Disconnect and shutdown | ||
129 | * @param cls closure | ||
130 | */ | ||
131 | static void | ||
132 | cleanup (void *cls) | ||
133 | { | ||
134 | if (NULL != gns_handle) | ||
135 | GNUNET_GNS_disconnect (gns_handle); | ||
136 | if (NULL != namestore_handle) | ||
137 | GNUNET_NAMESTORE_disconnect (namestore_handle); | ||
138 | if (NULL != identity_handle) | ||
139 | GNUNET_IDENTITY_disconnect (identity_handle); | ||
140 | |||
141 | GNUNET_free (did); | ||
142 | GNUNET_free (didd); | ||
143 | GNUNET_free (egoname); | ||
144 | GNUNET_free (expire); | ||
145 | |||
146 | GNUNET_SCHEDULER_shutdown (); | ||
147 | } | ||
148 | |||
149 | char* | ||
150 | ego_to_did (struct GNUNET_IDENTITY_Ego *ego) | ||
151 | { | ||
152 | struct GNUNET_IDENTITY_PublicKey pkey; // Get Public key | ||
153 | char *pkey_str; | ||
154 | char *did_str; | ||
155 | size_t pkey_len; | ||
156 | |||
157 | GNUNET_IDENTITY_ego_get_public_key (ego, &pkey); | ||
158 | |||
159 | pkey_str = GNUNET_IDENTITY_public_key_to_string (&pkey); | ||
160 | GNUNET_asprintf (&did_str, "%s%s", | ||
161 | GNUNET_DID_METHOD_RECLAIM_PREFIX, | ||
162 | pkey_str); | ||
163 | |||
164 | free (pkey_str); | ||
165 | return did_str; | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * @brief Callback for ego loockup of get_did_for_ego() | ||
170 | * | ||
171 | * @param cls closure | ||
172 | * @param ego the returned ego | ||
173 | */ | ||
174 | static void | ||
175 | get_did_for_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
176 | { | ||
177 | char *did_str; | ||
178 | |||
179 | if (ego == NULL) | ||
180 | { | ||
181 | printf ("EGO not found\n"); | ||
182 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
183 | ret = 1; | ||
184 | return; | ||
185 | } | ||
186 | did_str = ego_to_did (ego); | ||
187 | |||
188 | printf ("%s\n", did_str); | ||
189 | |||
190 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
191 | ret = 0; | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * @brief Get the DID for a given EGO | ||
197 | * | ||
198 | */ | ||
199 | static void | ||
200 | get_did_for_ego () | ||
201 | { | ||
202 | if (egoname != NULL) | ||
203 | { | ||
204 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
205 | egoname, | ||
206 | &get_did_for_ego_lookup_cb, | ||
207 | NULL); | ||
208 | } | ||
209 | else { | ||
210 | printf ("Set the EGO argument to get the DID for a given EGO\n"); | ||
211 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
212 | ret = 1; | ||
213 | return; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | |||
218 | /** | ||
219 | * @brief Get the public key from did attribute given by the user | ||
220 | * | ||
221 | * @param pkey place to write the public key to | ||
222 | */ | ||
223 | static void | ||
224 | get_pkey_from_attr_did (struct GNUNET_IDENTITY_PublicKey *pkey) | ||
225 | { | ||
226 | /* FIXME-MSC: I suggest introducing a | ||
227 | * #define MAX_DID_LENGTH <something> | ||
228 | * here and use it for parsing | ||
229 | */ | ||
230 | char pkey_str[59]; | ||
231 | |||
232 | if ((1 != (sscanf (did, GNUNET_DID_METHOD_RECLAIM_PREFIX"%58s", pkey_str))) || | ||
233 | (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (pkey_str, pkey))) | ||
234 | { | ||
235 | fprintf (stderr, _("Invalid DID `%s'\n"), pkey_str); | ||
236 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
237 | ret = 1; | ||
238 | return; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * @brief GNS lookup callback. Prints the DID Document to standard out. | ||
244 | * Fails if there is more than one DID record. | ||
245 | * | ||
246 | * @param cls closure | ||
247 | * @param rd_count number of records in @a rd | ||
248 | * @param rd the records in the reply | ||
249 | */ | ||
250 | static void | ||
251 | print_did_document ( | ||
252 | void *cls, | ||
253 | uint32_t rd_count, | ||
254 | const struct GNUNET_GNSRECORD_Data *rd) | ||
255 | { | ||
256 | /* | ||
257 | * FIXME-MSC: The user may decide to put other records here. | ||
258 | * In general I am fine with the constraint here, but not when | ||
259 | * we move it to "@" | ||
260 | */ | ||
261 | if (rd_count != 1) | ||
262 | { | ||
263 | printf ("An ego should only have one DID Document\n"); | ||
264 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
265 | ret = 1; | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | if (rd[0].record_type == GNUNET_DNSPARSER_TYPE_TXT) | ||
270 | { | ||
271 | printf ("%s\n", (char *) rd[0].data); | ||
272 | } | ||
273 | else { | ||
274 | printf ("DID Document is not a TXT record\n"); | ||
275 | } | ||
276 | |||
277 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
278 | ret = 0; | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * @brief Resolve a DID given by the user. | ||
284 | */ | ||
285 | static void | ||
286 | resolve_did_document () | ||
287 | { | ||
288 | struct GNUNET_IDENTITY_PublicKey pkey; | ||
289 | |||
290 | if (did == NULL) | ||
291 | { | ||
292 | printf ("Set DID option to resolve DID\n"); | ||
293 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
294 | ret = 1; | ||
295 | return; | ||
296 | } | ||
297 | |||
298 | get_pkey_from_attr_did (&pkey); | ||
299 | |||
300 | GNUNET_GNS_lookup (gns_handle, GNUNET_GNS_EMPTY_LABEL_AT, &pkey, GNUNET_DNSPARSER_TYPE_TXT, | ||
301 | GNUNET_GNS_LO_DEFAULT, &print_did_document, NULL); | ||
302 | } | ||
303 | |||
304 | |||
305 | /** | ||
306 | * @brief Signature of a callback function that is called after a did has been removed | ||
307 | */ | ||
308 | typedef void | ||
309 | (*remove_did_document_callback) (void *cls); | ||
310 | |||
311 | /** | ||
312 | * @brief A Structure containing a cont and cls. Can be passed as a cls to a callback function | ||
313 | * | ||
314 | */ | ||
315 | struct Event | ||
316 | { | ||
317 | remove_did_document_callback cont; | ||
318 | void *cls; | ||
319 | }; | ||
320 | |||
321 | /** | ||
322 | * @brief Implements the GNUNET_NAMESTORE_ContinuationWithStatus | ||
323 | * Calls the callback function and cls in the event struct | ||
324 | * | ||
325 | * @param cls closure containing the event struct | ||
326 | * @param success | ||
327 | * @param emgs | ||
328 | */ | ||
329 | static void | ||
330 | remove_did_document_namestore_cb (void *cls, int32_t success, const char *emgs) | ||
331 | { | ||
332 | struct Event *event; | ||
333 | |||
334 | if (success != GNUNET_SYSERR) | ||
335 | { | ||
336 | event = (struct Event *) cls; | ||
337 | |||
338 | if (event->cont != NULL) | ||
339 | { | ||
340 | event->cont (event->cls); | ||
341 | free (event); | ||
342 | } | ||
343 | else { | ||
344 | free (event); | ||
345 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
346 | ret = 0; | ||
347 | return; | ||
348 | } | ||
349 | } else { | ||
350 | printf ("Something went wrong when deleting the DID Document\n"); | ||
351 | |||
352 | if (emgs != NULL) | ||
353 | { | ||
354 | printf ("%s\n", emgs); | ||
355 | } | ||
356 | |||
357 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
358 | ret = 0; | ||
359 | return; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * @brief Callback called after the ego has been locked up | ||
365 | * | ||
366 | * @param cls closure | ||
367 | * @param ego the ego returned by the identity service | ||
368 | */ | ||
369 | static void | ||
370 | remove_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
371 | { | ||
372 | const struct GNUNET_IDENTITY_PrivateKey *skey = | ||
373 | GNUNET_IDENTITY_ego_get_private_key (ego); | ||
374 | |||
375 | GNUNET_NAMESTORE_records_store (namestore_handle, | ||
376 | skey, | ||
377 | GNUNET_GNS_EMPTY_LABEL_AT, | ||
378 | 0, | ||
379 | NULL, | ||
380 | &remove_did_document_namestore_cb, | ||
381 | cls); | ||
382 | } | ||
383 | |||
384 | /** | ||
385 | * @brief Remove a DID Document | ||
386 | */ | ||
387 | static void | ||
388 | remove_did_document (remove_did_document_callback cont, void *cls) | ||
389 | { | ||
390 | struct Event *event; | ||
391 | |||
392 | if (egoname == NULL) | ||
393 | { | ||
394 | printf ("Remove requieres an ego option\n"); | ||
395 | GNUNET_SCHEDULER_add_now (cleanup, NULL); | ||
396 | ret = 1; | ||
397 | return; | ||
398 | } | ||
399 | else { | ||
400 | event = malloc (sizeof(*event)); | ||
401 | event->cont = cont; | ||
402 | event->cls = cls; | ||
403 | |||
404 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
405 | egoname, | ||
406 | &remove_did_document_ego_lookup_cb, | ||
407 | (void *) event); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | |||
412 | /** | ||
413 | * @brief Create a did generate did object | ||
414 | * | ||
415 | * @param pkey | ||
416 | * @return void* Return pointer to the DID Document | ||
417 | */ | ||
418 | char * | ||
419 | create_did_generate (struct GNUNET_IDENTITY_PublicKey pkey) | ||
420 | { | ||
421 | /* FIXME-MSC: I would prefer constants instead of magic numbers */ | ||
422 | char *pkey_str; // Convert public key to string | ||
423 | char did_str[71]; // 58 + 12 + 1 = 71 | ||
424 | char *didd_str; | ||
425 | char verify_id_str[77]; // did_str len + "#key-1" = 71 + 6 = 77 | ||
426 | char *pkey_multibase_str; | ||
427 | |||
428 | /* FIXME-MSC: This screams for a GNUNET_DID_identity_key_to_string() */ | ||
429 | char *b64; | ||
430 | char pkx[34]; | ||
431 | pkx[0] = 0xed; | ||
432 | pkx[1] = 0x01; | ||
433 | memcpy (pkx + 2, &(pkey.eddsa_key), sizeof(pkey.eddsa_key)); | ||
434 | GNUNET_STRINGS_base64_encode (pkx, sizeof(pkx), &b64); | ||
435 | |||
436 | GNUNET_asprintf (&pkey_multibase_str, "u%s", b64); | ||
437 | |||
438 | json_t *didd; | ||
439 | json_t *did_json; | ||
440 | json_t *pkey_multibase_json; | ||
441 | json_t *context_json; | ||
442 | json_t *context_1_json; | ||
443 | json_t *context_2_json; | ||
444 | json_t *verify_json; | ||
445 | json_t *verify_1_json; | ||
446 | json_t *verify_1_type_json; | ||
447 | json_t *verify_1_id_json; | ||
448 | json_t *verify_relative_ref_json; | ||
449 | json_t *auth_json; | ||
450 | json_t *assert_json; | ||
451 | |||
452 | /* FIXME-MSC: This screams for GNUNET_DID_identity_to_did() */ | ||
453 | pkey_str = GNUNET_IDENTITY_public_key_to_string (&pkey); // Convert public key to string | ||
454 | sprintf (did_str, "did:reclaim:%s", pkey_str); // Convert the public key to a DID str | ||
455 | sprintf (verify_id_str, "did:reclaim:%s#key-1", pkey_str); // Convert the public key to a DID str | ||
456 | |||
457 | // sprintf(pkey_multibase_str, "V%s", pkey_str); // Convert the public key to MultiBase data format | ||
458 | |||
459 | /* FIXME-MSC: This is effectively creating a DID Document default template for | ||
460 | * the initial document. | ||
461 | * Maybe this can be refactored to generate such a template for an identity? | ||
462 | * Even if higher layers add/modify it, there should probably still be a | ||
463 | * GNUNET_DID_document_template_from_identity() | ||
464 | */ | ||
465 | // Create Json Strings | ||
466 | did_json = json_string (did_str); | ||
467 | pkey_multibase_json = json_string (pkey_multibase_str); | ||
468 | |||
469 | context_1_json = json_string ("https://www.w3.org/ns/did/v1"); | ||
470 | context_2_json = json_string ( | ||
471 | "https://w3id.org/security/suites/ed25519-2020/v1"); | ||
472 | verify_1_id_json = json_string (verify_id_str); | ||
473 | verify_1_type_json = json_string ("Ed25519VerificationKey2020"); | ||
474 | |||
475 | // Add a relative DID URL to reference a verifiation method | ||
476 | // https://www.w3.org/TR/did-core/#relative-did-urls` | ||
477 | verify_relative_ref_json = json_string ("#key-1"); | ||
478 | |||
479 | // Create DID Document | ||
480 | didd = json_object (); | ||
481 | |||
482 | // Add context | ||
483 | context_json = json_array (); | ||
484 | json_array_append (context_json, context_1_json); | ||
485 | json_array_append (context_json, context_2_json); | ||
486 | json_object_set (didd, "@context", context_json); | ||
487 | |||
488 | // Add id | ||
489 | json_object_set (didd, "id", did_json); | ||
490 | |||
491 | // Add verification method | ||
492 | verify_json = json_array (); | ||
493 | verify_1_json = json_object (); | ||
494 | json_object_set (verify_1_json, "id", verify_1_id_json); | ||
495 | json_object_set (verify_1_json, "type", verify_1_type_json); | ||
496 | json_object_set (verify_1_json, "controller", did_json); | ||
497 | json_object_set (verify_1_json, "publicKeyMultiBase", pkey_multibase_json); | ||
498 | json_array_append (verify_json, verify_1_json); | ||
499 | json_object_set (didd, "verificationMethod", verify_json); | ||
500 | |||
501 | // Add authentication method | ||
502 | auth_json = json_array (); | ||
503 | json_array_append (auth_json, verify_relative_ref_json); | ||
504 | json_object_set (didd, "authentication", auth_json); | ||
505 | |||
506 | // Add assertion method to issue a Verifiable Credential | ||
507 | assert_json = json_array (); | ||
508 | json_array_append (assert_json, verify_relative_ref_json); | ||
509 | json_object_set (didd, "assertionMethod", assert_json); | ||
510 | |||
511 | // Encode DID Document as JSON string | ||
512 | didd_str = json_dumps (didd, JSON_INDENT (2)); | ||
513 | if (didd_str == NULL) | ||
514 | { | ||
515 | printf ("DID Document could not be encoded"); | ||
516 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
517 | ret = 1; | ||
518 | return NULL; | ||
519 | } | ||
520 | |||
521 | // TODO: MORE FREEEEEEEE | ||
522 | /* FIXME-MSC: json_t's are free'd using "json_decref". Also json_t usually | ||
523 | * keeps a reference counter. Check jansson docs for how to use it. | ||
524 | * Also: Use valgrind to find leaks. | ||
525 | */ | ||
526 | free (pkey_multibase_str); | ||
527 | free (b64); | ||
528 | |||
529 | free (didd); | ||
530 | free (did_json); | ||
531 | free (pkey_multibase_json); | ||
532 | free (context_json); | ||
533 | free (context_1_json); | ||
534 | free (context_2_json); | ||
535 | free (verify_json); | ||
536 | free (verify_1_json); | ||
537 | free (verify_1_type_json); | ||
538 | free (verify_1_id_json); | ||
539 | free (auth_json); | ||
540 | free (assert_json); | ||
541 | free (verify_relative_ref_json); | ||
542 | |||
543 | return didd_str; | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * @brief Create a DID. Store DID in Namestore cb | ||
548 | * | ||
549 | */ | ||
550 | static void | ||
551 | create_did_store_cb (void *cls, int32_t success, const char *emsg) | ||
552 | { | ||
553 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
554 | ret = 0; | ||
555 | return; | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * @brief Create a did. Store DID in Namestore | ||
560 | * | ||
561 | * @param didd_str String endoced DID Docuement | ||
562 | * @param ego Identity whos DID Document is stored | ||
563 | */ | ||
564 | static void | ||
565 | create_did_store (char *didd_str, struct GNUNET_IDENTITY_Ego *ego) | ||
566 | { | ||
567 | |||
568 | struct GNUNET_TIME_Relative expire_time; | ||
569 | struct GNUNET_GNSRECORD_Data record_data; | ||
570 | const struct GNUNET_IDENTITY_PrivateKey *skey; | ||
571 | |||
572 | if (GNUNET_STRINGS_fancy_time_to_relative ((NULL != expire) ? | ||
573 | expire : | ||
574 | GNUNET_DID_DEFAULT_DID_DOCUMENT_EXPIRATION_TIME, | ||
575 | &expire_time) == GNUNET_OK) | ||
576 | { | ||
577 | record_data.data = didd_str; | ||
578 | record_data.expiration_time = expire_time.rel_value_us; | ||
579 | record_data.data_size = strlen (didd_str) + 1; | ||
580 | record_data.record_type = GNUNET_GNSRECORD_typename_to_number ("TXT"), | ||
581 | record_data.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; | ||
582 | |||
583 | skey = GNUNET_IDENTITY_ego_get_private_key (ego); | ||
584 | |||
585 | GNUNET_NAMESTORE_records_store (namestore_handle, | ||
586 | skey, | ||
587 | GNUNET_GNS_EMPTY_LABEL_AT, | ||
588 | 1, //FIXME what if GNUNET_GNS_EMPTY_LABEL_AT has records | ||
589 | &record_data, | ||
590 | &create_did_store_cb, | ||
591 | NULL); | ||
592 | } | ||
593 | else { | ||
594 | printf ("Failed to read given expiration time\n"); | ||
595 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
596 | ret = 1; | ||
597 | return; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | /** | ||
602 | * @brief Create a did ego lockup cb | ||
603 | * | ||
604 | * @param cls | ||
605 | * @param ego | ||
606 | */ | ||
607 | static void | ||
608 | create_did_ego_lockup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
609 | { | ||
610 | struct GNUNET_IDENTITY_PublicKey pkey; | ||
611 | char *didd_str; | ||
612 | |||
613 | if (ego == NULL) | ||
614 | { | ||
615 | printf ("EGO not found\n"); | ||
616 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
617 | ret = 1; | ||
618 | return; | ||
619 | } | ||
620 | |||
621 | GNUNET_IDENTITY_ego_get_public_key (ego, &pkey); | ||
622 | |||
623 | if (ntohl (pkey.type) != GNUNET_GNSRECORD_TYPE_EDKEY) | ||
624 | { | ||
625 | printf ("The EGO has to have an EDDSA key pair\n"); | ||
626 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
627 | ret = 1; | ||
628 | return; | ||
629 | } | ||
630 | |||
631 | if (didd != NULL) | ||
632 | { | ||
633 | printf ( | ||
634 | "DID Docuement is read from \"did-document\" argument (EXPERIMENTAL)\n"); | ||
635 | didd_str = strdup (didd); | ||
636 | } | ||
637 | else { | ||
638 | // Generate DID Docuement from public key | ||
639 | didd_str = create_did_generate (pkey); | ||
640 | } | ||
641 | |||
642 | // Print DID Document to stdout | ||
643 | printf ("%s\n", didd_str); | ||
644 | |||
645 | // Store the DID Document | ||
646 | create_did_store (didd_str, ego); | ||
647 | |||
648 | // Save DID Document String to GNS | ||
649 | free (didd_str); | ||
650 | } | ||
651 | |||
652 | /** | ||
653 | * @brief Create a did document - Create a new identity first | ||
654 | */ | ||
655 | static void | ||
656 | create_did_document_ego_create_cb (void *cls, | ||
657 | const struct GNUNET_IDENTITY_PrivateKey *pk, | ||
658 | const char *emsg) | ||
659 | { | ||
660 | |||
661 | if (emsg != NULL) | ||
662 | { | ||
663 | printf ("%s\n", emsg); | ||
664 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
665 | ret = 1; | ||
666 | return; | ||
667 | } | ||
668 | |||
669 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
670 | egoname, | ||
671 | &create_did_ego_lockup_cb, | ||
672 | NULL); | ||
673 | } | ||
674 | |||
675 | /** | ||
676 | * @brief Create a did document | ||
677 | * | ||
678 | */ | ||
679 | static void | ||
680 | create_did_document () | ||
681 | { | ||
682 | if ((egoname != NULL) && (expire != NULL)) | ||
683 | { | ||
684 | GNUNET_IDENTITY_create (identity_handle, | ||
685 | egoname, | ||
686 | NULL, | ||
687 | GNUNET_IDENTITY_TYPE_EDDSA, | ||
688 | &create_did_document_ego_create_cb, | ||
689 | egoname); | ||
690 | } | ||
691 | else { | ||
692 | printf ( | ||
693 | "Set the EGO and the Expiration-time argument to create a new DID(-Document)\n"); | ||
694 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
695 | ret = 1; | ||
696 | return; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | |||
701 | /** | ||
702 | * @brief Replace a DID Docuemnt. Callback function after ego lockup | ||
703 | * | ||
704 | * @param cls | ||
705 | * @param ego | ||
706 | */ | ||
707 | static void | ||
708 | replace_did_document_ego_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) | ||
709 | { | ||
710 | create_did_store (didd, ego); | ||
711 | } | ||
712 | |||
713 | /** | ||
714 | * @brief Replace a DID Document. Callback functiona after remove | ||
715 | * | ||
716 | * @param cls | ||
717 | */ | ||
718 | static void | ||
719 | replace_did_document_remove_cb (void *cls) | ||
720 | { | ||
721 | GNUNET_IDENTITY_ego_lookup (my_cfg, | ||
722 | egoname, | ||
723 | &replace_did_document_ego_lookup_cb, | ||
724 | NULL); | ||
725 | } | ||
726 | |||
727 | /** | ||
728 | * @brief Replace a DID Docuemnt | ||
729 | * | ||
730 | */ | ||
731 | static void | ||
732 | replace_did_document () | ||
733 | { | ||
734 | if ((didd != NULL) && (expire != NULL)) | ||
735 | { | ||
736 | remove_did_document (&replace_did_document_remove_cb, NULL); | ||
737 | } | ||
738 | else { | ||
739 | printf ( | ||
740 | "Set the DID Document and expiration time argument to replace the DID Document\n"); | ||
741 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
742 | ret = 1; | ||
743 | return; | ||
744 | } | ||
745 | } | ||
746 | |||
747 | static void | ||
748 | post_ego_iteration (void *cls) | ||
749 | { | ||
750 | if (1 == replace) | ||
751 | { | ||
752 | replace_did_document (); | ||
753 | } | ||
754 | else if (1 == get) | ||
755 | { | ||
756 | resolve_did_document (); | ||
757 | } | ||
758 | else if (1 == remove_did) | ||
759 | { | ||
760 | remove_did_document (NULL, NULL); | ||
761 | } | ||
762 | else if (1 == create) | ||
763 | { | ||
764 | create_did_document (); | ||
765 | } | ||
766 | else { | ||
767 | // No Argument found | ||
768 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
769 | return; | ||
770 | } | ||
771 | } | ||
772 | |||
773 | static void | ||
774 | process_dids (void *cls, struct GNUNET_IDENTITY_Ego *ego, | ||
775 | void **ctx, const char*name) | ||
776 | { | ||
777 | char *did_str; | ||
778 | |||
779 | if (ego == NULL) | ||
780 | { | ||
781 | if (1 == ego_exists) | ||
782 | { | ||
783 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
784 | return; | ||
785 | } | ||
786 | GNUNET_SCHEDULER_add_now (&post_ego_iteration, NULL); | ||
787 | return; | ||
788 | } | ||
789 | if (NULL == name) | ||
790 | return; | ||
791 | if ((1 == create) && | ||
792 | (0 == strncmp (name, egoname, strlen (egoname))) && | ||
793 | (1 != ego_exists)) | ||
794 | { | ||
795 | fprintf(stderr, "%s already exists!\n", egoname); | ||
796 | ego_exists = 1; | ||
797 | return; | ||
798 | } | ||
799 | if (1 == show_all) | ||
800 | { | ||
801 | did_str = ego_to_did (ego); | ||
802 | printf ("%s\n", did_str); | ||
803 | GNUNET_free (did_str); | ||
804 | return; | ||
805 | } | ||
806 | if (1 == show) | ||
807 | { | ||
808 | if (0 == strncmp (name, egoname, strlen (egoname))) | ||
809 | { | ||
810 | did_str = ego_to_did (ego); | ||
811 | printf ("%s\n", did_str); | ||
812 | GNUNET_free (did_str); | ||
813 | return; | ||
814 | } | ||
815 | } | ||
816 | } | ||
817 | |||
818 | |||
819 | |||
820 | static void | ||
821 | run (void *cls, | ||
822 | char *const *args, | ||
823 | const char *cfgfile, | ||
824 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
825 | { | ||
826 | gns_handle = GNUNET_GNS_connect (c); | ||
827 | namestore_handle = GNUNET_NAMESTORE_connect (c); | ||
828 | my_cfg = c; | ||
829 | |||
830 | // check if GNS_handle could connect | ||
831 | if (gns_handle == NULL) | ||
832 | { | ||
833 | ret = 1; | ||
834 | return; | ||
835 | } | ||
836 | |||
837 | // check if NAMESTORE_handle could connect | ||
838 | if (namestore_handle == NULL) | ||
839 | { | ||
840 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
841 | ret = 1; | ||
842 | return; | ||
843 | } | ||
844 | |||
845 | identity_handle = GNUNET_IDENTITY_connect (c, &process_dids, NULL); | ||
846 | if (identity_handle == NULL) | ||
847 | { | ||
848 | GNUNET_SCHEDULER_add_now (&cleanup, NULL); | ||
849 | ret = 1; | ||
850 | return; | ||
851 | } | ||
852 | } | ||
853 | |||
854 | int | ||
855 | main (int argc, char *const argv[]) | ||
856 | { | ||
857 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
858 | GNUNET_GETOPT_option_flag ('C', | ||
859 | "create", | ||
860 | gettext_noop ( | ||
861 | "Create a DID Document and display its DID"), | ||
862 | &create), | ||
863 | GNUNET_GETOPT_option_flag ('g', | ||
864 | "get", | ||
865 | gettext_noop ( | ||
866 | "Get the DID Document associated with the given DID"), | ||
867 | &get), | ||
868 | GNUNET_GETOPT_option_flag ('s', | ||
869 | "show", | ||
870 | gettext_noop ("Show the DID for a given ego"), | ||
871 | &show), | ||
872 | GNUNET_GETOPT_option_flag ('r', | ||
873 | "remove", | ||
874 | gettext_noop ( | ||
875 | "Remove the DID"), | ||
876 | &remove_did), | ||
877 | GNUNET_GETOPT_option_flag ('R', | ||
878 | "replace", | ||
879 | gettext_noop ("Replace the DID Document."), | ||
880 | &replace), | ||
881 | GNUNET_GETOPT_option_flag ('A', | ||
882 | "--show-all", | ||
883 | gettext_noop ("Replace the DID Document."), | ||
884 | &show_all), | ||
885 | GNUNET_GETOPT_option_string ('d', | ||
886 | "did", | ||
887 | "DID", | ||
888 | gettext_noop ( | ||
889 | "The Decentralized Identity (DID)"), | ||
890 | &did), | ||
891 | GNUNET_GETOPT_option_string ('D', | ||
892 | "--did-document", | ||
893 | "JSON", | ||
894 | gettext_noop ( | ||
895 | "The DID Document to store in GNUNET"), | ||
896 | &didd), | ||
897 | GNUNET_GETOPT_option_string ('e', | ||
898 | "ego", | ||
899 | "EGO", | ||
900 | gettext_noop ("The name of the EGO"), | ||
901 | &egoname), | ||
902 | GNUNET_GETOPT_option_string ('t', | ||
903 | "expiration-time", | ||
904 | "TIME", | ||
905 | gettext_noop ( | ||
906 | "The time until the DID Document is going to expire (e.g. 5d)"), | ||
907 | &expire), | ||
908 | GNUNET_GETOPT_OPTION_END | ||
909 | }; | ||
910 | |||
911 | if (GNUNET_OK != GNUNET_PROGRAM_run (argc, | ||
912 | argv, | ||
913 | "gnunet-did", | ||
914 | _ ( | ||
915 | "Manage Decentralized Identities (DIDs)"), | ||
916 | options, | ||
917 | &run, | ||
918 | NULL)) | ||
919 | return 1; | ||
920 | else | ||
921 | return ret; | ||
922 | } | ||
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 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012-2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | |||
22 | /** | ||
23 | * @file src/did/test_w3c_ed25519_2020.c | ||
24 | * @brief Testcases for the w3c Ed25519 formats for SSIs https://w3c-ccg.github.io/lds-ed25519-2020 | ||
25 | * @author Martin Schanzenbach | ||
26 | */ | ||
27 | |||
28 | #include "platform.h" | ||
29 | #include "gnunet_crypto_lib.h" | ||
30 | #include "gnunet_strings_lib.h" | ||
31 | |||
32 | static char test_privkey[32] = { | ||
33 | 0x9b, 0x93, 0x7b, 0x81, 0x32, 0x2d, 0x81, 0x6c, | ||
34 | 0xfa, 0xb9, 0xd5, 0xa3, 0xba, 0xac, 0xc9, 0xb2, | ||
35 | 0xa5, 0xfe, 0xbe, 0x4b, 0x14, 0x9f, 0x12, 0x6b, | ||
36 | 0x36, 0x30, 0xf9, 0x3a, 0x29, 0x52, 0x70, 0x17 | ||
37 | }; | ||
38 | |||
39 | static char *targetPublicKeyMultibase = "u7QEJX5oaWV3edV2CeGhkrQPfpaT71ogyVmNk4rZeE8yeRA"; | ||
40 | |||
41 | int | ||
42 | main () | ||
43 | { | ||
44 | struct GNUNET_CRYPTO_EddsaPrivateKey privkey; | ||
45 | struct GNUNET_CRYPTO_EddsaPublicKey pubkey; | ||
46 | |||
47 | memcpy (&privkey, test_privkey, sizeof (privkey)); | ||
48 | GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey); | ||
49 | |||
50 | //This is how to convert out pubkeys to W3c Ed25519-2020 multibase (base64url no padding) | ||
51 | char *b64; | ||
52 | char pkx[34]; | ||
53 | pkx[0] = 0xed; | ||
54 | pkx[1] = 0x01; | ||
55 | memcpy (pkx+2, &pubkey, sizeof (pubkey)); | ||
56 | GNUNET_STRINGS_base64url_encode (pkx, | ||
57 | sizeof (pkx), | ||
58 | &b64); | ||
59 | printf ("u%s\n%s\n", b64, targetPublicKeyMultibase); | ||
60 | // FIXME convert pubkey to target | ||
61 | char *res; | ||
62 | GNUNET_asprintf (&res, "u%s", b64); | ||
63 | GNUNET_assert (0 == strcmp (res, | ||
64 | targetPublicKeyMultibase)); | ||
65 | |||
66 | GNUNET_free (b64); | ||
67 | GNUNET_free (res); | ||
68 | return 0; | ||
69 | } | ||