/*
This file is part of GNUnet.
Copyright (C) 2012-2013 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 gnunet-credential.c
* @brief command line tool to access command line Credential service
* @author Martin Schanzenbach
*/
#include "platform.h"
#include
#include
#include
#include "credential_misc.h"
#include "credential_serialization.h"
/**
* Configuration we are using.
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* EgoLookup
*/
static struct GNUNET_IDENTITY_EgoLookup *el;
/**
* Handle to Credential service.
*/
static struct GNUNET_CREDENTIAL_Handle *credential;
/**
* Desired timeout for the lookup (default is no timeout).
*/
static struct GNUNET_TIME_Relative timeout;
/**
* Handle to verify request
*/
static struct GNUNET_CREDENTIAL_Request *verify_request;
/**
* Handle to collect request
*/
static struct GNUNET_CREDENTIAL_Request *collect_request;
/**
* Task scheduled to handle timeout.
*/
static struct GNUNET_SCHEDULER_Task *tt;
/**
* Subject pubkey string
*/
static char *subject_key;
/**
* Subject credential string
*/
static char *subject_credential;
/**
* Credential TTL
*/
static char *expiration;
/**
* Subject key
*/
struct GNUNET_CRYPTO_EcdsaPublicKey subject_pkey;
/**
* Issuer key
*/
struct GNUNET_CRYPTO_EcdsaPublicKey issuer_pkey;
/**
* Issuer pubkey string
*/
static char *issuer_key;
/**
* ego
*/
static char *ego_name;
/**
* Issuer attribute
*/
static char *issuer_attr;
/**
* Verify mode
*/
static int verify;
/**
* Issue mode
*/
static int create_cred;
/**
* Collect mode
*/
static int collect;
/**
* Task run on shutdown. Cleans up everything.
*
* @param cls unused
*/
static void
do_shutdown(void *cls)
{
if (NULL != verify_request)
{
GNUNET_CREDENTIAL_request_cancel(verify_request);
verify_request = NULL;
}
if (NULL != credential)
{
GNUNET_CREDENTIAL_disconnect(credential);
credential = NULL;
}
if (NULL != tt)
{
GNUNET_SCHEDULER_cancel(tt);
tt = NULL;
}
}
/**
* Task run on timeout. Triggers shutdown.
*
* @param cls unused
*/
static void
do_timeout(void *cls)
{
tt = NULL;
GNUNET_SCHEDULER_shutdown();
}
static void
handle_collect_result(void *cls,
unsigned int d_count,
struct GNUNET_CREDENTIAL_Delegation *dc,
unsigned int c_count,
struct GNUNET_CREDENTIAL_Credential *cred)
{
int i;
char* line;
verify_request = NULL;
if (NULL != cred)
{
for (i = 0; i < c_count; i++)
{
line = GNUNET_CREDENTIAL_credential_to_string(&cred[i]);
printf("%s\n",
line);
GNUNET_free(line);
}
}
GNUNET_SCHEDULER_shutdown();
}
static void
handle_verify_result(void *cls,
unsigned int d_count,
struct GNUNET_CREDENTIAL_Delegation *dc,
unsigned int c_count,
struct GNUNET_CREDENTIAL_Credential *cred)
{
int i;
char* iss_key;
char* sub_key;
verify_request = NULL;
if (NULL == cred)
printf("Failed.\n");
else
{
printf("Delegation Chain:\n");
for (i = 0; i < d_count; i++)
{
iss_key = GNUNET_CRYPTO_ecdsa_public_key_to_string(&dc[i].issuer_key);
sub_key = GNUNET_CRYPTO_ecdsa_public_key_to_string(&dc[i].subject_key);
if (0 != dc[i].subject_attribute_len)
{
printf("(%d) %s.%s <- %s.%s\n", i,
iss_key, dc[i].issuer_attribute,
sub_key, dc[i].subject_attribute);
}
else
{
printf("(%d) %s.%s <- %s\n", i,
iss_key, dc[i].issuer_attribute,
sub_key);
}
GNUNET_free(iss_key);
GNUNET_free(sub_key);
}
printf("\nCredentials:\n");
for (i = 0; i < c_count; i++)
{
iss_key = GNUNET_CRYPTO_ecdsa_public_key_to_string(&cred[i].issuer_key);
sub_key = GNUNET_CRYPTO_ecdsa_public_key_to_string(&cred[i].subject_key);
printf("%s.%s <- %s\n",
iss_key, cred[i].issuer_attribute,
sub_key);
GNUNET_free(iss_key);
GNUNET_free(sub_key);
}
printf("Successful.\n");
}
GNUNET_SCHEDULER_shutdown();
}
/**
* Callback invoked from identity service with ego information.
* An @a ego of NULL means the ego was not found.
*
* @param cls closure with the configuration
* @param ego an ego known to identity service, or NULL
*/
static void
identity_cb(void *cls,
const struct GNUNET_IDENTITY_Ego *ego)
{
const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
struct GNUNET_CREDENTIAL_Credential *crd;
struct GNUNET_TIME_Absolute etime_abs;
struct GNUNET_TIME_Relative etime_rel;
char *res;
el = NULL;
if (NULL == ego)
{
if (NULL != ego_name)
{
fprintf(stderr,
_("Ego `%s' not known to identity service\n"),
ego_name);
}
GNUNET_SCHEDULER_shutdown();
return;
}
if (GNUNET_YES == collect)
{
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string(issuer_key,
strlen(issuer_key),
&issuer_pkey))
{
fprintf(stderr,
_("Issuer public key `%s' is not well-formed\n"),
issuer_key);
GNUNET_SCHEDULER_shutdown();
}
privkey = GNUNET_IDENTITY_ego_get_private_key(ego);
collect_request = GNUNET_CREDENTIAL_collect(credential,
&issuer_pkey,
issuer_attr, //TODO argument
privkey,
&handle_collect_result,
NULL);
return;
}
//Else issue
if (NULL == expiration)
{
fprintf(stderr,
"Please specify a TTL\n");
GNUNET_SCHEDULER_shutdown();
return;
}
else if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative(expiration,
&etime_rel))
{
etime_abs = GNUNET_TIME_relative_to_absolute(etime_rel);
}
else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute(expiration,
&etime_abs))
{
fprintf(stderr,
"%s is not a valid ttl!\n",
expiration);
GNUNET_SCHEDULER_shutdown();
return;
}
privkey = GNUNET_IDENTITY_ego_get_private_key(ego);
GNUNET_free_non_null(ego_name);
ego_name = NULL;
crd = GNUNET_CREDENTIAL_credential_issue(privkey,
&subject_pkey,
issuer_attr,
&etime_abs);
res = GNUNET_CREDENTIAL_credential_to_string(crd);
GNUNET_free(crd);
printf("%s\n", res);
GNUNET_SCHEDULER_shutdown();
}
/**
* Main function that will be run.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
* @param c configuration
*/
static void
run(void *cls,
char *const *args,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
cfg = c;
tt = GNUNET_SCHEDULER_add_delayed(timeout,
&do_timeout, NULL);
GNUNET_SCHEDULER_add_shutdown(&do_shutdown, NULL);
if (GNUNET_YES == collect)
{
if (NULL == issuer_key)
{
fprintf(stderr,
_("Issuer public key not well-formed\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
credential = GNUNET_CREDENTIAL_connect(cfg);
if (NULL == credential)
{
fprintf(stderr,
_("Failed to connect to CREDENTIAL\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
if (NULL == issuer_attr)
{
fprintf(stderr,
_("You must provide issuer the attribute\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
if (NULL == ego_name)
{
fprintf(stderr,
_("ego required\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
el = GNUNET_IDENTITY_ego_lookup(cfg,
ego_name,
&identity_cb,
(void *)cfg);
return;
}
if (NULL == subject_key)
{
fprintf(stderr,
_("Subject public key needed\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string(subject_key,
strlen(subject_key),
&subject_pkey))
{
fprintf(stderr,
_("Subject public key `%s' is not well-formed\n"),
subject_key);
GNUNET_SCHEDULER_shutdown();
return;
}
if (GNUNET_YES == verify)
{
if (NULL == issuer_key)
{
fprintf(stderr,
_("Issuer public key not well-formed\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string(issuer_key,
strlen(issuer_key),
&issuer_pkey))
{
fprintf(stderr,
_("Issuer public key `%s' is not well-formed\n"),
issuer_key);
GNUNET_SCHEDULER_shutdown();
return;
}
credential = GNUNET_CREDENTIAL_connect(cfg);
if (NULL == credential)
{
fprintf(stderr,
_("Failed to connect to CREDENTIAL\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
if (NULL == issuer_attr || NULL == subject_credential)
{
fprintf(stderr,
_("You must provide issuer and subject attributes\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
//Subject credentials are comma separated
char *tmp = GNUNET_strdup(subject_credential);
char *tok = strtok(tmp, ",");
if (NULL == tok)
{
fprintf(stderr,
"Invalid subject credentials\n");
GNUNET_free(tmp);
GNUNET_SCHEDULER_shutdown();
return;
}
int count = 1;
int i;
while (NULL != (tok = strtok(NULL, ",")))
count++;
struct GNUNET_CREDENTIAL_Credential credentials[count];
struct GNUNET_CREDENTIAL_Credential *cred;
GNUNET_free(tmp);
tmp = GNUNET_strdup(subject_credential);
tok = strtok(tmp, ",");
for (i = 0; i < count; i++)
{
cred = GNUNET_CREDENTIAL_credential_from_string(tok);
GNUNET_memcpy(&credentials[i],
cred,
sizeof(struct GNUNET_CREDENTIAL_Credential));
credentials[i].issuer_attribute = GNUNET_strdup(cred->issuer_attribute);
tok = strtok(NULL, ",");
GNUNET_free(cred);
}
verify_request = GNUNET_CREDENTIAL_verify(credential,
&issuer_pkey,
issuer_attr, //TODO argument
&subject_pkey,
count,
credentials,
&handle_verify_result,
NULL);
for (i = 0; i < count; i++)
{
GNUNET_free((char*)credentials[i].issuer_attribute);
}
GNUNET_free(tmp);
}
else if (GNUNET_YES == create_cred)
{
if (NULL == ego_name)
{
fprintf(stderr,
_("Issuer ego required\n"));
GNUNET_SCHEDULER_shutdown();
return;
}
el = GNUNET_IDENTITY_ego_lookup(cfg,
ego_name,
&identity_cb,
(void *)cfg);
return;
}
else
{
fprintf(stderr,
_("Please specify name to lookup, subject key and issuer key!\n"));
GNUNET_SCHEDULER_shutdown();
}
return;
}
/**
* The main function for gnunet-gns.
*
* @param argc number of arguments from the command line
* @param argv command line arguments
* @return 0 ok, 1 on error
*/
int
main(int argc, char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_flag('I',
"issue",
gettext_noop("create credential"),
&create_cred),
GNUNET_GETOPT_option_flag('V',
"verify",
gettext_noop("verify credential against attribute"),
&verify),
GNUNET_GETOPT_option_string('s',
"subject",
"PKEY",
gettext_noop("The public key of the subject to lookup the credential for"),
&subject_key),
GNUNET_GETOPT_option_string('b',
"credential",
"CRED",
gettext_noop("The name of the credential presented by the subject"),
&subject_credential),
GNUNET_GETOPT_option_string('i',
"issuer",
"PKEY",
gettext_noop("The public key of the authority to verify the credential against"),
&issuer_key),
GNUNET_GETOPT_option_string('e',
"ego",
"EGO",
gettext_noop("The ego to use"),
&ego_name),
GNUNET_GETOPT_option_string('a',
"attribute",
"ATTR",
gettext_noop("The issuer attribute to verify against or to issue"),
&issuer_attr),
GNUNET_GETOPT_option_string('T',
"ttl",
"EXP",
gettext_noop("The time to live for the credential"),
&expiration),
GNUNET_GETOPT_option_flag('g',
"collect",
gettext_noop("collect credentials"),
&collect),
GNUNET_GETOPT_OPTION_END
};
int ret;
timeout = GNUNET_TIME_UNIT_FOREVER_REL;
if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args(argc, argv, &argc, &argv))
return 2;
GNUNET_log_setup("gnunet-credential", "WARNING", NULL);
ret =
(GNUNET_OK ==
GNUNET_PROGRAM_run(argc, argv, "gnunet-credential",
_("GNUnet credential resolver tool"),
options,
&run, NULL)) ? 0 : 1;
GNUNET_free((void*)argv);
return ret;
}
/* end of gnunet-credential.c */