Merge remote-tracking branch 'origin/master' into identity_abe
[oweals/gnunet.git] / src / credential / gnunet-credential.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2013 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file gnunet-credential.c
22  * @brief command line tool to access command line Credential service
23  * @author Adnan Husain
24  */
25 #include "platform.h"
26 #include <gnunet_util_lib.h>
27 #include <gnunet_credential_service.h>
28 #include <gnunet_gnsrecord_lib.h>
29 #include "credential_misc.h"
30 #include "credential_serialization.h"
31
32 /**
33  * Configuration we are using.
34  */
35 static const struct GNUNET_CONFIGURATION_Handle *cfg;
36
37 /**
38  * EgoLookup
39  */
40 static struct GNUNET_IDENTITY_EgoLookup *el;
41
42 /**
43  * Handle to Credential service.
44  */
45 static struct GNUNET_CREDENTIAL_Handle *credential;
46
47 /**
48  * Desired timeout for the lookup (default is no timeout).
49  */
50 static struct GNUNET_TIME_Relative timeout;
51
52 /**
53  * Handle to verify request
54  */
55 static struct GNUNET_CREDENTIAL_Request *verify_request;
56
57 /**
58  * Handle to collect request
59  */
60 static struct GNUNET_CREDENTIAL_Request *collect_request;
61
62 /**
63  * Task scheduled to handle timeout.
64  */
65 static struct GNUNET_SCHEDULER_Task *tt;
66
67 /**
68  * Subject pubkey string
69  */
70 static char *subject_key;
71
72 /**
73  * Subject credential string
74  */
75 static char *subject_credential;
76
77 /**
78  * Credential TTL
79  */
80 static char *expiration;
81
82 /**
83  * Subject key
84  */
85 struct GNUNET_CRYPTO_EcdsaPublicKey subject_pkey;
86
87 /**
88  * Issuer key
89  */
90 struct GNUNET_CRYPTO_EcdsaPublicKey issuer_pkey;
91
92
93 /**
94  * Issuer pubkey string
95  */
96 static char *issuer_key;
97
98 /**
99  * ego
100  */
101 static char *ego_name;
102
103 /**
104  * Issuer attribute
105  */
106 static char *issuer_attr;
107
108 /**
109  * Verify mode
110  */
111 static int verify;
112
113 /**
114  * Issue mode
115  */
116 static int create_cred;
117
118 /**
119  * Collect mode
120  */
121 static int collect;
122
123 /**
124  * Task run on shutdown.  Cleans up everything.
125  *
126  * @param cls unused
127  */
128 static void
129 do_shutdown (void *cls)
130 {
131   if (NULL != verify_request)
132   {
133     GNUNET_CREDENTIAL_request_cancel (verify_request);
134     verify_request = NULL;
135   }
136   if (NULL != credential)
137   {
138     GNUNET_CREDENTIAL_disconnect (credential);
139     credential = NULL;
140   }
141   if (NULL != tt)
142   {
143     GNUNET_SCHEDULER_cancel (tt);
144     tt = NULL;
145   }
146 }
147
148
149 /**
150  * Task run on timeout. Triggers shutdown.
151  *
152  * @param cls unused
153  */
154 static void
155 do_timeout (void *cls)
156 {
157   tt = NULL;
158   GNUNET_SCHEDULER_shutdown ();
159 }
160
161 /**
162  * Function called with the result of a Credential lookup.
163  *
164  * @param cls the 'const char *' name that was resolved
165  * @param cd_count number of records returned
166  * @param cd array of @a cd_count records with the results
167  */
168 static void
169 handle_collect_result (void *cls,
170                       unsigned int d_count,
171                       struct GNUNET_CREDENTIAL_Delegation *dc,
172                       unsigned int c_count,
173                       struct GNUNET_CREDENTIAL_Credential *cred)
174 {
175   int i;
176   char* line;
177
178   verify_request = NULL;
179   if (NULL != cred)
180   {
181     for (i=0;i<c_count;i++)
182     {
183       line = GNUNET_CREDENTIAL_credential_to_string (&cred[i]);
184       printf ("%s\n",
185               line);
186       GNUNET_free (line);
187     }
188   }
189
190
191   GNUNET_SCHEDULER_shutdown ();
192 }
193
194
195 /**
196  * Function called with the result of a Credential lookup.
197  *
198  * @param cls the 'const char *' name that was resolved
199  * @param cd_count number of records returned
200  * @param cd array of @a cd_count records with the results
201  */
202 static void
203 handle_verify_result (void *cls,
204                       unsigned int d_count,
205                       struct GNUNET_CREDENTIAL_Delegation *dc,
206                       unsigned int c_count,
207                       struct GNUNET_CREDENTIAL_Credential *cred)
208 {
209   int i;
210   char* iss_key;
211   char* sub_key;
212
213   verify_request = NULL;
214   if (NULL == cred)
215     printf ("Failed.\n");
216   else
217   {
218     printf("Delegation Chain:\n");
219     for (i=0;i<d_count;i++)
220     {
221       iss_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&dc[i].issuer_key);
222       sub_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&dc[i].subject_key);
223       if (0 != dc[i].subject_attribute_len)
224       {
225         printf ("(%d) %s.%s <- %s.%s\n", i,
226                 iss_key, dc[i].issuer_attribute,
227                 sub_key, dc[i].subject_attribute);
228       } else {
229         printf ("(%d) %s.%s <- %s\n", i,
230                 iss_key, dc[i].issuer_attribute,
231                 sub_key);
232       }
233       GNUNET_free (iss_key);
234       GNUNET_free (sub_key);
235     }
236     printf("\nCredentials:\n");
237     for (i=0;i<c_count;i++)
238     {
239       iss_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred[i].issuer_key);
240       sub_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred[i].subject_key);
241       printf ("%s.%s <- %s\n",
242               iss_key, cred[i].issuer_attribute,
243               sub_key);
244       GNUNET_free (iss_key);
245       GNUNET_free (sub_key);
246
247     }
248     printf ("Successful.\n");
249   }
250
251
252   GNUNET_SCHEDULER_shutdown ();
253 }
254
255 /**
256  * Callback invoked from identity service with ego information.
257  * An @a ego of NULL means the ego was not found.
258  *
259  * @param cls closure with the configuration
260  * @param ego an ego known to identity service, or NULL
261  */
262 static void
263 identity_cb (void *cls,
264              const struct GNUNET_IDENTITY_Ego *ego)
265 {
266   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
267   struct GNUNET_CREDENTIAL_Credential *crd;
268   struct GNUNET_TIME_Absolute etime_abs;
269   struct GNUNET_TIME_Relative etime_rel;
270   char *res;
271
272   el = NULL;
273   if (NULL == ego)
274   {
275     if (NULL != ego_name)
276     {
277       fprintf (stderr,
278                _("Ego `%s' not known to identity service\n"),
279                ego_name);
280     }
281     GNUNET_SCHEDULER_shutdown ();
282     return;
283   }
284
285   if (GNUNET_YES == collect)
286   {
287     
288     if (GNUNET_OK !=
289         GNUNET_CRYPTO_ecdsa_public_key_from_string (issuer_key,
290                                                     strlen (issuer_key),
291                                                     &issuer_pkey))
292     {
293       fprintf (stderr,
294                _("Issuer public key `%s' is not well-formed\n"),
295                issuer_key);
296       GNUNET_SCHEDULER_shutdown ();
297     }
298     privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
299
300     collect_request = GNUNET_CREDENTIAL_collect(credential,
301                                                 &issuer_pkey,
302                                                 issuer_attr, //TODO argument
303                                                 privkey,
304                                                 &handle_collect_result,
305                                                 NULL);
306     return;
307   }
308
309   //Else issue
310
311   if (NULL == expiration)
312   {
313     fprintf (stderr,
314              "Please specify a TTL\n");
315     GNUNET_SCHEDULER_shutdown ();
316     return;
317   } else if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative (expiration,
318                                                                  &etime_rel))
319   {
320     etime_abs = GNUNET_TIME_relative_to_absolute (etime_rel);
321   } else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (expiration,
322                                                                  &etime_abs))
323   {
324     fprintf (stderr,
325              "%s is not a valid ttl!\n",
326              expiration);
327     GNUNET_SCHEDULER_shutdown ();
328     return;
329   }
330
331
332   privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
333   GNUNET_free_non_null (ego_name);
334   ego_name = NULL;
335   crd = GNUNET_CREDENTIAL_credential_issue (privkey,
336                                             &subject_pkey,
337                                             issuer_attr,
338                                             &etime_abs);
339
340   res = GNUNET_CREDENTIAL_credential_to_string (crd);
341   GNUNET_free (crd);
342   printf ("%s\n", res);
343   GNUNET_SCHEDULER_shutdown ();
344 }
345
346
347
348
349 /**
350  * Main function that will be run.
351  *
352  * @param cls closure
353  * @param args remaining command-line arguments
354  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
355  * @param c configuration
356  */
357 static void
358 run (void *cls,
359      char *const *args,
360      const char *cfgfile,
361      const struct GNUNET_CONFIGURATION_Handle *c)
362 {
363
364   cfg = c;
365
366
367   tt = GNUNET_SCHEDULER_add_delayed (timeout,
368                                      &do_timeout, NULL);
369   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
370
371   if (GNUNET_YES == collect) {
372     if (NULL == issuer_key)
373     {
374       fprintf (stderr,
375                _("Issuer public key not well-formed\n"));
376       GNUNET_SCHEDULER_shutdown ();
377       return;
378
379     }
380
381     credential = GNUNET_CREDENTIAL_connect (cfg);
382
383     if (NULL == credential)
384     {
385       fprintf (stderr,
386                _("Failed to connect to CREDENTIAL\n"));
387       GNUNET_SCHEDULER_shutdown ();
388     }
389     if (NULL == issuer_attr)
390     {
391       fprintf (stderr,
392                _("You must provide issuer the attribute\n"));
393       GNUNET_SCHEDULER_shutdown ();
394     }
395
396     if (NULL == ego_name)
397     {
398       fprintf (stderr,
399                _("ego required\n"));
400       GNUNET_SCHEDULER_shutdown ();
401       return;
402
403     }
404     el = GNUNET_IDENTITY_ego_lookup (cfg,
405                                      ego_name,
406                                      &identity_cb,
407                                      (void *) cfg);
408     return;
409
410   } 
411
412   if (NULL == subject_key)
413   {
414     fprintf (stderr,
415              _("Subject public key needed\n"));
416     GNUNET_SCHEDULER_shutdown ();
417     return;
418
419   }
420   if (GNUNET_OK !=
421       GNUNET_CRYPTO_ecdsa_public_key_from_string (subject_key,
422                                                   strlen (subject_key),
423                                                   &subject_pkey))
424   {
425     fprintf (stderr,
426              _("Subject public key `%s' is not well-formed\n"),
427              subject_key);
428     GNUNET_SCHEDULER_shutdown ();
429     return;
430   }
431   if (GNUNET_YES == verify) {
432     if (NULL == issuer_key)
433     {
434       fprintf (stderr,
435                _("Issuer public key not well-formed\n"));
436       GNUNET_SCHEDULER_shutdown ();
437       return;
438
439     }
440     if (GNUNET_OK !=
441         GNUNET_CRYPTO_ecdsa_public_key_from_string (issuer_key,
442                                                     strlen (issuer_key),
443                                                     &issuer_pkey))
444     {
445       fprintf (stderr,
446                _("Issuer public key `%s' is not well-formed\n"),
447                issuer_key);
448       GNUNET_SCHEDULER_shutdown ();
449     }
450     credential = GNUNET_CREDENTIAL_connect (cfg);
451
452     if (NULL == credential)
453     {
454       fprintf (stderr,
455                _("Failed to connect to CREDENTIAL\n"));
456       GNUNET_SCHEDULER_shutdown ();
457     }
458     if (NULL == issuer_attr || NULL == subject_credential)
459     {
460       fprintf (stderr,
461                _("You must provide issuer and subject attributes\n"));
462       GNUNET_SCHEDULER_shutdown ();
463     }
464
465     //Subject credentials are comma separated
466     char *tmp = GNUNET_strdup (subject_credential);
467     char *tok = strtok (tmp, ",");
468     if (NULL == tok)
469     {
470       fprintf (stderr,
471                "Invalid subject credentials\n");
472       GNUNET_free (tmp);
473       GNUNET_SCHEDULER_shutdown ();
474     }
475     int count = 1;
476     int i;
477     while (NULL != (tok = strtok(NULL, ",")))
478       count++;
479     struct GNUNET_CREDENTIAL_Credential credentials[count];
480     struct GNUNET_CREDENTIAL_Credential *cred;
481     GNUNET_free (tmp);
482     tmp = GNUNET_strdup (subject_credential);
483     tok = strtok (tmp, ",");
484     for (i=0;i<count;i++)
485     {
486       cred = GNUNET_CREDENTIAL_credential_from_string (tok);
487       GNUNET_memcpy (&credentials[i],
488                      cred,
489                      sizeof (struct GNUNET_CREDENTIAL_Credential));
490       credentials[i].issuer_attribute = GNUNET_strdup (cred->issuer_attribute);
491       tok = strtok(NULL, ",");
492       GNUNET_free (cred);
493     }
494
495     verify_request = GNUNET_CREDENTIAL_verify(credential,
496                                               &issuer_pkey,
497                                               issuer_attr, //TODO argument
498                                               &subject_pkey,
499                                               count,
500                                               credentials,
501                                               &handle_verify_result,
502                                               NULL);
503     for (i=0;i<count;i++)
504     {
505       GNUNET_free ((char*)credentials[i].issuer_attribute);
506     }
507   } else if (GNUNET_YES == create_cred) {
508     if (NULL == ego_name)
509     {
510       fprintf (stderr,
511                _("Issuer ego required\n"));
512       GNUNET_SCHEDULER_shutdown ();
513       return;
514
515     }
516     el = GNUNET_IDENTITY_ego_lookup (cfg,
517                                      ego_name,
518                                      &identity_cb,
519                                      (void *) cfg);
520     return;
521   } else {
522     fprintf (stderr,
523              _("Please specify name to lookup, subject key and issuer key!\n"));
524     GNUNET_SCHEDULER_shutdown ();
525   }
526   return;
527 }
528
529
530 /**
531  * The main function for gnunet-gns.
532  *
533  * @param argc number of arguments from the command line
534  * @param argv command line arguments
535  * @return 0 ok, 1 on error
536  */
537 int
538 main (int argc, char *const *argv)
539 {
540   struct GNUNET_GETOPT_CommandLineOption options[] = {
541     GNUNET_GETOPT_option_flag ('I',
542                                "issue",
543                                gettext_noop ("create credential"),
544                                &create_cred),
545     GNUNET_GETOPT_option_flag ('V',
546                                "verify",
547                                gettext_noop ("verify credential against attribute"),
548                                &verify),
549     GNUNET_GETOPT_option_string ('s',
550                                  "subject",
551                                  "PKEY",
552                                  gettext_noop ("The public key of the subject to lookup the credential for"),
553                                  &subject_key),
554     GNUNET_GETOPT_option_string ('b',
555                                  "credential",
556                                  "CRED",
557                                  gettext_noop ("The name of the credential presented by the subject"),
558                                  &subject_credential),
559     GNUNET_GETOPT_option_string ('i',
560                                  "issuer",
561                                  "PKEY",
562                                  gettext_noop ("The public key of the authority to verify the credential against"),
563                                  &issuer_key),
564     GNUNET_GETOPT_option_string ('e',
565                                  "ego",
566                                  "EGO",
567                                  gettext_noop ("The ego to use"),
568                                  &ego_name),
569     GNUNET_GETOPT_option_string ('a',
570                                  "attribute",
571                                  "ATTR",
572                                  gettext_noop ("The issuer attribute to verify against or to issue"),
573                                  &issuer_attr),
574     GNUNET_GETOPT_option_string ('T',
575                                  "ttl",
576                                  "EXP",
577                                  gettext_noop ("The time to live for the credential"),
578                                  &expiration),
579     GNUNET_GETOPT_option_flag ('g',
580                                "collect",
581                                gettext_noop ("collect credentials"),
582                                &collect),
583     GNUNET_GETOPT_OPTION_END
584   };
585   int ret;
586
587   timeout = GNUNET_TIME_UNIT_FOREVER_REL;
588   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
589     return 2;
590
591   GNUNET_log_setup ("gnunet-credential", "WARNING", NULL);
592   ret =
593     (GNUNET_OK ==
594      GNUNET_PROGRAM_run (argc, argv, "gnunet-credential",
595                          _("GNUnet credential resolver tool"),
596                          options,
597                          &run, NULL)) ? 0 : 1;
598   GNUNET_free ((void*) argv);
599   return ret;
600 }
601
602 /* end of gnunet-credential.c */