diff options
author | Nico Thomas <nico.thomas@tum.de> | 2021-12-16 18:13:21 +0100 |
---|---|---|
committer | Martin Schanzenbach <schanzen@gnunet.org> | 2021-12-28 22:43:00 +0100 |
commit | 88fb1d89ed483576a7f02060cd72be7761b2be3a (patch) | |
tree | bcbffd33835229417b27074819c8ae486bde556b | |
parent | 7205436569d1b1e0f10485dbdec087f19de865f8 (diff) |
IDENTITY: Expose encryption based on identities on CLI
-rw-r--r-- | doc/man/gnunet-identity.1 | 26 | ||||
-rw-r--r-- | src/identity/Makefile.am | 5 | ||||
-rw-r--r-- | src/identity/gnunet-identity.c | 209 | ||||
-rw-r--r-- | src/identity/test_identity.conf | 3 | ||||
-rwxr-xr-x | src/identity/test_identity_messages.sh | 34 |
5 files changed, 272 insertions, 5 deletions
diff --git a/doc/man/gnunet-identity.1 b/doc/man/gnunet-identity.1 index 599d3b269..835dfb225 100644 --- a/doc/man/gnunet-identity.1 +++ b/doc/man/gnunet-identity.1 @@ -26,7 +26,7 @@ .Os .Sh NAME .Nm gnunet-identity -.Nd create, delete or list egos +.Nd maintain (create, delete or list) or perform actions with egos .Sh SYNOPSIS .Nm .Op Fl C Ar NAME | Fl -create= Ns Ar NAME @@ -34,11 +34,14 @@ .Op Fl d | -display .Op Fl e Ar NAME | Fl -ego= Ns Ar NAME .Op Fl h | -help +.Op Fl k Ar PUBLIC_KEY | Fl -key= Ns Ar PUBLIC_KEY .Op Fl m | -monitor .Op Fl p | -private-keys .Op Fl q | -quiet +.Op Fl R Ar MESSAGE | Fl -read= Ns Ar MESSAGE .Op Fl s Ar SUBSYSTEM | Fl -set= Ns Ar SUBSYSTEM .Op Fl V | -verbose +.Op Fl W Ar MESSAGE | Fl -write= Ns Ar MESSAGE .Op Fl X | -eddsa .Sh DESCRIPTION .Nm @@ -51,6 +54,9 @@ created locally, to create new egos, and to delete existing egos (the namespace will continue to exist, but it will be impossible to add additional data to it). .Pp +In addition, it is possible to encrypt and decrypt messages (arbitrary strings) +using a given public key (for encryption) or ego (for decryption). +.Pp Creating a new ego requires using the .Fl C option together with an identifier (name) that is to be used for @@ -72,6 +78,11 @@ Perform "set" operation with the respective ego or restrict "display" operation to the respective ego. .It Fl h | -help Print the help page. +.It Fl k Ar PUBLIC_KEY | Fl -key= Ns Ar PUBLIC_KEY +The public key to use for a message recipient. Use together with +.Fl W . +The recipient can obtain the desired ego's public key using the "display" +operation. .It Fl m | -monitor Run in monitor mode, listing all ouf our egos until CTRL-C is pressed. Each ego is listed together with a unique pointer value; if @@ -83,6 +94,12 @@ keys. The second column shows the public key, the third column shows the private key. .It Fl q | -quiet Be quiet, in particular outputs only the public key when listing egos. +.It Fl R Ar MESSAGE | Fl -read= Ns Ar MESSAGE +Decrypt (read) a message using the respective ego private key. Use together with +.Fl e . +The message consists of an ephemeral key and the ciphertext, separated by a dot. +Such messages can be created with +.Fl W . .It Fl s Ar SUBSYSTEM | Fl -set= Ns Ar SUBSYSTEM Perform "set" operation for the specified .Ar SUBSYSTEM @@ -95,6 +112,13 @@ This will fail if does not yet exist. .It Fl V | -verbose Be verbose, in particular outputs the public key of freshly created egos. +.It Fl W Ar MESSAGE | Fl -write= Ns Ar MESSAGE +Encrypt (write) the given message for the identity given with +.Fl k . +The output contains an ephemeral message public key and the message separated +by a dot. The entire line needs to be transferred to the recipient, who can use +.Fl R +to decrypt the message. .It Fl X | -eddsa Use EdDSA instead of ECDSA. .El diff --git a/src/identity/Makefile.am b/src/identity/Makefile.am index 59ace6c41..e535c208a 100644 --- a/src/identity/Makefile.am +++ b/src/identity/Makefile.am @@ -71,9 +71,12 @@ check_PROGRAMS = \ test_identity \ test_identity_defaults +check_SCRIPTS = \ + test_identity_messages.sh + if ENABLE_TEST_RUN AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; -TESTS = $(check_PROGRAMS) +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) endif diff --git a/src/identity/gnunet-identity.c b/src/identity/gnunet-identity.c index d8dc936d3..97dc2ce7e 100644 --- a/src/identity/gnunet-identity.c +++ b/src/identity/gnunet-identity.c @@ -71,6 +71,16 @@ static int quiet; static int type_eddsa; /** + * -W option + */ +static char *write_msg; + +/** + * -R option + */ +static char *read_msg; + +/** * -C option */ static char *create_ego; @@ -86,6 +96,11 @@ static char *delete_ego; static char *privkey_ego; /** + * -k option + */ +static char *pubkey_msg; + +/** * -s option. */ static char *set_ego; @@ -164,6 +179,8 @@ test_finished (void) (NULL == delete_op) && (NULL == set_op) && (NULL == set_subsystem) && + (NULL == write_msg) && + (NULL == read_msg) && (! list) && (! monitor)) { @@ -260,6 +277,135 @@ set_done (void *cls, const char *emsg) /** + * Encrypt a message given with -W, encrypted using public key of + * an identity given with -k. + */ +static void +write_encrypted_message (void) +{ + struct GNUNET_IDENTITY_PublicKey recipient; + if (GNUNET_IDENTITY_public_key_from_string (pubkey_msg, &recipient) != + GNUNET_SYSERR) + { + struct GNUNET_CRYPTO_EcdhePublicKey message_key; + size_t msg_len = strlen (write_msg); + ssize_t res = GNUNET_IDENTITY_encrypt (write_msg, + msg_len, + &recipient, + &message_key, + write_msg); + if (-1 != res) + { + char *keystr; + char *serialized_msg; + keystr = GNUNET_STRINGS_data_to_string_alloc (&message_key, + sizeof(struct + GNUNET_CRYPTO_EcdhePublicKey)); + serialized_msg = GNUNET_STRINGS_data_to_string_alloc (write_msg, + msg_len); + fprintf (stdout, + "%s.%s\n", + keystr, serialized_msg); + GNUNET_free (keystr); + GNUNET_free (serialized_msg); + } + else + { + fprintf (stderr, "Error during encryption.\n"); + global_ret = 1; + } + } + else + { + fprintf (stderr, "Invalid recipient public key.\n"); + global_ret = 1; + } +} + + +/** + * Decrypt a message given with -R, encrypted using public key of @c ego + * and ephemeral key given with -k. + * + * @param ego ego whose private key is used for decryption + */ +static void +read_encrypted_message (struct GNUNET_IDENTITY_Ego *ego) +{ + // message contains ECDHE key and ciphertext divided by ".", so split up first + char delim[2] = "."; + char *key_msg = strtok (read_msg, delim); + char *cipher; + if (NULL == key_msg) + { + fprintf (stderr, "Invalid message format.\n"); + global_ret = 1; + return; + } + cipher = strtok (NULL, delim); + if (NULL == cipher) + { + fprintf (stderr, "Invalid message format, text missing.\n"); + global_ret = 1; + return; + } + + if (NULL != strtok (NULL, delim)) + { + fprintf (stderr, + "Invalid message format, expecting only key and cipher components.\n"); + global_ret = 1; + return; + } + + struct GNUNET_CRYPTO_EcdhePublicKey message_key; + if (GNUNET_OK == GNUNET_STRINGS_string_to_data (key_msg, strlen ( + key_msg), + &message_key, + sizeof(message_key))) + { + char *deserialized_msg; + size_t msg_len; + if (GNUNET_OK == GNUNET_STRINGS_string_to_data_alloc (cipher, strlen ( + cipher), + (void **) & + deserialized_msg, + &msg_len)) + { + ssize_t res = GNUNET_IDENTITY_decrypt (deserialized_msg, + msg_len, + GNUNET_IDENTITY_ego_get_private_key ( + ego), + &message_key, + deserialized_msg); + if (-1 != res) + { + fprintf (stdout, + "%s\n", + deserialized_msg); + } + else + { + fprintf (stderr, "Failed to decrypt message.\n"); + global_ret = 1; + } + GNUNET_free (deserialized_msg); + } + else + { + fprintf (stderr, "Invalid message format.\n"); + global_ret = 1; + } + } + else + { + fprintf (stderr, "Invalid message ephemeral key.\n"); + global_ret = 1; + } +} + + +/** * If listing is enabled, prints information about the egos. * * This function is initially called for all egos and then again @@ -330,13 +476,25 @@ print_ego (void *cls, GNUNET_free (set_ego); set_ego = NULL; } + if ( (NULL == ego) && + (NULL != set_ego) && + (NULL != read_msg) ) + { + fprintf (stderr, + "Ego `%s' is not known, cannot decrypt message.\n", + set_ego); + GNUNET_free (read_msg); + read_msg = NULL; + GNUNET_free (set_ego); + set_ego = NULL; + } if ((NULL == ego) && (! monitor)) { list = 0; test_finished (); return; } - if (! (list | monitor)) + if (! (list | monitor) && (NULL == read_msg)) return; if ( (NULL == ego) || (NULL == identifier) ) @@ -349,7 +507,14 @@ print_ego (void *cls, s = GNUNET_IDENTITY_public_key_to_string (&pk); privs = GNUNET_IDENTITY_private_key_to_string ( GNUNET_IDENTITY_ego_get_private_key (ego)); - if ((monitor) || (NULL != identifier)) + if ((NULL != read_msg) && (NULL != set_ego)) + { + // due to the check above, set_ego and the identifier are equal + read_encrypted_message (ego); + GNUNET_free (read_msg); + read_msg = NULL; + } + else if ((monitor) || (NULL != identifier)) { if (quiet) { @@ -397,6 +562,19 @@ run (void *cls, fprintf (stderr, "Option -s requires option -e to be specified as well.\n"); return; } + + if ((NULL != read_msg) && (NULL == set_ego)) + { + fprintf (stderr, + "Option -R requires options -e to be specified as well.\n"); + return; + } + + if ((NULL != write_msg) && (NULL == pubkey_msg)) + { + fprintf (stderr, "Option -W requires option -k to be specified as well.\n"); + return; + } sh = GNUNET_IDENTITY_connect (cfg, (monitor | list) || (NULL != set_ego) || @@ -404,6 +582,13 @@ run (void *cls, ? &print_ego : NULL, NULL); + if (NULL != write_msg) + { + write_encrypted_message (); + GNUNET_free (write_msg); + write_msg = NULL; + } + // read message is handled in ego callback (print_ego) if (NULL != delete_ego) delete_op = GNUNET_IDENTITY_delete (sh, @@ -471,6 +656,18 @@ main (int argc, char *const *argv) gettext_noop ( "set the private key for the identity to PRIVATE_KEY (use together with -C)"), &privkey_ego), + GNUNET_GETOPT_option_string ('R', + "read", + "MESSAGE", + gettext_noop ( + "Read and decrypt message encrypted for the given ego (use together with -e EGO)"), + &read_msg), + GNUNET_GETOPT_option_string ('W', + "write", + "MESSAGE", + gettext_noop ( + "Encrypt and write message for recipient identity PULBIC_KEY, (use together with -k RECIPIENT_PUBLIC_KEY)"), + &write_msg), GNUNET_GETOPT_option_flag ('X', "eddsa", gettext_noop ( @@ -489,8 +686,14 @@ main (int argc, char *const *argv) "ego", "NAME", gettext_noop ( - "set default identity to NAME for a subsystem SUBSYSTEM (use together with -s) or restrict results to NAME (use together with -d)"), + "set default identity to NAME for a subsystem SUBSYSTEM (use together with -s), restrict results to NAME (use together with -d) or read and decrypt a message for NAME (use together with -R)"), &set_ego), + GNUNET_GETOPT_option_string ('k', + "key", + "PUBLIC_KEY", + gettext_noop ( + "The public key of the recipient (with -W)"), + &pubkey_msg), GNUNET_GETOPT_option_flag ('m', "monitor", gettext_noop ("run in monitor mode egos"), diff --git a/src/identity/test_identity.conf b/src/identity/test_identity.conf index 9c433da77..14b915732 100644 --- a/src/identity/test_identity.conf +++ b/src/identity/test_identity.conf @@ -1,3 +1,6 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-identity-service/ + [arm] PORT = 12000 UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock diff --git a/src/identity/test_identity_messages.sh b/src/identity/test_identity_messages.sh new file mode 100755 index 000000000..250c6a6f1 --- /dev/null +++ b/src/identity/test_identity_messages.sh @@ -0,0 +1,34 @@ +#!/bin/bash +trap "gnunet-arm -e -c test_identity.conf" SIGINT + +LOCATION=$(which gnunet-config) +if [ -z $LOCATION ] +then + LOCATION="gnunet-config" +fi +$LOCATION --version 1> /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `gnunet-config -c test_identity.conf -s PATHS -o GNUNET_HOME -f` + +which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30" + +TEST_MSG="This is a test message. 123" +gnunet-arm -s -c test_identity.conf +gnunet-identity -C recipientego -c test_identity.conf +RECIPIENT_KEY=$(gnunet-identity -d -e recipientego -q -c test_identity.conf) +MSG_ENC=$(gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY -c test_identity.conf) +MSG_DEC=$(gnunet-identity -R "$MSG_ENC" -e recipientego -c test_identity.conf) + +if test "$TEST_MSG" != "$MSG_DEC" +then + echo "Failed - $TEST_MSG != $MSG_DEC" + exit 1 +fi + +gnunet-identity -D recipientego -c test_identity.conf +gnunet-arm -e -c test_identity.conf |